Уникальные случайные числа с использованием VBA

#excel #vba

#excel #vba

Вопрос:

Я пытаюсь создать серию уникальных (не дублирующихся) случайных чисел в пределах определенного пользователем диапазона. Мне удалось создать случайные числа, но я получаю повторяющиеся значения. Как я могу гарантировать, что случайные числа никогда не будут дублироваться?

 Sub GenerateCodesUser()
    Application.ScreenUpdating = False
    Worksheets("Users").Activate

    Dim MINNUMBER As Long
    Dim MAXNUMBER As Long

    MINNUMBER = 1000
    MAXNUMBER = 9999999

    Dim Row As Integer
    Dim Number As Long
    Dim high As Double
    Dim Low As Double
    Dim i As Integer

    If (CustomCodes.CardNumberMin.Value = "") Then
        MsgBox ("Fill Card Number Field!")
        Exit Sub
    ElseIf (CustomCodes.CardNumberMin.Value < MINNUMBER) Then
        MsgBox ("Card Number Value must be equal or higher then" amp; MINNUMBER)
        Exit Sub
    End If

    If (CustomCodes.CardNumberMax.Value = "") Then
        MsgBox ("Fill Card Number Field!")
        Exit Sub
    ElseIf (CustomCodes.CardNumberMax.Value > MAXNUMBER) Then
        MsgBox ("Card Number Value must be equal or higher then " amp; MAXNUMBER)
        Exit Sub
    End If

    Low = CustomCodes.CardNumberMin.Value
    high = CustomCodes.CardNumberMax.Value '<<< CHANGE AS DESIRED

    If (Low < 1000) Then
        'break
    End If

    For i = 1 To Cells(1, 1).End(xlToRight).Column
        If InStr(Cells(1, i), "CardNumber") Then
            Row = 2
            While Cells(Row, 1) <> 0
                Do
                    Number = ((high - Low   1) * Rnd()   Low)
                Loop Until Number > Low
                Cells(Row, i) = Number
                Row = Row   1
            Wend
        End If
    Next

    Application.ScreenUpdating = True
End Sub
 

Комментарии:

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

Ответ №1:

Вот способ гарантировать уникальные целочисленные случайные числа. Встроенные комментарии описывают метод.

 Function UniuqeRandom(Mn As Long, Mx As Long, Sample As Long) As Long()
    Dim dat() As Long
    Dim i As Long, j As Long
    Dim tmp As Long

    ' Input validation checks here
    If Mn > Mx Or Sample > (Mx - Mn   1) Then
        ' declare error to suit your needs
        Exit Function
    End If

    ' size array to hold all possible values
    ReDim dat(0 To Mx - Mn)

    ' Fill the array
    For i = 0 To UBound(dat)
        dat(i) = Mn   i
    Next

    ' Shuffle array, unbiased
    For i = UBound(dat) To 1 Step -1
        tmp = dat(i)
        j = Int((i   1) * Rnd)
        dat(i) = dat(j)
        dat(j) = tmp
    Next

    'original biased shuffle
    'For i = 0 To UBound(dat)
    '    tmp = dat(i)
    '    j = Int((Mx - Mn) * Rnd)
    '    dat(i) = dat(j)
    '    dat(j) = tmp
    'Next

    ' Return sample
    ReDim Preserve dat(0 To Sample - 1)
    UniuqeRandom = dat
End Function
 

используйте это так

 Dim low As Long, high As Long

Dim rng As Range
Dim dat() As Long

Set rng = Range(Cells(1, 1), Cells(1, 1).End(xlToRight))
dat = UniuqeRandom(low, high, rng.Columns.Count)
rng.Offset(1, 0) = dat
 

Примечание: смотрите Эту статью в Википедии о смещении в случайном порядке

Редактирование исправило один источник смещения. Присущие ограничения Rnd (на основе 32-битного начального числа) и смещения по модулю сохраняются.

Комментарии:

1. Привет, Крис, большое спасибо за вашу помощь, я вставлю это в свой код и протестирую, скоро я сообщу новости

2. Это перемешивание выглядит предвзятым . Если это имеет значение для вас, вы можете реализовать Кнута в случайном порядке или сортировать по случайным ключам.

3. Привет, ребята. большое вам спасибо за вашу помощь, это было очень ценно. Что касается проблемы, которую я «решил» с помощью этого кода, она работает для меня и покрывает все мои потребности. Спасибо большое за вашу помощь С наилучшими пожеланиями, Карлос

Ответ №2:

Я вижу, что у вас есть принятый ответ, но, чего бы это ни стоило, вот мой ответ на этот вопрос. В этом используется логическая функция вместо числовых массивов. Это очень просто, но быстро. Преимущество этого, о котором я не говорю, что оно идеально, заключается в эффективном решении для чисел в большом диапазоне, потому что вы проверяете только те числа, которые вы уже выбрали и сохранили, и вам не нужен потенциально большой массив для хранения отклоненных вами значений, поэтому это не вызовет проблем с памятью из-заразмер массива.

 Sub UniqueRandomGenerator()
Dim N As Long, MaxNum As Long, MinNum As Long, Rand As Long, i As Long

MinNum = 1        'Put the input of minimum number here
MaxNum = 100      'Put the input of maximum number here
N = MaxNum - MinNum   1

ReDim Unique(1 To N, 1 To 1)

For i = 1 To N
Randomize         'I put this inside the loop to make sure of generating "good" random numbers
    Do
        Rand = Int(MinNum   N * Rnd)
        If IsUnique(Rand, Unique) Then Unique(i, 1) = Rand:  Exit Do
    Loop
Next
Sheet1.[A1].Resize(N) = Unique
End Sub

Function IsUnique(Num As Long, Data As Variant) As Boolean
Dim iFind As Long

On Error GoTo Unique
iFind = Application.WorksheetFunction.Match(Num, Data, 0)

If iFind > 0 Then IsUnique = False: Exit Function

Unique:
    IsUnique = True
End Function
 

Комментарии:

1. Как работает этот код? Я попытался запустить его, ничего не происходит.

2. @Max Вы проверили выходные данные в столбце A листа 1?

3. Да, я сделал. Просто пустые ячейки. Это не дает никаких результатов. Я использую Excel 2010.

4. @Max Это отлично работает в Excel 2010 и 2013. Я пробовал это на обеих платформах. Не уверен, что не так с вашим

5. Возможно ли, что вы можете поделиться ссылкой на образец файла Excel? пожалуйста

Ответ №3:

Это работает отлично:

 Option Base 1
Public Function u(a As Variant, b As Variant) As Variant
 Application.Volatile
 Dim k%, p As Double, flag As Boolean, x() As Variant
    k = 1
  flag = False
  ReDim x(1)
   x(1) = Application.RandBetween(a, b)
  Do Until k = b - a   1

   Do While flag = False
   Randomize
    p = Application.RandBetween(a, b)
     'Debug.Assert p = 2
    resultado = Application.Match(p, x, False)
     If IsError(resultado) Then
      k = k   1
      ReDim Preserve x(k)
      x(k) = p
       flag = True
      Else
       flag = False
      End If
   Loop
   flag = False
  Loop
  u = x
End Function