2009-02-16 20 views
6

Ho una lista di tipo System.IO.FileInfo, e vorrei randomizzare la lista. Ho pensato di ricordare di aver visto qualcosa come list.randomize() un po 'di tempo fa ma non riesco a trovare dove possa averlo visto.Esiste un modo semplice per randomizzare un elenco in VB.NET?

La mia prima incursione in questo mi ha dato con questa funzione:.

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 

ho inviarlo al massimo val voglio che iterare fino a, e un riferimento alla lista che voglio il contenuto randomizzato in The la variabile IMG_COUNT è impostata più in alto nello script, che indica quante immagini casuali voglio visualizzare.

Grazie ragazzi, lo apprezzo: D

risposta

2

costruire un operatore di confronto:

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 

usare in questo modo, supponendo che si intende un generico List(Of FileInfo):

list.Sort(New Randomizer(Of IO.FileInfo)()) 

È inoltre possibile utilizzare una chiusura per rendere il valore casuale 'appiccicosa' e quindi basta usare linq's .OrderBy() su questo (C# questa volta, perché la sintassi lambda VB è brutta):

list = list.OrderBy(a => Guid.NewGuid()).ToList(); 

spiegato qui, insieme con il motivo per cui potrebbe anche non essere veloce come vero e proprio riordino:
http://www.codinghorror.com/blog/archives/001008.html?r=31644

+0

Continuo a ricevere un errore: "Class 'Randomizer' deve implementare 'Function Compare (x come T, y as T) As Integer' per l'interfaccia 'System.Collections.Generic.IComparer (di T)'." Questo errore si ottiene semplicemente usando il tuo secondo blocco di codice. – Anders

+0

Si noti che con la seconda opzione il metodo non ha bisogno di vivere in una classe separata e lo si utilizza tramite l'operatore AddressOf come mostrato, piuttosto che creando un'istanza di classe. –

+1

-1: solo una cattiva implementazione. La funzione in realtà non randomizza nulla, perché due liste contenenti gli stessi elementi saranno "randomizzate" nello stesso ordine. Inoltre, nulla impedisce che gli articoli sequenziali abbiano hashcode sequenziali. Ci sono modi molto migliori per scrivere questa funzione. – Juliet

-2

Si potrebbe creare di confronto personalizzato che restituisce solo un numero casuale, quindi ordinare l'elenco utilizzando questo operatore di confronto. Potrebbe essere orribilmente inefficiente e causare un ciclo quasi infinito, ma potrebbe valere la pena di provarlo.

+0

in altri parole, il mio metodo è un buon modo per andare? – Anders

+0

No, si vuole assolutamente usare array.Sort() con un comparatore personalizzato. È solo questione di come implementare il comparatore-> possibilmente basato sul valore GetHashCode() di ciascun oggetto. –

+0

ci sono buone risorse che conosci per comparatori personalizzati? Non ho guardato molto in quelli di late * anatre * – Anders

1

Si potrebbe anche implementare un shuffle, molti modi per farlo, il più semplice è scegliere casualmente un oggetto e inserirlo in una nuova posizione un sacco di volte.

0

Se si dispone del numero di elementi, è possibile utilizzare un metodo pseudo-casuale in cui si sceglie il primo elemento a caso (ad esempio utilizzando la funzione di numero casuale incorporato) quindi aggiungere un primo e prendere il resto dopo la divisione per il numero di valori. per esempio. per una lista di 10 potresti fare i = (i + prime)% 10 agli indici generati i da un valore iniziale. Finché il primo è maggiore del numero di valori nell'elenco, crei una sequenza che attraversa tutti i numeri 0 ... n dove n è il numero di valori - 1, ma in ordine pseudocasuale.

2

Ci sono diversi metodi ragionevoli di rimescolamento.

Uno è già stato menzionato. (The Knuth Shuffle.)

Un altro metodo sarebbe assegnare un "peso" a ciascun elemento e ordinare l'elenco in base a quel "peso". Questo metodo è possibile ma sarebbe spiacevole perché non puoi ereditare da FileInfo.

Un metodo finale sarebbe quello di selezionare casualmente un elemento nell'elenco originale e aggiungerlo a un nuovo elenco. Ovviamente, se non ti dispiace creare una nuova lista. (Non ho ancora testato questo codice ...)


     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 
+0

cool, malato di tenere questo in mente per il futuro. – Anders

0
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 
+0

Se si sostituisce semplicemente 'oRand.Next (0, currentVals.Count)' con 'oRand.Next (x, currentVals.Count)', si otterrà una migliore distribuzione casuale. Vedi [Insightful blog post di Jeff Atwood] (http://www.codinghorror.com/blog/2007/12/the-danger-of-naivete.html). –

5

ho esteso la classe List con la Randomize() seguente funzione di utilizzare la Fisher-Yates mischiare algoritmo:

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