VBA: добавление случайных чисел в сетку, которые еще не в сетке

Sub FWP()

Dim i As Integer
Dim j As Integer
Dim n As Integer
n = Range("A1").Value

For i = 1 To n
  For j = 1 To n

    If Cells(i + 1, j) = 0 Then
        Cells(i + 1, j).Value = Int(((n ^ 2) - 1 + 1) * Rnd + 1)
    ElseIf Cells(i + 1, j) <> 0 Then
        Cells(i + 1, j).Value = Cells(i + 1, j).Value
    End If
  Next j
Next i

Я пытаюсь выполнить часть домашнего задания, в котором предлагается заполнить пропущенные пробелы в магическом квадрате в VBA. Он настроен как (n x n) матрица с n^2 числами в; пробелы, которые мне нужно заполнить, представлены нулями в матрице. Пока у меня есть некоторый код, который проходит проверку каждого отдельного значения ячейки и оставит значения в покое, если не 0, и если значение равно 0, он заменяет их случайным числом от 1 до n^2. Проблема в том, что, очевидно, я получаю несколько повторяющихся значений, что недопустимо, должно быть только 1 из каждого числа.

Как мне его кодировать, чтобы в сетке не было повторяющихся чисел? Я пытаюсь включить функцию проверки, чтобы увидеть, есть ли они уже в сетке, но я не уверен, как это сделать

Спасибо

2 ответа

Есть много подходов, которые вы можете использовать, но @CMArg прав, говоря, что массив или словарь - хороший способ убедиться, что у вас нет дубликатов.

Чего вы хотите избежать, так это сценария, в котором каждая ячейка занимает все больше времени для заполнения. Это не проблема для очень маленьких квадратов (например, 10х10), но очень большие квадраты могут стать некрасивыми. (Если ваш диапазон составляет 1-100, а все числа, кроме 31, уже есть в таблице, потребуется много времени - в среднем 100 догадок, верно?- чтобы вытянуть одно неиспользуемое число. Если диапазон равен 1-40000 (200x200), для заполнения последней ячейки потребуется 40000 догадок.)

Поэтому вместо того, чтобы хранить список уже использованных чисел, подумайте о том, как можно эффективно пройти и "вычеркнуть" уже использованные числа, чтобы каждая новая ячейка заняла ровно 1 "предположение" для заполнения.

Вот один из способов, которым вы можете реализовать это:

Класс: SingleRandoms

Option Explicit

Private mUnusedValues As Scripting.Dictionary
Private mUsedValues As Scripting.Dictionary

Private Sub Class_Initialize()
  Set mUnusedValues = New Scripting.Dictionary
  Set mUsedValues = New Scripting.Dictionary
End Sub

Public Sub GenerateRange(minimumNumber As Long, maximumNumber As Long)
  Dim i As Long
  With mUnusedValues
    .RemoveAll
    For i = minimumNumber To maximumNumber
      .Add i, i
    Next
  End With
End Sub

Public Function GetRandom() As Long
  Dim i As Long, keyID As Long
  Randomize timer
  With mUnusedValues
    i = .Count
    keyID = Int(Rnd * i)
    GetRandom = .Keys(keyID)
    .Remove GetRandom
  End With
  mUsedValues.Add GetRandom, GetRandom
End Function

Public Property Get AvailableValues() As Scripting.Dictionary
  Set AvailableValues = mUnusedValues
End Property

Public Property Get UsedValues() As Scripting.Dictionary
  Set UsedValues = mUsedValues
End Property

Пример класса в действии:

Public Sub getRandoms()
Dim r As SingleRandoms
Set r = New SingleRandoms
With r
  .GenerateRange 1, 100
  Do Until .AvailableValues.Count = 0
    Debug.Print .GetRandom()
  Loop
End With
End Sub

Использование коллекции на самом деле было бы более эффективным в использовании памяти и быстрее, чем использование словаря, но словарь облегчает проверку того, что он делает то, что должен (поскольку вы можете использовать .Exists, так далее.).

Никто не собирается делать за тебя домашнее задание. Вы бы только обманывали себя. Позор им, если они делают.

Я не уверен, насколько требователен ваш учитель, но есть много способов решить это.

Вы можете поместить значения матрицы в массив. Проверьте, существует ли элемент с нулевым значением, если нет, прервите. Затем получите ваше потенциальное случайное число для вставки. Итерация по массиву с циклом for, проверяющим каждый элемент на это значение. Если его нет, замените нулевой элемент.

Другие вопросы по тегам