代码之家  ›  专栏  ›  技术社区  ›  brendan

VB.NET中的随机整数

  •  65
  • brendan  · 技术社区  · 16 年前

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

    我该怎么做?

    12 回复  |  直到 10 年前
        1
  •  52
  •   brendan    10 年前

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

    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
    
        2
  •  65
  •   Dan Tao    15 年前

    要获得1到N(包括N)之间的随机整数值,可以使用以下方法。

    CInt(Math.Ceiling(Rnd() * n)) + 1
    
        3
  •  32
  •   Joseph Sturtevant    11 年前

    使用 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)
    
        4
  •  5
  •   Shawn Kovac    11 年前

    微软示例 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))
    
        5
  •  4
  •   Bill the Lizard    15 年前
    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
    
        6
  •  2
  •   Wais    9 年前

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

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

    对于a 简单的 回答(但仍有其他可能但罕见的问题),然后您需要使用:

    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>最大值+1。当min=10且max=5时,Next函数会抛出错误。这可能是你想要的。但也可能不是。或者考虑当min=5和max=4时。通过添加1,5被传递给Next方法,但它不会抛出错误,当它真的是一个错误时,而是微软。我测试的NET代码返回5。因此,当最大值=最小值时,它真的不是“排他性”的最大值,而是当最大值<随机的最小值。Next()函数,然后抛出ArgumentOutOfRangeException。因此,微软的实现在这方面确实不一致,也存在漏洞。

    当min>因此不会抛出错误,但这完全取决于所需的内容。如果你想在无效值上出错,那么最好在我们代码中微软的独占最大值(max+1)等于最小值时抛出错误,在这种情况下,微软不会出错。

    处理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
    
        7
  •  1
  •   Rogala    11 年前

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

    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
    

    随你喜欢的次数。使用包装器函数是合理的,只是因为最大值是排他性的——我知道随机数是这样工作的,但定义是。接下来是令人困惑的。

    在我看来,每次需要一个数字时都创建一个生成器是错误的;伪随机数不以这种方式工作。

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

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

        8
  •  1
  •   achar    9 年前

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

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

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

    自系统以来。Randomy()函数基于系统时钟,我们需要留出足够的时间在下一次调用之前对其进行更改。实现这一点的一种方法是暂停当前线程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
    
        9
  •  0
  •   Binny    10 年前
    Dim rnd As Random = New Random
    rnd.Next(n)
    
        10
  •  0
  •   Zibri    8 年前

    仅供参考,RND和RANDOMIZE的VB NET函数定义(应给出与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
    
        11
  •  -5
  •   patmortech    11 年前

    我看到很多用户对使用不满意 System.Random .

    尽管我个人仍然会使用 系统。随机的 ,我在考虑一种使用GUID作为随机值基础的方法。GUID可以使用其 ToByteArray 方法,并且可以使用 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
    

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

    公认的答案使用 Microsoft.VisualBasic.VBMath.Rnd 方法,它确实提供了一个简单而有吸引力的单行代码,但我个人会避免编写使用 Microsoft.VisualBasic 命名空间。