¿Hay alguna manera fácil de aleatorizar una lista en VB.NET?
visual-studio-2008 random (8)
Tengo una lista de tipo System.IO.FileInfo
, y me gustaría aleatorizar la lista. Pensé que recuerdo haber visto algo así como list.randomize()
un rato, pero no puedo encontrar dónde pude haber visto eso.
Mi primera incursión en esto me produjo esta función:
Private Shared Sub GetRandom(ByVal oMax As Integer, ByRef currentVals As List(Of Integer))
Dim oRand As New Random(Now.Millisecond)
Dim oTemp As Integer = -1
Do Until currentVals.Count = IMG_COUNT
oTemp = oRand.Next(1, oMax)
If Not currentVals.Contains(oTemp) Then currentVals.Add(oTemp)
Loop
End Sub
Le envío el valor máximo que quiero que itere, y una referencia a la lista en la que quiero el contenido aleatorizado. La variable IMG_COUNT
se establece más arriba en la secuencia de comandos, designando cuántas imágenes aleatorias quiero mostrar.
Gracias chicos, lo aprecio: D
Consulte el algoritmo aleatorio de Fisher-Yates aquí: http://en.wikipedia.org/wiki/Knuth_shuffle
con una discusión más concisa del jefe supremo de este sitio aquí: http://www.codinghorror.com/blog/archives/001015.html
Hay una implementación simple de C # en la entrada del blog que debería ser realmente fácil de cambiar a VB.NET
Hay varios métodos razonables de barajar.
Uno ya ha sido mencionado. (The Knuth Shuffle.)
Otro método sería asignar un "peso" a cada elemento y ordenar la lista de acuerdo con ese "peso". Este método es posible pero sería poco sofisticado porque no puede heredar de FileInfo.
Un método final sería seleccionar aleatoriamente un elemento en la lista original y agregarlo a una nueva lista. Por supuesto, eso es, si no te importa crear una nueva lista. (No he probado este código ...)
Dim rnd As New Random
Dim lstOriginal As New List(Of FileInfo)
Dim lstNew As New List(Of FileInfo)
While lstOriginal.Count > 0
Dim idx As Integer = rnd.Next(0, lstOriginal.Count - 1)
lstNew.Add(lstOriginal(idx))
lstOriginal.RemoveAt(idx)
End While
Puede crear un comparador personalizado que simplemente devuelve un número aleatorio, luego ordenar la lista usando este comparador. Podría ser terriblemente ineficiente y causar un ciclo casi infinito, pero podría valer la pena intentarlo.
Si tiene la cantidad de elementos, se puede usar un método pseudoaleatorio mediante el cual se elige el primer elemento al azar (por ejemplo, usando la función de número aleatorio incorporado), luego se agrega un primo y se toma el resto después de la división por el número de valores. por ejemplo, para una lista de 10, podría hacer i = (i + prime)% 10 para los índices generados i a partir de algún valor inicial. Siempre que el primo sea mayor que el número de valores en la lista, entonces usted crea una secuencia que se ejecuta a través de todos los números 0 ... n donde n es el número de valores - 1, pero en un orden pseudoaleatorio.
También puede implementar un orden aleatorio, de muchas maneras para hacer esto, lo más simple es elegir aleatoriamente un elemento e insertarlo en una nueva ubicación un montón de veces.
Construye un Comparador:
Public Class Randomizer(Of T)
Implements IComparer(Of T)
''''// Ensures different instances are sorted in different orders
Private Shared Salter As New Random() ''''// only as random as your seed
Private Salt As Integer
Public Sub New()
Salt = Salter.Next(Integer.MinValue, Integer.MaxValue)
End Sub
Private Shared sha As New SHA1CryptoServiceProvider()
Private Function HashNSalt(ByVal x As Integer) As Integer
Dim b() As Byte = sha.ComputeHash(BitConverter.GetBytes(x))
Dim r As Integer = 0
For i As Integer = 0 To b.Length - 1 Step 4
r = r Xor BitConverter.ToInt32(b, i)
Next
Return r Xor Salt
End Function
Public Function Compare(x As T, y As T) As Integer _
Implements IComparer(Of T).Compare
Return HashNSalt(x.GetHashCode()).CompareTo(HashNSalt(y.GetHashCode()))
End Function
End Class
Úselo así, suponiendo que se refiera a una List(Of FileInfo)
genérica List(Of FileInfo)
:
list.Sort(New Randomizer(Of IO.FileInfo)())
También puede usar un cierre para hacer que el valor aleatorio sea ''adhesivo'' y luego simplemente use .ql''s .OrderBy () en eso (C # esta vez, porque la sintaxis lambda de VB es fea):
list = list.OrderBy(a => Guid.NewGuid()).ToList();
Explicado aquí, junto con por qué podría no ser tan rápido como el barajado real:
http://www.codinghorror.com/blog/archives/001008.html?r=31644
Dim oRand As New Random() ''do not seed!!!!
Private Sub GetRandom(ByRef currentVals As List(Of Integer))
Dim i As New List(Of Integer), j As Integer
For x As Integer = 0 To currentVals.Count - 1
j = oRand.Next(0, currentVals.Count)
i.Add(currentVals(j))
currentVals.RemoveAt(j)
Next
currentVals = i
End Sub
Amplié la clase List
con la siguiente función Randomize()
para utilizar el algoritmo de mezcla de Fisher-Yates:
'''''' <summary>
'''''' Randomizes the contents of the list using Fisher–Yates shuffle (a.k.a. Knuth shuffle).
'''''' </summary>
'''''' <typeparam name="T"></typeparam>
'''''' <param name="list"></param>
'''''' <returns>Randomized result</returns>
'''''' <remarks></remarks>
<Extension()>
Function Randomize(Of T)(ByVal list As List(Of T)) As List(Of T)
Dim rand As New Random()
Dim temp As T
Dim indexRand As Integer
Dim indexLast As Integer = list.Count - 1
For index As Integer = 0 To indexLast
indexRand = rand.Next(index, indexLast)
temp = list(indexRand)
list(indexRand) = list(index)
list(index) = temp
Next index
Return list
End Function