vb.net - rnd - visual basic 2015 random number
Entero aleatorio en VB.NET (11)
Necesito generar un entero aleatorio entre 1 y n (donde n es un número entero positivo) para usar para una prueba unitaria. No necesito algo demasiado complicado para garantizar la verdadera aleatoriedad, solo un número aleatorio anticuado.
¿Como podría hacerlo?
Como se ha señalado muchas veces, la sugerencia de escribir código como este es problemática:
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
La razón es que el constructor de la clase Random
proporciona una semilla predeterminada basada en el reloj del sistema. En la mayoría de los sistemas, esto tiene una granularidad limitada, en algún lugar cercano a los 20 ms. Entonces, si escribe el siguiente código, obtendrá el mismo número un montón de veces seguidas:
Dim randoms(1000) As Integer
For i As Integer = 0 to randoms.Length - 1
randoms(i) = GetRandom(1, 100)
Next
El siguiente código aborda este 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
Lancé un programa simple usando ambos métodos para generar 25 enteros aleatorios entre 1 y 100. Aquí está el resultado:
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
Deberías crear un generador de números pseudoaleatorio solo una vez:
Dim Generator As System.Random = New System.Random()
Entonces, si un número entero es suficiente para sus necesidades, puede usar:
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
tantas veces como quieras El uso de la función de envoltura se justifica solo porque el valor máximo es exclusivo: sé que los números aleatorios funcionan de esta manera, pero la definición de .Next es confusa.
Crear un generador cada vez que necesita un número es, en mi opinión, incorrecto; los números pseudoaleatorios no funcionan de esta manera.
Primero, obtienes el problema con la inicialización que se ha discutido en las otras respuestas. Si se inicializa una vez, no tiene este problema.
En segundo lugar, no estoy del todo seguro de que obtenga una secuencia válida de números aleatorios; más bien, obtienes una colección de la primera cantidad de múltiples secuencias diferentes que se inicializan automáticamente en función del tiempo de la computadora. No estoy seguro de que estos números pasen las pruebas que confirman la aleatoriedad de la secuencia.
Microsoft Example Rnd Function
https://msdn.microsoft.com/en-us/library/f7s023d2%28v=vs.90%29.aspx
1- Inicializa el generador de números aleatorios.
Randomize()
2 - Genera un valor aleatorio entre 1 y 6.
Dim value As Integer = CInt(Int((6 * Rnd()) + 1))
Para obtener un valor entero aleatorio entre 1 y N (inclusive) puede usar lo siguiente.
CInt(Math.Ceiling(Rnd() * n)) + 1
Si está utilizando la respuesta de José, que es una gran respuesta, y la ejecuta de nuevo de la siguiente manera:
dim i = GetRandom(1, 1715)
dim o = GetRandom(1, 1715)
Entonces, el resultado podría repetirse una y otra vez, ya que procesa la llamada tan rápido. Esto puede no haber sido un problema en ''08, pero dado que los procesadores son mucho más rápidos hoy en día, la función no permite que el reloj del sistema tenga tiempo suficiente para cambiar antes de hacer la segunda llamada.
Dado que la función System.Random () se basa en el reloj del sistema, debemos dejar tiempo suficiente para que cambie antes de la próxima llamada. Una forma de lograr esto es pausar el hilo actual durante 1 milisegundo. Vea el siguiente ejemplo:
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
Solo como referencia, la definición de VB NET Fuction para RND y RANDOMIZE (que debe dar los mismos resultados de BASIC (1980 años) y todas las versiones posteriores es:
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
Mientras que la CLASE Aleatoria es:
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
Todas las respuestas hasta ahora tienen problemas o errores (en plural, no solo uno). Lo explicaré. Pero primero quiero complementar la idea de Dan Tao de usar una variable estática para recordar la variable Generador, por lo que llamarla varias veces no repetirá el mismo # una y otra vez, además de que me dio una explicación muy buena. Pero su código sufrió el mismo defecto que la mayoría de los demás, como explico ahora.
MS hizo su método Next () bastante extraño. el parámetro Mín es el mínimo inclusivo como cabría esperar, pero el parámetro Máx es el máximo exclusivo que uno NO esperaría. en otras palabras, si pasa min = 1 y max = 5, sus números aleatorios serían cualquiera de 1, 2, 3 o 4, pero nunca incluiría 5. Este es el primero de dos posibles errores en todo el código que usa el método Random.Next () de Microsoft.
Para una respuesta simple (pero aún con otros problemas posibles pero raros), entonces necesitaría usar:
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
(Me gusta usar Int32
lugar de Integer
porque deja más claro el tamaño del int, además de que es más corto de escribir, pero se adapta a ti).
Veo dos posibles problemas con este método, pero será adecuado (y correcto) para la mayoría de los usos. Entonces, si quieres una solución simple , creo que esto es correcto.
Los únicos 2 problemas que veo con esta función son: 1: cuando Max = Int32.MaxValue, por lo que agregar 1 crea un desbordamiento numérico. aunque, esto sería raro, todavía es una posibilidad. 2: cuando min> max + 1. cuando min = 10 y max = 5, la función Next arroja un error. esto puede ser lo que quieras. pero puede no ser tampoco. o considerar cuando min = 5 y max = 4. agregando 1, 5 pasa al método Next, pero no arroja un error, cuando realmente es un error, pero el código de Microsoft .NET que probé devuelve 5. entonces realmente no es un máximo ''exclusivo'' cuando el máximo = el mínimo. pero cuando max <min para la función Random.Next (), arroja una ArgumentOutOfRangeException. por lo que la implementación de Microsoft es realmente inconsistente y con errores también en este sentido.
Es posible que desee simplemente intercambiar los números cuando min> max para que no se genere ningún error, pero depende totalmente de lo que se desee. si desea un error en los valores no válidos, entonces es probable que también arroje el error cuando el máximo exclusivo de Microsoft (máximo + 1) en nuestro código es igual al mínimo, donde MS falla al error en este caso.
manejar una solución temporal para cuando max = Int32.MaxValue es un poco inconveniente, pero espero publicar una función completa que maneje ambas situaciones. y si quieres un comportamiento diferente de como lo codifiqué, únete a ti mismo. pero tenga en cuenta estos 2 problemas.
Feliz codificación!
Editar: Así que necesitaba un generador de enteros aleatorio, y decidí codificarlo ''a la derecha''. Entonces, si alguien quiere la funcionalidad completa, aquí hay una que realmente funciona. (Pero no gana el premio más simple con solo 2 líneas de código. Pero tampoco es realmente complejo).
'''''' <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
Use 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)
Dim rnd As Random = New Random
rnd.Next(n)
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] Esta es la mejor manera, desde cero: P
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