Случайное целое число в VB.NET
Мне нужно сгенерировать случайное целое число от 1 до n (где n-положительное целое число) для использования в модульном тесте. Мне не нужно что - то слишком сложное, чтобы обеспечить истинную случайность-просто старомодное случайное число.
Как бы я это сделал?
11 ответов:
чтобы получить случайное целое значение от 1 до N (включительно), вы можете использовать следующее.
CInt(Math.Ceiling(Rnd() * n)) + 1
как уже неоднократно указывалось, предложение написать такой код является проблематичным:
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
все ответы до сих пор имеют проблемы или ошибки (множественное число, а не только один). Я все объясню. Но сначала я хочу похвалить понимание Дэна Тао, чтобы использовать статическую переменную для запоминания переменной генератора, поэтому вызов ее несколько раз не будет повторять один и тот же # снова и снова, плюс он дал очень хорошее объяснение. Но его код страдал тем же недостатком, что и большинство других, как я объясняю сейчас.
MS сделал их следующий () метод довольно странным. параметр min-это минимальный включено в качестве одного ожидал бы, но максимальный параметр эксклюзивные максимум, как и следовало ожидать. другими словами, если вы передадите min=1 и max=5, то ваши случайные числа будут любыми из 1, 2, 3 или 4, но они никогда не будут включать 5. Это первая из двух потенциальных ошибок во всем коде, который использует Microsoft Random.Следующий метод.
на простой ответ (но все же с другими возможными, но редкими проблемами), то вам нужно будет использовать:
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, плюс он короче для ввода, но подходит вам.)Я вижу две потенциальные проблемы с этим методом, но он будет подходящим (и правильным) для большинства применений. Так что если вы хотите простой решение, я считаю, что это правильно.
только 2 проблемы, которые я вижу с этой функцией: 1: Когда Макс = int32 значение.MaxValue поэтому добавление 1 создает числовое переполнение. хотя, это было бы редко, это все еще a возможность. 2: когда мин > макс + 1. когда min = 10 и max = 5, то следующая функция выдает ошибку. это может быть то, что вы хотите. но это может быть и не так. или рассмотрим, когда min = 5 и max = 4. при добавлении 1, 5 передается в следующий метод, но он не выдает ошибку, когда это действительно ошибка, но Microsoft .NET код, который я тестировал возвращает 5. так что это действительно не "эксклюзивный" Макс, когда макс = мин. но когда max
вы можете просто поменять местами числа, когда min > max, поэтому ошибка не возникает, но это полностью зависит от того, что требуется. если вы хотите ошибку на недопустимых значениях, то, вероятно, лучше также выбросить ошибку, когда эксклюзивный максимум Microsoft (max + 1) в нашем коде равен минимуму, где MS не может ошибиться в этом случае.
обработка обходного пути для когда max = Int32.MaxValue немного неудобно, но я ожидаю опубликовать тщательную функцию, которая обрабатывает обе эти ситуации. и если вы хотите другое поведение, чем то, как я его закодировал, устраивайте себя. но имейте в виду эти 2 вопроса.
удачи в кодировании!
изменить: Поэтому мне нужен генератор случайных чисел, и я решил закодировать его "правильно". Поэтому, если кто-то хочет полную функциональность, вот тот, который действительно работает. (Но он не выигрывает самый простой приз только с 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
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/en-us/library/f7s023d2%28v=vs.90%29.aspx
1-инициализация генератора случайных чисел.
Randomize()
2-генерация случайного значения между 1 и 6.
Dim value As Integer = CInt(Int((6 * Rnd()) + 1))
Если вы используете ответ Джозефа, который является отличным ответом, и вы запускаете их спина к спине следующим образом:
dim i = GetRandom(1, 1715) dim o = GetRandom(1, 1715)
тогда результат может вернуться то же самое снова и снова, потому что он обрабатывает вызов так быстро. Возможно, это не было проблемой в '08, но поскольку процессоры сегодня намного быстрее, функция не позволяет системным часам достаточно времени для изменения до выполнения второго вызова.
С системой.Случайная() функция основана на системе часы, мы должны позволить достаточно времени для его изменения до следующего вызова. Один из способов сделать это, чтобы приостановить текущий поток на 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
генератор псевдослучайных чисел следует создавать только один раз:
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
столько раз, сколько вам нравится. Использование функции обертки оправдано только потому, что максимальное значение является исключительным - я знаю, что случайные числа работают таким образом, но определение .Дальше-путаница.
создание генератора каждый раз, когда вам нужно число, на мой взгляд, неправильно; псевдослучайный цифры не работают.
во-первых, вы получите проблемы с инициализацией, которая обсуждалась в других ответах. Если вы инициализируете один раз, у вас нет этой проблемы.
просто для справки, определение VB NET Fuction для RND и RANDOMIZE (которое должно дать те же результаты 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