VB.NET中的随机整数

时间:2008-08-20 19:54:00

标签: vb.net random

我需要生成1到n之间的随机整数(其中n是正整数),用于单元测试。我不需要过于复杂的东西来确保真正的随机性 - 只是一个老式的随机数。

我该怎么做?

12 个答案:

答案 0 :(得分:67)

正如已经多次指出的那样,编写这样的代码的建议是有问题的:

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毫秒附近。因此,如果您编写以下代码,您将连续多次获得相同的数字:

Dim randoms(1000) As Integer
For i As Integer = 0 to randoms.Length - 1
    randoms(i) = GetRandom(1, 100)
Next

以下代码解决了此问题:

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之间的随机整数。这是输出:

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 :(得分:56)

要获得1到N(含)之间的随机整数值,您可以使用以下内容。

CInt(Math.Ceiling(Rnd() * n)) + 1

答案 2 :(得分:32)

使用System.Random

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)

答案 3 :(得分:5)

到目前为止,所有答案都存在问题或错误(复数,而不仅仅是一个)。我会解释。但首先,我想赞美Dan Tao的见解,使用静态变量来记住Generator变量,因此多次调用它不会重复相同的#一遍又一遍,而且他给出了一个非常好的解释。但正如我现在解释的那样,他的代码遭遇了与其他大多数人相同的缺陷。

MS使他们的Next()方法相当奇怪。 Min参数是所期望的包容性最小值,但Max参数是独占最大值,这是人们所不期望的。换句话说,如果你传递min = 1和max = 5,那么你的随机数将是1,2,3或4中的任何一个,但它永远不会包含5.这是所有代码中两个潜在错误中的第一个使用Microsoft的Random.Next()方法。

对于简单的答案(但仍有其他可能但很少见的问题),那么您需要使用:

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.所以当max = min时,它确实不是'独占'最大值。但是当max&lt; min.for Random.Next()函数,然后它抛出ArgumentOutOfRangeException。所以微软的实施在这方面确实不一致和错误。

你可能想在min&gt;时简单地交换数字。最大,因此不会抛出任何错误,但这完全取决于所需的内容。如果你想在无效值上出错,那么当我们的代码中微软的独占最大值(max + 1)等于最小值时,最好也抛出错误,在这种情况下MS无法出错。

处理max = Int32.MaxValue时的解决方法有点不方便,但我希望发布一个处理这两种情况的彻底函数。如果你想要不同于我编码的行为,请适合自己。但要注意这两个问题。

快乐的编码!

编辑: 所以我需要一个随机整数生成器,我决定将它编码为“正确”。因此,如果有人想要完整的功能,这里有一个实际工作。 (但它只用2行代码就没有赢得最简单的奖项。但它也不是很复杂。)

''' <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

答案 4 :(得分:4)

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

答案 5 :(得分:2)

Microsoft示例Rnd Function

https://msdn.microsoft.com/en-us/library/f7s023d2%28v=vs.90%29.aspx

1-初始化随机数生成器。

Randomize()

2 - 生成1到6之间的随机值。

Dim value As Integer = CInt(Int((6 * Rnd()) + 1))

答案 6 :(得分:1)

如果你使用约瑟夫的答案,这是一个很好的答案,你可以像这样背靠背地运行:

dim i = GetRandom(1, 1715)
dim o = GetRandom(1, 1715)

然后结果会一遍又一遍地回来,因为它会如此快速地处理呼叫。这可能不是08年的问题,但由于处理器今天要快得多,因此该功能不允许系统时钟在进行第二次调用之前有足够的时间进行更改。

由于System.Random()函数基于系统时钟,我们需要留出足够的时间在下次调用之前进行更改。实现此目的的一种方法是暂停当前线程1毫秒。见下面的例子:

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

答案 7 :(得分:1)

您应该只创建一次伪随机数生成器:

Dim Generator As System.Random = New System.Random()

然后,如果一个整数满足您的需要,您可以使用:

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的定义令人困惑。

每次你需要一个数字时创建一个生成器在我看来是错误的;伪随机数不能这样工作。

首先,您会遇到其他回复中讨论过的初始化问题。如果初始化一次,则没有此问题。

其次,我完全不确定你得到一个有效的随机数序列;相反,您将获得第一批多个不同序列的集合,这些序列根据计算机时间自动播种。我不确定这些数字是否会通过确认序列随机性的测试。

答案 8 :(得分:0)

Dim rnd As Random = New Random
rnd.Next(n)

答案 9 :(得分:0)

仅供参考,RND和RANDOMIZE的VB NET Fuction定义(应该给出BASIC(1980年)和之后所有版本的相同结果:

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

随机课程是:

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

答案 10 :(得分:0)

我看到很多用户对使用 System.Random 不满意的回答。

尽管我个人仍然会使用 System.Random,但我正在考虑一种使用 GUID 作为随机值基础的方法。可以使用其 ToByteArray 方法将 GUID 转换为字节数组,并且可以使用 BitConverter 将生成的字节数组转换为数值。

'Function for reuse (min is inclusive and max is exclusive)
Function GetRandom(min As Integer, max As Integer) As Integer
    Return BitConverter.ToUInt64(Guid.NewGuid.ToByteArray) Mod (max - min) + min
End Function
'one-liner specific for your purpose (n is exclusive)
BitConverter.ToUInt64(Guid.NewGuid.ToByteArray) Mod (n - 1) + 1

请注意,这只是一个小小的思想实验。我没有测试性能,也没有调查结果的实际“随机性”。但就您的目的而言,它可能只是完成工作。

接受的答案使用 https://www.npmjs.com/package/connect-flash 方法,它确实提供了一个简单且有吸引力的单行,但我个人会避免编写使用 Microsoft.VisualBasic 命名空间的新代码。

答案 11 :(得分:-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