2013-04-02 5 views
3

Besoin de compléter le module Enumerable avec un nouvel itérateur, qui renvoie les éléments de la collection dans un ordre aléatoire. La seule information sur la collection - il répond à chacun. Aucune autre hypothèse sur les éléments. J'ai une solution - pour envelopper des éléments dans un tableau, puis utiliser la méthode de l'échantillon:Itérateur de permutation aléatoire

def each_permuted 
    tmp = [] 
    self.each do |w| 
     tmp << w 
    end 
    tmp.sample(tmp.length).each do |w| 
     yield w 
    end 
end 

ne l'aime pas, parce qu'ici nous traversons collection deux fois (même trois fois le comptage tmp.sample permutation aléatoire). Est-ce possible avec un simple passage?

+3

Si la collection ne répond qu'à 'each', alors vous devez passer complètement au moins une fois pour faire un échantillon aléatoire (car sinon vous ne connaissez même pas la longueur pour la probabilité de choisir des échantillons). Pas moyen de contourner cela AFAIK. Si les éléments de la collection étaient adressables de quelque manière que ce soit, vous pouvez échantillonner en fonction des adresses. Je pense que votre code est proche de l'optimal. Vous pouvez utiliser '.shuffle' au lieu de' .sample (tmp.length) 'cependant - je ne connais pas les internes de Ruby, mais cela a une chance d'être légèrement plus rapide pour vous. –

+3

Quel est le problème avec 'enumerable.to_a.shuffle'? – tokland

Répondre

3

Je doute que ce soit possible avec signle. Jetez un oeil à cette page: http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_.22inside-out.22_algorithm

I mis en œuvre l'algorithme nommé "l'algorithme inside-out" dans l'article (il passe par la collecte deux fois):

def each_permuted 
    generator = Random.new 
    tmp = [] 
    self.each do |w| 
    r = generator.rand(tmp.size + 1) 
    if r == tmp.size 
     tmp << w 
    else 
     tmp << tmp[r] 
     tmp[r] = w 
    end 
    end 

    tmp.each do |w| 
    yield w 
    end 
end 

Tests:

1.9.3p327 :064 > [1,2,3,4,5,6].each_permuted { |x| p x } 
1 
5 
2 
6 
3 
4 
=> [1, 5, 2, 6, 3, 4] 
1.9.3p327 :065 > [1,2,3,4,5,6].each_permuted { |x| p x } 
4 
3 
2 
5 
6 
1 
=> [4, 3, 2, 5, 6, 1] 
1.9.3p327 :066 > [1,2,3,4,5,6].each_permuted { |x| p x } 
4 
5 
2 
1 
3 
6 
=> [4, 5, 2, 1, 3, 6] 
0
def each_permuted &pr; shuffle.each(&pr) end 
+0

No. _Les seules informations sur la collection - il répond à each_. L'auteur n'a pas dit qu'il répondait à "shuffle" ou à toute autre méthode. – DNNX

+0

Je pense que 'def each_permuted ≺ to_a.shuffle.each (& pr) end' serait correct, puisque nous augmentons' Enumerable' et que nous ajoutons 'to_a' en nous basant sur' each'. –