2009-02-16 1 views
6

J'ai une liste de type System.IO.FileInfo, et je voudrais randomiser la liste. Je pensais me souvenir d'avoir vu quelque chose comme list.randomize() un peu plus tôt mais je ne peux pas trouver où j'ai pu voir ça.Existe-t-il un moyen facile de randomiser une liste dans VB.NET?

Ma première incursion dans ce qui m'a donné cette fonction.

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 

je l'envoyer au maximum val Je veux qu'il itérer jusqu'à, et une référence à la liste Je veux que le contenu aléatoire dans la La variable IMG_COUNT est placée plus loin dans le script, indiquant le nombre d'images aléatoires que je veux afficher.

Merci les gars, je l'apprécie: D

Répondre

2

Construire un Comparer:

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 

utiliser comme ceci, en supposant que dire un List(Of FileInfo) générique:

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

Vous pouvez également utiliser une fermeture pour rendre la valeur aléatoire « collante », puis il suffit d'utiliser .OrderBy() de linq sur ce (C# cette fois, car la syntaxe VB lambda est moche):

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

explicités ici, ainsi pourquoi aussi vite que réel aléatoire pourrait ne pas être même:
http://www.codinghorror.com/blog/archives/001008.html?r=31644

+0

Je continue d'obtenir une erreur: "Randomizer" de classe doit mettre en œuvre 'Function Compare (x comme T, y comme T) comme Integer' pour l'interface 'System.Collections.Generic.IComparer (of T)'. " Cette erreur est obtenue juste en utilisant votre deuxième bloc de code. – Anders

+0

Notez qu'avec la deuxième option, cette méthode n'a pas besoin de vivre dans une classe séparée, et vous l'utilisez via l'opérateur AddressOf comme indiqué, plutôt que de créer une instance de classe. –

+1

-1: Juste une mauvaise implémentation. La fonction ne randomise réellement rien, car deux listes contenant les mêmes éléments seront "randomisées" dans le même ordre. En outre, rien n'empêche les éléments séquentiels d'avoir des hashcodes séquentiels. Il existe de bien meilleurs moyens d'écrire cette fonction. – Juliet

-2

Vous pouvez créer comparateur personnalisé qui retourne juste un nombre aléatoire, puis trier la liste en utilisant ce comparateur. Il pourrait être horriblement inefficace et provoquer une boucle presque infinie, mais pourrait valoir la peine d'essayer.

+0

dans d'autres mots, est-ce que ma méthode est un bon moyen d'y aller? – Anders

+0

Non, vous voulez certainement utiliser array.Sort() avec un comparateur personnalisé. C'est juste une question de comment implémenter le comparateur-> éventuellement basé sur la valeur GetHashCode() de chaque objet. –

+0

Y at-il de bonnes ressources que vous connaissez pour les comparateurs personnalisés? Je n'avais pas l'air beaucoup dans ces derniers * canards * – Anders

1

Vous pouvez également mettre en œuvre un brassage, de nombreuses façons de le faire, le plus simple est de choisir au hasard un élément et l'insérer dans un nouvel emplacement un tas de fois.

0

Si vous avez le nombre d'éléments, vous pouvez utiliser une méthode pseudo-aléatoire en choisissant le premier élément au hasard (par exemple en utilisant la fonction de nombre aléatoire intégrée) puis ajouter un nombre premier et prendre le reste après le des valeurs. par exemple. Pour une liste de 10, vous pouvez faire i = (i + prime)% 10 aux indices générés i à partir d'une valeur de départ. Tant que le nombre premier est supérieur au nombre de valeurs de la liste, alors vous créez une séquence qui parcourt tous les nombres 0 ... n où n est le nombre de valeurs - 1, mais dans un ordre pseudo-aléatoire.

2

Il existe plusieurs méthodes raisonnables de brassage.

On a déjà mentionné. (Le Knuth Shuffle.)

Une autre méthode consisterait à attribuer un "poids" à chaque élément et à trier la liste en fonction de ce "poids". Cette méthode est possible mais ne serait pas utile car vous ne pouvez pas hériter de FileInfo.Une dernière méthode consisterait à sélectionner aléatoirement un élément dans la liste d'origine et à l'ajouter à une nouvelle liste. Bien sûr, si cela ne vous dérange pas de créer une nouvelle liste. (Ai pas testé ce code ...)


     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, malade gardez cela à l'esprit pour l'avenir. – 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

Si vous remplacez simplement 'oRand.Next (0, currentVals.Count)' par 'oRand.Next (x, currentVals.Count)', vous obtiendrez une meilleure distribution aléatoire. Voir [le blog perspicace de Jeff Atwood] (http://www.codinghorror.com/blog/2007/12/the-danger-of-naivete.html). –

5

J'ai étendu la classe List avec la fonction Randomize() suivante pour utiliser l'algorithme Fisher-Yates lecture aléatoire:

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