2010-10-30 22 views
2

J'essaie de mélanger un tableau, mais la façon dont je le fais ne fonctionne que toutes les cinq fois. J'apprécierais grandement si quelqu'un pourrait expliquer pourquoi cela ne fonctionne pas correctement et peut-être proposer un tweak.Ineffective Array Shuffler

private Button[] scrambleBoard(Button[] buttons) 
{ 
    for (int x = 100 * buttons.Count(); x > 0; x--) 
    { 
     Random rand = new Random(); 
     int first = rand.Next(buttons.Count()); 
     int second = rand.Next(buttons.Count()); 


     Button temp = buttons[first]; 
     buttons[first] = buttons[second]; 
     buttons[second] = temp; 
    } 

    return buttons; 
} 

Répondre

3

déplacer la ligne suivante en dehors de la boucle:

Random rand = new Random(); 

La graine par défaut utilisée par System.Random est basé sur Environment.TickCount. Dans une boucle serrée, le nombre de ticks peut ne pas changer entre les itérations successives, ainsi il peut finir par utiliser avec la même graine encore et encore. Par conséquent, la boucle va échanger à plusieurs reprises les mêmes éléments jusqu'à ce que le nombre de ticks change (il peut ne pas le faire avant la fin de la boucle). Pour vérifier que c'est le problème, vous pouvez essayer d'ajouter un Thread.Sleep(100) ou similaire à l'intérieur de la boucle; vous devriez alors être capable de voir le shuffle fonctionner correctement (quoique très lentement).

Vous devez également noter que le technique you're using pour permuter le tableau est polarisé; toutes les permutations ne sont pas également probables. Vous pouvez utiliser un algorithme de brassage connu pour être sans biais, tel que le Fisher-Yates shuffle.

Alternativement, vous pouvez utiliser une technique très simple pour mélanger. Il est légèrement inefficace, mais non biaisé:

var rand = new Random(); 
return buttons.OrderBy(button => rand.Next()).ToArray(); 
+3

Dang, je me sens bête. Je n'ai plus besoin de boire pendant le codage ... Merci! – PFranchise

+1

Voici un bon livre blanc qui explique les conséquences d'un brassage biaisé et d'une mauvaise initialisation de PRNG: Comment nous avons appris à tricher au poker en ligne. http://www.cigital.com/papers/download/developer_gambling.php – PleaseStand

+0

@idealmachine: Nice one. – Ani

1

Le problème est que vous créez l'objet Random() à chaque itération de votre boucle. Comme l'objet Random utilise une graine lors de l'initialisation, vous verrez que la plupart des valeurs seront identiques et non aléatoires.

Vous pouvez résoudre le problème en déclarant la classe Random statique en dehors du corps de la méthode.

private static Random rand = new Random(); 

private Button[] scrambleBoard(Button[] buttons) 
{ 
    for (int x = 100 * buttons.Count(); x > 0; x--) 
    { 
     int first = rand.Next(buttons.Count()); 
     int second = rand.Next(buttons.Count()); 


     Button temp = buttons[first]; 
     buttons[first] = buttons[second]; 
     buttons[second] = temp; 
    } 

    return buttons; 
} 
+0

Appréciez la réponse. +1! – PFranchise

+1

Le Random n'a vraiment pas besoin d'être statique, au fait ... –

+1

Et comme ce n'est pas thread-safe, s'il y a une chance multiple les threads peuvent l'utiliser, il ne doit pas être statique (ou vous devez ajouter une synchronisation appropriée). –

0

Votre question a été répondue, mais j'ai pensé que je partagerais un bon petit truc pour mélanger une collection en utilisant LINQ et Guid. Cela crée une liste ordonnée au hasard avec une bonne répartition des valeurs.

private Button[] scrambleBoard(Button[] buttons) 
{ 
    return buttons.OrderBy(b => Guid.NewGuid()).ToArray(); 
}