VB.NET中的随机整数

VB.NET中的随机整数

Random integer in VB.NET

我需要生成一个介于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-初始化随机数生成器。

1
Randomize()

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


推荐阅读