Willekeurig geheel getal in VB.NET

Ik moet een willekeurig geheel getal genereren tussen 1 en n (waarbij n een positief geheel getal is) om te gebruiken voor een eenheidstest. Ik heb niet iets heel ingewikkelds nodig om echte willekeur te garanderen – alleen een ouderwets willekeurig getal.

Hoe zou ik dat doen?


Antwoord 1, autoriteit 100%

Om een willekeurig geheel getal tussen 1 en N (inclusief) te krijgen, kun je het volgende gebruiken.

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

Antwoord 2, autoriteit 95%

Zoals al vele malen is opgemerkt, is de suggestie om code als deze te schrijven 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

De reden is dat de constructor voor de klasse Randomeen standaard seed levert op basis van de systeemklok. Op de meeste systemen heeft dit een beperkte granulariteit – ergens in de buurt van 20 ms. Dus als je de volgende code schrijft, krijg je hetzelfde aantal keer achter elkaar:

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

De volgende code verhelpt dit probleem:

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

Ik gooide een eenvoudig programma samen met beide methoden om 25 willekeurige gehele getallen tussen 1 en 100 te genereren. Hier is de output:

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

Antwoord 3, Autoriteit 54%

Gebruik systeem.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)

Antwoord 4, Autoriteit 6%

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

Antwoord 5, autoriteit 6%

Alle antwoorden tot nu toe bevatten problemen of bugs (meervoud, niet slechts één). Ik zal het uitleggen. Maar eerst wil ik Dan Tao’s inzicht complimenteren om een statische variabele te gebruiken om de Generator-variabele te onthouden, zodat het meerdere keren bellen niet hetzelfde # keer op keer zal herhalen, plus hij gaf een heel mooie uitleg. Maar zijn code had dezelfde fout als de meeste anderen, zoals ik nu uitleg.

MS maakte hun Next()-methode nogal vreemd. de Min parameter is het inclusieve minimum zoals men zou verwachten, maar de Max parameter is het exclusievemaximum zoals men NIET zou verwachten. met andere woorden, als u min=1 en max=5 doorgeeft, zijn uw willekeurige getallen 1, 2, 3 of 4, maar nooit 5. Dit is de eerste van twee mogelijke bugs in alle code die gebruikt de Random.Next()-methode van Microsoft.

Voor een eenvoudigantwoord (maar nog steeds met andere mogelijke maar zeldzame problemen) moet u het volgende gebruiken:

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

(Ik gebruik graag Int32in plaats van Integeromdat het duidelijker maakt hoe groot de int is, plus het is korter om te typen, maar past bij jezelf.)

Ik zie twee mogelijke problemen met deze methode, maar deze zal geschikt (en correct) zijn voor de meeste toepassingen. Dus als je een eenvoudigeoplossing wilt, denk ik dat dit correct is.

De enige 2 problemen die ik zie met deze functie is:
1: wanneer max = int32.maxvalue, dus het toevoegen van 1 creëert een numerieke overloop. Altho, dit zou zeldzaam zijn, het is nog steeds een mogelijkheid.
2: Wanneer MIN & GT; MAX + 1. Wanneer min = 10 en max = 5 dan de volgende functie een foutgeeft. Dit kan zijn wat je wilt. Maar het is misschien niet. of overwegen wanneer min = 5 en max = 4. Door het toevoegen van 1, wordt 5 doorgegeven aan de volgende methode, maar het gooit geen fout, wanneer het echt een fout is, maar Microsoft .NET-code die ik heb getest. Dus Het is echt geen ‘exclusieve’ max wanneer het max = de min. Maar wanneer Max & LT; Min voor de functie van het willekeurige.volgende (), dan gooit het een argumentOutofrangeexception. Dus de implementatie van Microsoft is in dit verband ook echt inconsistent en buggy.

Misschien wilt u gewoon de cijfers wisselen wanneer MIN & GT; max, dus er wordt geen fout gegooid, maar het is volledig afhankelijk van wat gewenst is. Als u een fout wilt op ongeldige waarden, dan is het waarschijnlijk beter om ook de fout te gooien wanneer Microsoft’s exclusieve maximum (max + 1) in onze code gelijk is aan minimum, waarbij MS in dit geval fouten faalt.

Omgaan met een work-around voor wanneer Max = Int32.MaxValue een beetje ongemakkelijk is, maar ik verwacht een grondige functie te plaatsen die beide situaties behandelt. En als je een ander gedrag wilt dan hoe ik het heb gecodeerd, pak jezelf dan aan. Maar wees je bewust van deze 2 problemen.

Happy Codering!

EDIT:
Dus ik had een willekeurige integergenerator nodig, en ik besloot het ‘rechts’ te coderen. Dus als iemand de volledige functionaliteit wil, is hier een die eigenlijk werkt. (Maar het wint niet de eenvoudigste prijs met slechts 2 regels code. Maar het is ook niet echt complex.)

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

Antwoord 6, Autoriteit 5%

Microsoft Voorbeeld functie RND

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

1- Initialize de random-number generator.

Randomize()

2 – het genereren van willekeurige waarde tussen 1 en 6.

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

Antwoord 7, Autoriteit 3%

U moet slechts eenmaal een pseudo-willekeurige nummergenerator maken:

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

Dan, als een geheel getal volstaat voor uw behoeften, kunt u gebruiken:

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

zo vaak als je wilt. Het gebruik van de Wrapper-functie is alleen gerechtvaardigd omdat de maximale waarde exclusief is – ik weet dat de willekeurige getallen op deze manier werken, maar de definitie van .volgende is verwarrend.

Een generator maken elke keer dat u een nummer nodig heeft, is naar mijn mening verkeerd; De pseudo-willekeurige getallen werken niet op deze manier.

Ten eerste krijg je het probleem met initialisatie die in de andere antwoorden is besproken. Als u eenmaal initialiseert, heeft u dit probleem niet.

Ten tweede, ik ben helemaal niet zeker dat u een geldige volgorde van willekeurige getallen krijgt; Integendeel, u krijgt een verzameling van het eerste aantal meerdere verschillende sequenties die automatisch worden gezaaid op basis van computertijd. Ik ben er niet zeker van dat deze cijfers de tests bevatten die de willekeurigheid van de reeks bevestigen.


Antwoord 8, Autoriteit 2%

Als u het antwoord van Jozef gebruikt, wat een geweldig antwoord is, en u deze terug naar achteren runt:

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

Dan kan het resultaat steeds weer hetzelfde zijn omdat het de oproep zo snel verwerkt. Dit was misschien geen probleem in ’08, maar aangezien de processors tegenwoordig veel sneller zijn, laat de functie de systeemklok niet genoeg tijd veranderen voordat de tweede oproep wordt gedaan.

Aangezien de functie System.Random() is gebaseerd op de systeemklok, moeten we voldoende tijd geven om deze te wijzigen voordat de volgende aanroep wordt gedaan. Een manier om dit te bereiken is door de huidige thread 1 milliseconde te pauzeren. Zie voorbeeld hieronder:

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

Antwoord 9

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

Antwoord 10

Ter referentie, VB NET Fuction-definitie voor RND en RANDOMIZE (die dezelfde resultaten zouden moeten geven van BASIC (1980 jaar) en alle versies daarna is:

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

Hoewel de willekeurige klasse 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

Antwoord 11

Ik zie veel antwoorden van gebruikers die niet tevreden zijn met het gebruik van System.Random.

Ondanks het feit dat ik persoonlijk nog steeds System.Randomzou gebruiken, dacht ik aan een manier om een GUID te gebruiken als basis voor een willekeurige waarde. Een GUID kan worden geconverteerd naar een bytearray met behulp van de ToByteArray-methode, en de resulterende bytearray kan worden geconverteerd naar een numerieke waarde met behulp van een 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

Merk op dat dit slechts een klein gedachte-experiment is. Ik heb de prestaties niet getest, noch heb ik de werkelijke “willekeurigheid” van de resultaten onderzocht. Maar voor jouw doel kan het gewoon zijn werk doen.

Het geaccepteerde antwoord gebruikt de Microsoft.VisualBasic.VBMath.Rndmethode, die inderdaad een eenvoudige en aantrekkelijke oneliner biedt, maar ik zou persoonlijk vermijden om nieuwe code te schrijven die de Microsoft.VisualBasicnameruimte gebruikt.


Antwoord 12

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

[bbose]
Dit is de beste manier, helemaal opnieuw: p

Previous articleHoe Conda draaien?
Next articleWoorden tellen in string

Other episodes