我需要生成一个介于1和n之间的随机整数(其中n是一个正整数)以用于单元测试。 我不需要太复杂的东西来确保真正的随机性-只是一个老式的随机数。
我该怎么办?
正如多次指出的那样,编写这样的代码的建议是有问题的:
1 2 3 4
| Public Function GetRandom(ByVal Min As Integer, ByVal Max As Integer) As Integer
Dim Generator As System.Random = New System.Random()
Return Generator.Next(Min, Max)
End Function |
原因是Random类的构造函数提供了基于系统时钟的默认种子。在大多数系统上,这具有有限的粒度-大约20毫秒左右。因此,如果您编写以下代码,则将连续获得相同的数字:
1 2 3 4
| Dim randoms(1000) As Integer
For i As Integer = 0 to randoms.Length - 1
randoms(i) = GetRandom(1, 100)
Next |
以下代码解决了此问题:
1 2 3 4 5 6 7
| Public Function GetRandom(ByVal Min As Integer, ByVal Max As Integer) As Integer
' by making Generator static, we preserve the same instance '
' (i.e., do not create new instances with the same seed over and over) '
' between calls '
Static Generator As System.Random = New System.Random()
Return Generator.Next(Min, Max)
End Function |
我使用这两种方法将一个简单的程序组合在一起,以生成25个1到100之间的随机整数。这是输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| Non-static: 70 Static: 70
Non-static: 70 Static: 46
Non-static: 70 Static: 58
Non-static: 70 Static: 19
Non-static: 70 Static: 79
Non-static: 70 Static: 24
Non-static: 70 Static: 14
Non-static: 70 Static: 46
Non-static: 70 Static: 82
Non-static: 70 Static: 31
Non-static: 70 Static: 25
Non-static: 70 Static: 8
Non-static: 70 Static: 76
Non-static: 70 Static: 74
Non-static: 70 Static: 84
Non-static: 70 Static: 39
Non-static: 70 Static: 30
Non-static: 70 Static: 55
Non-static: 70 Static: 49
Non-static: 70 Static: 21
Non-static: 70 Static: 99
Non-static: 70 Static: 15
Non-static: 70 Static: 83
Non-static: 70 Static: 26
Non-static: 70 Static: 16
Non-static: 70 Static: 75 |
要获得介于1到N(含)之间的随机整数值,可以使用以下命令。
1
| CInt(Math.Ceiling(Rnd() * n)) + 1 |
使用System.Random:
1 2 3 4 5 6 7 8 9 10
| Dim MyMin As Integer = 1, MyMax As Integer = 5, My1stRandomNumber As Integer, My2ndRandomNumber As Integer
' Create a random number generator
Dim Generator As System.Random = New System.Random()
' Get a random number >= MyMin and <= MyMax
My1stRandomNumber = Generator.Next(MyMin, MyMax + 1) ' Note: Next function returns numbers _less than_ max, so pass in max + 1 to include max as a possible value
' Get another random number (don't create a new generator, use the same one)
My2ndRandomNumber = Generator.Next(MyMin, MyMax + 1) |
到目前为止,所有答案都有问题或错误(多个,而不仅仅是一个)。我会解释。但是首先,我要赞扬Dan Tao的见解,即使用静态变量来记住Generator变量,因此多次调用它不会一次又一次重复相同的代码,此外,他给出了很好的解释。但是,正如我现在所解释的那样,他的代码遭受了与大多数其他代码相同的缺陷。
MS使他们的Next()方法变得很奇怪。 Min参数是一个不期望的包含最小值,但Max参数是一个不期望的包含最大值。换句话说,如果您传递min = 1和max = 5,那么您的随机数将是1、2、3或4中的任何一个,但它永远不会包含5。这是所有代码中两个潜在错误中的第一个使用Microsoft的Random.Next()方法。
对于一个简单的答案(但仍然存在其他可能但罕见的问题),则需要使用:
1 2 3 4
| Private Function GenRandomInt(min As Int32, max As Int32) As Int32
Static staticRandomGenerator As New System.Random
Return staticRandomGenerator.Next(min, max + 1)
End Function |
(我喜欢使用Int32而不是Integer,因为它可以更清楚地知道int的大小,加上键入的时间较短,但适合您自己。)
我看到此方法有两个潜在的问题,但它适合大多数情况(正确)。因此,如果您想要一个简单的解决方案,我相信这是正确的。
我看到此功能的唯一两个问题是:
1:当Max = Int32.MaxValue时,加1会产生数值溢出。还有,这将是罕见的,这仍然是可能的。
2:当min> max + 1时。当min = 10且max = 5时,Next函数将引发错误。这可能就是您想要的。但也可能不是。或考虑min = 5和max = 4的时间。通过加1,将5传递给Next方法,但是当它确实是一个错误但我测试的Microsoft .NET代码返回5时,它不会引发错误。当最大值=最小值时,它确实不是"排他"最大值。但是对于Random.Next()函数,当max
您可能只想在min> max时简单地交换数字,因此不会引发任何错误,但这完全取决于所需的内容。如果您想对无效值进行错误处理,那么当我们代码中的Microsoft专有最大值(max + 1)等于最小值(MS在这种情况下无法出错)时,最好也抛出该错误。
处理max = Int32.MaxValue时的变通办法有点不方便,但是我希望发布一个处理这两种情况的详尽函数。如果您想要的行为与我的编码方式不同,请适合自己。但请注意这两个问题。
编码愉快!
编辑:
所以我需要一个随机整数生成器,因此我决定将其编码为"正确"。因此,如果有人想要完整的功能,那么这实际上是可行的。 (但是,仅用两行代码就不会赢得最简单的奖励。但这也不是很复杂。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| ''' <summary>
''' Generates a random Integer with any (inclusive) minimum or (inclusive) maximum values, with full range of Int32 values.
''' </summary>
''' <param name="inMin">Inclusive Minimum value. Lowest possible return value.</param>
''' <param name="inMax">Inclusive Maximum value. Highest possible return value.</param>
''' <returns></returns>
''' <remarks></remarks>
Private Function GenRandomInt(inMin As Int32, inMax As Int32) As Int32
Static staticRandomGenerator As New System.Random
If inMin > inMax Then Dim t = inMin : inMin = inMax : inMax = t
If inMax < Int32.MaxValue Then Return staticRandomGenerator.Next(inMin, inMax + 1)
' now max = Int32.MaxValue, so we need to work around Microsoft's quirk of an exclusive max parameter.
If inMin > Int32.MinValue Then Return staticRandomGenerator.Next(inMin - 1, inMax) + 1 ' okay, this was the easy one.
' now min and max give full range of integer, but Random.Next() does not give us an option for the full range of integer.
' so we need to use Random.NextBytes() to give us 4 random bytes, then convert that to our random int.
Dim bytes(3) As Byte ' 4 bytes, 0 to 3
staticRandomGenerator.NextBytes(bytes) ' 4 random bytes
Return BitConverter.ToInt32(bytes, 0) ' return bytes converted to a random Int32
End Function |
1 2 3 4 5
| Public Function RandomNumber(ByVal n As Integer) As Integer
'initialize random number generator
Dim r As New Random(System.DateTime.Now.Millisecond)
Return r.Next(1, n)
End Function |
Microsoft示例Rnd函数
https://msdn.microsoft.com/zh-CN/library/f7s023d2%28v=vs.90%29.aspx
1-初始化随机数生成器。
2-产生1到6之间的随机值。
1
| Dim value As Integer = CInt(Int((6 * Rnd()) + 1)) |
您应该只创建一次伪随机数生成器:
1
| Dim Generator As System.Random = New System.Random() |
然后,如果整数满足您的需求,则可以使用:
1 2 3 4
| Public Function GetRandom(myGenerator As System.Random, ByVal Min As Integer, ByVal Max As Integer) As Integer
'min is inclusive, max is exclusive (dah!)
Return myGenerator.Next(Min, Max + 1)
End Function |
尽可能多的次数。仅因为最大值是互斥的,才使用包装函数是合理的-我知道随机数可以这样工作,但是.Next的定义令人困惑。
我认为每次需要一个数字时都生成一个生成器是错误的。伪随机数不能以这种方式工作。
首先,您会遇到初始化问题,这在其他答复中已经讨论过。如果初始化一次,则不会出现此问题。
其次,我完全不确定您是否获得了有效的随机数序列;相反,您会获得多个不同序列的第一个数量的集合,这些序列会根据计算机时间自动播种。我不确定这些数字是否会通过确认序列随机性的测试。
如果您使用的是约瑟夫的答案,这是一个很好的答案,那么您可以像下面这样连续地运行这些答案:
1 2
| dim i = GetRandom(1, 1715)
dim o = GetRandom(1, 1715) |
然后,结果可能会一遍又一遍地返回,因为它是如此迅速地处理了呼叫。在08年这可能不是问题,但是由于当今的处理器要快得多,因此该功能不允许系统时钟有足够的时间在进行第二次调用之前进行更改。
由于System.Random()函数基于系统时钟,因此我们需要在下一次调用之前留出足够的时间来更改它。一种实现方法是将当前线程暂停1毫秒。请参见下面的示例:
1 2 3 4 5
| Public Function GetRandom(ByVal min as Integer, ByVal max as Integer) as Integer
Static staticRandomGenerator As New System.Random
max += 1
Return staticRandomGenerator.Next(If(min > max, max, min), If(min > max, min, max))
End Function |
仅供参考,RND和RANDOMIZE的VB NET功能定义(应该给出与BASIC(1980年)相同的结果,其后的所有版本均为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| Public NotInheritable Class VBMath
' Methods
Private Shared Function GetTimer() As Single
Dim now As DateTime = DateTime.Now
Return CSng((((((60 * now.Hour) + now.Minute) * 60) + now.Second) + (CDbl(now.Millisecond) / 1000)))
End Function
Public Shared Sub Randomize()
Dim timer As Single = VBMath.GetTimer
Dim projectData As ProjectData = ProjectData.GetProjectData
Dim rndSeed As Integer = projectData.m_rndSeed
Dim num3 As Integer = BitConverter.ToInt32(BitConverter.GetBytes(timer), 0)
num3 = (((num3 And &HFFFF) Xor (num3 >> &H10)) << 8)
rndSeed = ((rndSeed And -16776961) Or num3)
projectData.m_rndSeed = rndSeed
End Sub
Public Shared Sub Randomize(ByVal Number As Double)
Dim num2 As Integer
Dim projectData As ProjectData = ProjectData.GetProjectData
Dim rndSeed As Integer = projectData.m_rndSeed
If BitConverter.IsLittleEndian Then
num2 = BitConverter.ToInt32(BitConverter.GetBytes(Number), 4)
Else
num2 = BitConverter.ToInt32(BitConverter.GetBytes(Number), 0)
End If
num2 = (((num2 And &HFFFF) Xor (num2 >> &H10)) << 8)
rndSeed = ((rndSeed And -16776961) Or num2)
projectData.m_rndSeed = rndSeed
End Sub
Public Shared Function Rnd() As Single
Return VBMath.Rnd(1!)
End Function
Public Shared Function Rnd(ByVal Number As Single) As Single
Dim projectData As ProjectData = ProjectData.GetProjectData
Dim rndSeed As Integer = projectData.m_rndSeed
If (Number <> 0) Then
If (Number < 0) Then
Dim num1 As UInt64 = (BitConverter.ToInt32(BitConverter.GetBytes(Number), 0) And &HFFFFFFFF)
rndSeed = CInt(((num1 + (num1 >> &H18)) And CULng(&HFFFFFF)))
End If
rndSeed = CInt((((rndSeed * &H43FD43FD) + &HC39EC3) And &HFFFFFF))
End If
projectData.m_rndSeed = rndSeed
Return (CSng(rndSeed) / 1.677722E+07!)
End Function
End Class |
而随机类是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
| Public Class Random
' Methods
<__DynamicallyInvokable> _
Public Sub New()
Me.New(Environment.TickCount)
End Sub
<__DynamicallyInvokable> _
Public Sub New(ByVal Seed As Integer)
Me.SeedArray = New Integer(&H38 - 1) {}
Dim num4 As Integer = If((Seed = -2147483648), &H7FFFFFFF, Math.Abs(Seed))
Dim num2 As Integer = (&H9A4EC86 - num4)
Me.SeedArray(&H37) = num2
Dim num3 As Integer = 1
Dim i As Integer
For i = 1 To &H37 - 1
Dim index As Integer = ((&H15 * i) Mod &H37)
Me.SeedArray(index) = num3
num3 = (num2 - num3)
If (num3 < 0) Then
num3 = (num3 + &H7FFFFFFF)
End If
num2 = Me.SeedArray(index)
Next i
Dim j As Integer
For j = 1 To 5 - 1
Dim k As Integer
For k = 1 To &H38 - 1
Me.SeedArray(k) = (Me.SeedArray(k) - Me.SeedArray((1 + ((k + 30) Mod &H37))))
If (Me.SeedArray(k) < 0) Then
Me.SeedArray(k) = (Me.SeedArray(k) + &H7FFFFFFF)
End If
Next k
Next j
Me.inext = 0
Me.inextp = &H15
Seed = 1
End Sub
Private Function GetSampleForLargeRange() As Double
Dim num As Integer = Me.InternalSample
If ((Me.InternalSample Mod 2) = 0) Then
num = -num
End If
Dim num2 As Double = num
num2 = (num2 + 2147483646)
Return (num2 / 4294967293)
End Function
Private Function InternalSample() As Integer
Dim inext As Integer = Me.inext
Dim inextp As Integer = Me.inextp
If (++inext >= &H38) Then
inext = 1
End If
If (++inextp >= &H38) Then
inextp = 1
End If
Dim num As Integer = (Me.SeedArray(inext) - Me.SeedArray(inextp))
If (num = &H7FFFFFFF) Then
num -= 1
End If
If (num < 0) Then
num = (num + &H7FFFFFFF)
End If
Me.SeedArray(inext) = num
Me.inext = inext
Me.inextp = inextp
Return num
End Function
<__DynamicallyInvokable> _
Public Overridable Function [Next]() As Integer
Return Me.InternalSample
End Function
<__DynamicallyInvokable> _
Public Overridable Function [Next](ByVal maxValue As Integer) As Integer
If (maxValue < 0) Then
Dim values As Object() = New Object() {"maxValue" }
Throw New ArgumentOutOfRangeException("maxValue", Environment.GetResourceString("ArgumentOutOfRange_MustBePositive", values))
End If
Return CInt((Me.Sample * maxValue))
End Function
<__DynamicallyInvokable> _
Public Overridable Function [Next](ByVal minValue As Integer, ByVal maxValue As Integer) As Integer
If (minValue > maxValue) Then
Dim values As Object() = New Object() {"minValue","maxValue" }
Throw New ArgumentOutOfRangeException("minValue", Environment.GetResourceString("Argument_MinMaxValue", values))
End If
Dim num As Long = (maxValue - minValue)
If (num <= &H7FFFFFFF) Then
Return (CInt((Me.Sample * num)) + minValue)
End If
Return (CInt(CLng((Me.GetSampleForLargeRange * num))) + minValue)
End Function
<__DynamicallyInvokable> _
Public Overridable Sub NextBytes(ByVal buffer As Byte())
If (buffer Is Nothing) Then
Throw New ArgumentNullException("buffer")
End If
Dim i As Integer
For i = 0 To buffer.Length - 1
buffer(i) = CByte((Me.InternalSample Mod &H100))
Next i
End Sub
<__DynamicallyInvokable> _
Public Overridable Function NextDouble() As Double
Return Me.Sample
End Function
<__DynamicallyInvokable> _
Protected Overridable Function Sample() As Double
Return (Me.InternalSample * 4.6566128752457969E-10)
End Function
' Fields
Private inext As Integer
Private inextp As Integer
Private Const MBIG As Integer = &H7FFFFFFF
Private Const MSEED As Integer = &H9A4EC86
Private Const MZ As Integer = 0
Private SeedArray As Integer()
End Class |
1 2
| Dim rnd As Random = New Random
rnd.Next(n) |
1 2 3 4 5
| Function xrand() As Long
Dim r1 As Long = Now.Day & Now.Month & Now.Year & Now.Hour & Now.Minute & Now.Second & Now.Millisecond
Dim RAND As Long = Math.Max(r1, r1 * 2)
Return RAND
End Function |
[BBOYSE]
这是最好的方法,从头开始:P