Frage
Ich muss eine zufällige Ganzzahl zwischen 1 und n generieren (wobei n eine positive ganze Zahl ist), um sie für einen Komponententest zu verwenden.Ich brauche nichts allzu Kompliziertes, um echte Zufälligkeit zu gewährleisten – nur eine altmodische Zufallszahl.
Wie würde ich das machen?
Lösung
Um einen zufälligen ganzzahligen Wert zwischen 1 und N (einschließlich) zu erhalten, können Sie Folgendes verwenden.
CInt(Math.Ceiling(Rnd() * n)) + 1
Andere Tipps
Wie schon oft darauf hingewiesen wurde, ist der Vorschlag, Code wie diesen zu schreiben, problematisch:
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
Der Grund dafür ist, dass der Konstruktor für die Random
Die Klasse stellt einen Standard-Seed bereit, der auf der Systemuhr basiert.Auf den meisten Systemen hat dies eine begrenzte Granularität – irgendwo in der Nähe von 20 ms.Wenn Sie also den folgenden Code schreiben, erhalten Sie mehrmals hintereinander dieselbe Zahl:
Dim randoms(1000) As Integer
For i As Integer = 0 to randoms.Length - 1
randoms(i) = GetRandom(1, 100)
Next
Der folgende Code behebt dieses Problem:
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
Ich habe ein einfaches Programm zusammengestellt, das beide Methoden verwendet, um 25 zufällige ganze Zahlen zwischen 1 und 100 zu generieren.Hier ist die Ausgabe:
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
Verwenden System.Zufällig:
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)
Alle bisherigen Antworten weisen Probleme oder Fehler auf (Plural, nicht nur eine).Ich werde erklären.Aber zuerst möchte ich Dan Taos Einsicht loben, eine statische Variable zu verwenden, um sich die Generator-Variable zu merken, sodass ein mehrmaliges Aufrufen nicht immer wieder dasselbe # wiederholt, und er hat eine sehr schöne Erklärung gegeben.Aber sein Code hatte den gleichen Fehler wie die meisten anderen, wie ich jetzt erkläre.
MS hat ihre Next()-Methode ziemlich seltsam gemacht.Der Min-Parameter ist das inklusive Minimum, wie man es erwarten würde, aber der Max-Parameter ist das exklusiv maximal, wie man es NICHT erwarten würde.Mit anderen Worten: Wenn Sie min=1 und max=5 übergeben, wären Ihre Zufallszahlen 1, 2, 3 oder 4, aber niemals 5.Dies ist der erste von zwei potenziellen Fehlern im gesamten Code, der die Random.Next()-Methode von Microsoft verwendet.
Für ein einfach Antwort (aber immer noch mit anderen möglichen, aber seltenen Problemen), dann müssten Sie Folgendes verwenden:
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
(Ich benutze gerne Int32
statt Integer
weil es klarer macht, wie groß das int ist, außerdem ist es kürzer in der Eingabe, passt aber zu Ihnen.)
Ich sehe zwei potenzielle Probleme bei dieser Methode, aber sie wird für die meisten Anwendungen geeignet (und richtig) sein.Also, wenn Sie ein wollen einfach Lösung, ich glaube, das ist richtig.
Die einzigen zwei Probleme, die ich bei dieser Funktion sehe, sind:1:Wenn Max = Int32.MaxValue ist, führt das Hinzufügen von 1 zu einem numerischen Überlauf.Obwohl dies selten vorkäme, ist es dennoch eine Möglichkeit.2:wenn min > max + 1.wenn min = 10 und max = 5, dann gibt die Next-Funktion einen Fehler aus.Das könnte das sein, was Sie wollen.aber vielleicht ist es das auch nicht.oder überlegen Sie, wann min = 5 und max = 4.Durch Hinzufügen von 1 wird 5 an die Next-Methode übergeben, aber es wird kein Fehler ausgegeben, obwohl es sich wirklich um einen Fehler handelt, aber der von mir getestete Microsoft .NET-Code gibt 5 zurück.Es handelt sich also wirklich nicht um ein „exklusives“ Maximum, wenn das Maximum = das Minimum ist.aber wenn max < min für die Funktion Random.Next(), dann wird eine ArgumentOutOfRangeException ausgelöst.Daher ist die Implementierung von Microsoft in dieser Hinsicht wirklich inkonsistent und fehlerhaft.
Möglicherweise möchten Sie die Zahlen einfach vertauschen, wenn min > max, damit kein Fehler ausgegeben wird. Dies hängt jedoch ganz davon ab, was gewünscht wird.Wenn Sie einen Fehler bei ungültigen Werten wünschen, ist es wahrscheinlich besser, den Fehler auch dann auszulösen, wenn das exklusive Maximum von Microsoft (max + 1) in unserem Code dem Minimum entspricht, wobei MS in diesem Fall keinen Fehler ausgibt.
Die Handhabung einer Problemumgehung für den Fall max = Int32.MaxValue ist etwas umständlich, aber ich erwarte, eine gründliche Funktion zu veröffentlichen, die diese beiden Situationen behandelt.Und wenn Sie ein anderes Verhalten wünschen, als ich es codiert habe, passen Sie es an.Aber seien Sie sich dieser beiden Probleme bewusst.
Viel Spaß beim Codieren!
Bearbeiten:Also brauchte ich einen Zufallsgenerator für ganze Zahlen und beschloss, ihn „richtig“ zu codieren.Wenn also jemand die volle Funktionalität möchte, finden Sie hier eine, die tatsächlich funktioniert.(Aber es gewinnt nicht den einfachsten Preis mit nur 2 Codezeilen.Aber es ist auch nicht wirklich komplex.)
''' <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-Beispiel Rnd-Funktion
https://msdn.microsoft.com/en-us/library/f7s023d2%28v=vs.90%29.aspx
1- Initialisieren Sie den Zufallszahlengenerator.
Randomize()
2 – Zufallswert zwischen 1 und 6 generieren.
Dim value As Integer = CInt(Int((6 * Rnd()) + 1))
Wenn Sie Josephs Antwort verwenden, die eine großartige Antwort ist, und diese wie folgt hintereinander ausführen:
dim i = GetRandom(1, 1715)
dim o = GetRandom(1, 1715)
Dann könnte das Ergebnis immer wieder dasselbe sein, weil der Anruf so schnell verarbeitet wird.Dies war im Jahr 2008 möglicherweise kein Problem, aber da die Prozessoren heute viel schneller sind, lässt die Funktion der Systemuhr nicht genügend Zeit, sich vor dem zweiten Aufruf zu ändern.
Da die System.Random()-Funktion auf der Systemuhr basiert, müssen wir vor dem nächsten Aufruf genügend Zeit einplanen, damit sie sich ändert.Eine Möglichkeit, dies zu erreichen, besteht darin, den aktuellen Thread für eine Millisekunde anzuhalten.Siehe Beispiel unten:
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
Sie sollten einen Pseudozufallszahlengenerator nur einmal erstellen:
Dim Generator As System.Random = New System.Random()
Wenn eine Ganzzahl für Ihre Anforderungen ausreicht, können Sie Folgendes verwenden:
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
so oft Sie möchten.Die Verwendung der Wrapper-Funktion ist nur deshalb gerechtfertigt, weil der Maximalwert exklusiv ist. Ich weiß, dass die Zufallszahlen auf diese Weise funktionieren, aber die Definition von .Next ist verwirrend.
Meiner Meinung nach ist es falsch, jedes Mal einen Generator zu erstellen, wenn Sie eine Nummer benötigen.Die Pseudozufallszahlen funktionieren auf diese Weise nicht.
Erstens tritt das Problem mit der Initialisierung auf, das in den anderen Antworten besprochen wurde.Wenn Sie einmal initialisieren, tritt dieses Problem nicht auf.
Zweitens bin ich mir überhaupt nicht sicher, ob man eine gültige Folge von Zufallszahlen erhält;Stattdessen erhalten Sie eine Sammlung der ersten Anzahl mehrerer unterschiedlicher Sequenzen, die automatisch basierend auf der Computerzeit erstellt werden.Ich bin nicht sicher, ob diese Zahlen die Tests bestehen, die die Zufälligkeit der Sequenz bestätigen.
Dim rnd As Random = New Random
rnd.Next(n)
Nur als Referenz lautet die VB NET-Funktionsdefinition für RND und RANDOMIZE (die die gleichen Ergebnisse von BASIC (1980 Jahre) und allen Versionen danach liefern sollte):
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
Während die Zufallsklasse lautet:
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
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] Dies ist der beste Weg, von Grund auf neu: P.