VB.NET 中的随机整数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18676/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 13:48:33  来源:igfitidea点击:

Random integer in VB.NET

vb.netrandom

提问by brendan

I need to generate a random integer between 1 and n (where n is a positive whole number) to use for a unit test. I don't need something overly complicated to ensure true randomness - just an old-fashioned random number.

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

How would I do that?

我该怎么做?

采纳答案by Kibbee

To get a random integer value between 1 and N (inclusive) you can use the following.

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

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

回答by Dan Tao

As has been pointed out many times, the suggestion to write code like this is problematic:

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

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

The reason is that the constructor for the Randomclass provides a default seed based on the system's clock. On most systems, this has limited granularity -- somewhere in the vicinity of 20 ms. So if you write the following code, you're going to get the same number a bunch of times in a row:

原因是Random该类的构造函数提供了基于系统时钟的默认种子。在大多数系统上,这具有有限的粒度——大约在 20 毫秒附近。因此,如果您编写以下代码,您将连续多次获得相同的数字:

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

The following code addresses this issue:

下面的代码解决了这个问题:

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

I threw together a simple program using both methods to generate 25 random integers between 1 and 100. Here's the output:

我使用这两种方法编写了一个简单的程序来生成 1 到 100 之间的 25 个随机整数。 这是输出:

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

回答by Joseph Sturtevant

Use System.Random:

使用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)

回答by Shawn Kovac

All the answers so far have problems or bugs (plural, not just one). I will explain. But first I want to compliment Dan Tao's insight to use a static variable to remember the Generator variable so calling it multiple times will not repeat the same # over and over, plus he gave a very nice explanation. But his code suffered the same flaw that most others have, as i explain now.

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

MS made their Next() method rather odd. the Min parameter is the inclusive minimum as one would expect, but the Max parameter is the exclusivemaximum as one would NOT expect. in other words, if you pass min=1 and max=5 then your random numbers would be any of 1, 2, 3, or 4, but it would never include 5. This is the first of two potential bugs in all code that uses Microsoft's Random.Next() method.

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

For a simpleanswer (but still with other possible but rare problems) then you'd need to use:

对于一个简单的答案(但仍然存在其他可能但罕见的问题),那么您需要使用:

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

(I like to use Int32rather than Integerbecause it makes it more clear how big the int is, plus it is shorter to type, but suit yourself.)

(我喜欢使用Int32而不是Integer因为它可以更清楚地说明 int 有多大,而且输入更短,但适合自己。)

I see two potential problems with this method, but it will be suitable (and correct) for most uses. So if you want a simplesolution, i believe this is correct.

我发现这种方法有两个潜在的问题,但它适合(并且正确)大多数用途。所以如果你想要一个简单的解决方案,我相信这是正确的。

The only 2 problems i see with this function is: 1: when Max = Int32.MaxValue so adding 1 creates a numeric overflow. altho, this would be rare, it is still a possibility. 2: when min > max + 1. when min = 10 and max = 5 then the Next function throws an error. this may be what you want. but it may not be either. or consider when min = 5 and max = 4. by adding 1, 5 is passed to the Next method, but it does not throw an error, when it really is an error, but Microsoft .NET code that i tested returns 5. so it really is not an 'exclusive' max when the max = the min. but when max < min for the Random.Next() function, then it throws an ArgumentOutOfRangeException. so Microsoft's implementation is really inconsistent and buggy too in this regard.

我在这个函数中看到的唯一 2 个问题是: 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 时,它会抛出一个 ArgumentOutOfRangeException。所以微软的实现在这方面也确实不一致和错误。

you may want to simply swap the numbers when min > max so no error is thrown, but it totally depends on what is desired. if you want an error on invalid values, then it is probably better to also throw the error when Microsoft's exclusive maximum (max + 1) in our code equals minimum, where MS fails to error in this case.

您可能只想在 min > max 时交换数字,以免引发错误,但这完全取决于所需的内容。如果您希望在无效值上出现错误,那么最好在我们代码中 Microsoft 的唯一最大值 (max + 1) 等于最小值时也抛出错误,在这种情况下,MS 不会出错。

handling a work-around for when max = Int32.MaxValue is a little inconvenient, but i expect to post a thorough function which handles both these situations. and if you want different behavior than how i coded it, suit yourself. but be aware of these 2 issues.

处理 max = Int32.MaxValue 时的变通方法有点不方便,但我希望发布一个全面的函数来处理这两种情况。如果您想要与我编码方式不同的行为,请选择适合自己的方式。但要注意这两个问题。

Happy coding!

快乐编码!

Edit: So i needed a random integer generator, and i decided to code it 'right'. So if anyone wants the full functionality, here's one that actually works. (But it doesn't win the simplest prize with only 2 lines of code. But it's not really complex either.)

编辑:所以我需要一个随机整数生成器,我决定对其进行“正确”编码。所以如果有人想要完整的功能,这里有一个真正有效的。(但它并没有赢得只有 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

回答by Bill the Lizard

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

回答by Wais

Microsoft Example Rnd Function

Microsoft 示例Rnd 函数

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

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

1- Initialize the random-number generator.

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

Randomize()

2 - Generate random value between 1 and 6.

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

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

回答by achar

You should create a pseudo-random number generator only once:

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

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

Then, if an integer suffices for your needs, you can use:

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

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

as many times as you like. Using the wrapper function is justified only because the maximum value is exclusive - I know that the random numbers work this way but the definition of .Next is confusing.

随心所欲。使用包装函数只是因为最大值是独占的 - 我知道随机数是这样工作的,但 .Next 的定义令人困惑。

Creating a generator every time you need a number is in my opinion wrong; the pseudo-random numbers do not work this way.

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

First, you get the problem with initialization which has been discussed in the other replies. If you initialize once, you do not have this problem.

首先,您会遇到其他答复中讨论过的初始化问题。如果初始化一次,就不会有这个问题。

Second, I am not at all certain that you get a valid sequence of random numbers; rather, you get a collection of the first number of multiple different sequences which are seeded automatically based on computer time. I am not certain that these numbers will pass the tests that confirm the randomness of the sequence.

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

回答by Rogala

If you are using Joseph's answer which is a great answer, and you run these back to back like this:

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

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

Then the result could come back the same over and over because it processes the call so quickly. This may not have been an issue in '08, but since the processors are much faster today, the function doesn't allow the system clock enough time to change prior to making the second call.

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

Since the System.Random() function is based on the system clock, we need to allow enough time for it to change prior to the next call. One way of accomplishing this is to pause the current thread for 1 millisecond. See example below:

由于 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

回答by Binny

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

回答by Zibri

Just for reference, VB NET Fuction definition for RND and RANDOMIZE (which should give the same results of BASIC (1980 years) and all versions after is:

仅供参考,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

While the Random CLASS is:

而随机类是:

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