Domanda

Devo generare un numero intero casuale compreso tra 1 e n (dove n è un numero intero positivo) da utilizzare per un test unitario.Non ho bisogno di qualcosa di eccessivamente complicato per garantire la vera casualità: basta un numero casuale vecchio stile.

Come potrei farlo?

È stato utile?

Soluzione

Per ottenere un valore intero casuale compreso tra 1 e N (incluso) è possibile utilizzare quanto segue.

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

Altri suggerimenti

Come è stato sottolineato molte volte, il suggerimento di scrivere codice come questo è problematico:

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

Il motivo è che il costruttore del file Random La classe fornisce un seed predefinito basato sull'orologio del sistema.Sulla maggior parte dei sistemi, questo ha una granularità limitata, intorno ai 20 ms.Quindi se scrivi il seguente codice, otterrai lo stesso numero un sacco di volte di seguito:

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

Il codice seguente risolve questo problema:

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

Ho messo insieme un semplice programma utilizzando entrambi i metodi per generare 25 numeri interi casuali compresi tra 1 e 100.Ecco l'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

Utilizzo 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)

Tutte le risposte finora presentano problemi o bug (plurale, non solo uno).Spiegherò.Ma prima voglio complimentarmi per l'intuizione di Dan Tao di utilizzare una variabile statica per ricordare la variabile Generator, quindi chiamandola più volte non si ripeterà lo stesso # ancora e ancora, inoltre ha dato una spiegazione molto bella.Ma il suo codice presentava lo stesso difetto della maggior parte degli altri, come spiegherò ora.

MS ha reso il metodo Next() piuttosto strano.il parametro Min è il minimo inclusivo come ci si aspetterebbe, ma il parametro Max è il esclusivo massimo come NON ci si aspetterebbe.in altre parole, se passi min=1 e max=5 i tuoi numeri casuali saranno uno qualsiasi tra 1, 2, 3 o 4, ma non includeranno mai 5.Questo è il primo di due potenziali bug in tutto il codice che utilizza il metodo Random.Next() di Microsoft.

Per un semplice risposta (ma ancora con altri possibili ma rari problemi) allora dovresti usare:

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

(Mi piace usare Int32 piuttosto che Integer perché rende più chiaro quanto è grande l'int, inoltre è più breve da scrivere, ma adatto a te.)

Vedo due potenziali problemi con questo metodo, ma sarà adatto (e corretto) per la maggior parte degli usi.Quindi se vuoi un semplice soluzione, credo che sia corretta.

Gli unici 2 problemi che vedo con questa funzione sono:1:quando Max = Int32.MaxValue quindi l'aggiunta di 1 crea un overflow numerico.anche se questo sarebbe raro, è ancora una possibilità.2:quando min > max + 1.quando min = 10 e max = 5 la funzione Next genera un errore.questo potrebbe essere quello che vuoi.ma potrebbe anche non esserlo.oppure considerare quando min = 5 e max = 4.aggiungendo 1, 5 viene passato al metodo Next, ma non genera un errore, quando in realtà è un errore, ma il codice Microsoft .NET che ho testato restituisce 5.quindi non è davvero un massimo "esclusivo" quando il massimo = il minimo.ma quando max < min per la funzione Random.Next(), genera un'ArgumentOutOfRangeException.quindi anche l'implementazione di Microsoft è davvero incoerente e piena di bug a questo riguardo.

potresti voler semplicemente scambiare i numeri quando min > max in modo che non venga generato alcun errore, ma dipende totalmente da ciò che si desidera.se si desidera un errore su valori non validi, probabilmente è meglio generare l'errore anche quando il massimo esclusivo di Microsoft (max + 1) nel nostro codice è uguale al minimo, dove MS non riesce a generare errori in questo caso.

gestire una soluzione alternativa per quando max = Int32.MaxValue è un po' scomodo, ma mi aspetto di pubblicare una funzione approfondita che gestisca entrambe queste situazioni.e se vuoi un comportamento diverso da come l'ho codificato, adattati.ma attenzione a questi 2 problemi.

Buona programmazione!

Modificare:Quindi avevo bisogno di un generatore di numeri interi casuali e ho deciso di codificarlo "giusto".Quindi, se qualcuno vuole la piena funzionalità, eccone uno che funziona davvero.(Ma non vince il premio più semplice con solo 2 righe di codice.Ma non è nemmeno molto complesso.)

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

Esempio Microsoft Funzione Rnd

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

1- Inizializza il generatore di numeri casuali.

Randomize()

2 - Genera un valore casuale compreso tra 1 e 6.

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

Se stai usando la risposta di Joseph, che è un'ottima risposta, e le esegui una dopo l'altra in questo modo:

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

Quindi il risultato potrebbe tornare sempre lo stesso perché elabora la chiamata così rapidamente.Questo potrebbe non essere stato un problema nel 2008, ma poiché i processori sono molto più veloci oggi, la funzione non concede all'orologio di sistema abbastanza tempo per cambiare prima di effettuare la seconda chiamata.

Poiché la funzione System.Random() è basata sull'orologio di sistema, è necessario concedere tempo sufficiente affinché possa cambiare prima della chiamata successiva.Un modo per ottenere ciò è mettere in pausa il thread corrente per 1 millisecondo.Vedi l'esempio qui sotto:

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

Dovresti creare un generatore di numeri pseudo-casuali solo una volta:

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

Quindi, se un numero intero è sufficiente per le tue esigenze, puoi utilizzare:

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

quante volte vuoi.L'uso della funzione wrapper è giustificato solo perché il valore massimo è esclusivo: so che i numeri casuali funzionano in questo modo ma la definizione di .Next è fonte di confusione.

Creare un generatore ogni volta che serve un numero è secondo me sbagliato;i numeri pseudo-casuali non funzionano in questo modo.

Innanzitutto, ottieni il problema con l'inizializzazione che è stato discusso nelle altre risposte.Se inizializzi una volta, non hai questo problema.

In secondo luogo, non sono affatto sicuro che si ottenga una sequenza valida di numeri casuali;piuttosto, ottieni una raccolta del primo numero di sequenze multiple diverse che vengono seminate automaticamente in base all'ora del computer.Non sono sicuro che questi numeri supereranno i test che confermeranno la casualità della sequenza.

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

Solo per riferimento, la definizione della funzione VB NET per RND e RANDOMIZE (che dovrebbe dare gli stessi risultati di BASIC (1980 anni) e tutte le versioni successive è:

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

Mentre la CLASSE Casuale è:

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] Questo è il modo migliore, da zero: P

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top