【问题标题】:Unique Random Numbers using VBA使用 VBA 的唯一随机数
【发布时间】:2013-09-03 18:56:10
【问题描述】:

我正在尝试在用户定义的范围内创建一系列唯一(非重复)随机数。我设法创建了随机数,但我得到了重复的值。如何确保随机数永远不会重复?

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" & 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 " & 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

【问题讨论】:

  • 由于您没有检查重复项,因此您得到一些并不奇怪......数字是随机的重要吗?为什么不按顺序填写数字?

标签: excel vba


【解决方案1】:

这是一种保证唯一整数随机数的方法。内联 cmets 描述了该方法。

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

注意:见this Wikipedia article regarding shuffle bias

编辑修复了一个偏见来源。 Rnd(基于 32 位种子)和模偏差的固有限制仍然存在。

【讨论】:

  • 嗨,克里斯,非常感谢您的帮助,我将把它插入到我的代码中并进行测试,我很快就会带来消息
  • 这个洗牌看起来biased。如果这对您很重要,您可以实现 Knuth 的 shuffle 或按随机键排序。
  • Hi Guy's.thank you very much for your help,非常感谢。关于我用这段代码“解决”的问题,它适用于我并满足我的所有需求。非常感谢您的帮助 问候卡洛斯
【解决方案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

【讨论】:

  • 这段代码是如何工作的?我尝试运行它,没有任何反应。
  • @Max 你检查过 Sheet1 A 列的输出吗?
  • 是的,我做到了。只是空单元格。它没有给出任何结果。我使用 excel 2010。
  • @Max 在 Excel 2010 和 2013 中完美运行。我在两个平台上都试过了。不知道你有什么问题
  • 您是否可以共享指向示例 excel 文件的链接?请
【解决方案3】:
Function RandLotto(Bottom As Integer, Top As Integer, _

                    Amount As Integer) As String

    Dim iArr As Variant

    Dim i As Integer

    Dim r As Integer

    Dim temp As Integer



    Application.Volatile



    ReDim iArr(Bottom To Top)

    For i = Bottom To Top

        iArr(i) = i

    Next i



    For i = Top To Bottom + 1 Step -1

        r = Int(Rnd() * (i - Bottom + 1)) + Bottom

        temp = iArr(r)

        iArr(r) = iArr(i)

        iArr(i) = temp

    Next i



    For i = Bottom To Bottom + Amount - 1

        RandLotto = RandLotto & " " & iArr(i)

    Next i



    RandLotto = Trim(RandLotto)



End Function

【讨论】:

    【解决方案4】:

    完美运行:

    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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-05
      • 1970-01-01
      • 2016-12-03
      • 2012-02-02
      • 2016-05-09
      • 1970-01-01
      相关资源
      最近更新 更多