36 votes

Comment générer une liste de n nombres aléatoires uniques dans Ruby?

Voici ce que j'ai jusqu'à présent:

 myArray.map!{ rand(max) }
 

De toute évidence, cependant, parfois les numéros de la liste ne sont pas uniques. Comment puis-je m'assurer que ma liste ne contient que des numéros uniques sans avoir à créer une liste plus grande à partir de laquelle je choisis ensuite les n numéros uniques?

Éditer:
J'aimerais vraiment voir ce faire sans boucle - si possible.

66voto

Kent Fredric Points 35592
 (0..50).to_a.sort{ rand() - 0.5 }[0..x]
 

(0..50).to_a peut être remplacé par n'importe quel tableau. 0 est "valeur min", 50 est "valeur max" x est "combien de valeurs je veux"

bien sûr, il est impossible que x soit supérieur à max-min :)

En élargissant comment cela fonctionne

 (0..5).to_a  ==> [0,1,2,3,4,5]
[0,1,2,3,4,5].sort{ -1 }  ==>  [0, 1, 2, 4, 3, 5]  # constant
[0,1,2,3,4,5].sort{  1 }  ==>  [5, 3, 0, 4, 2, 1]  # constant
[0,1,2,3,4,5].sort{ rand() - 0.5 }   ==>  [1, 5, 0, 3, 4, 2 ]  # random
[1, 5, 0, 3, 4, 2 ][ 0..2 ]   ==>  [1, 5, 0 ]
 

25voto

Ryan Leavengood Points 216

Cela utilise Set:

 require 'set'

def rand_n(n, max)
    randoms = Set.new
    loop do
        randoms << rand(max)
        return randoms.to_a if randoms.size >= n
    end
end
 

24voto

Duncan Beevers Points 1587

Ruby 1.9 propose la méthode d'exemple Array # qui renvoie un élément ou des éléments sélectionnés au hasard dans un tableau. Les résultats de #sample n'incluront pas deux fois le même élément Array.

 (1..999).to_a.sample 5 # => [389, 30, 326, 946, 746]
 

Par rapport au to_a.sort_by approche, le sample méthode semble être nettement plus rapide. Dans un scénario simple, j'ai comparé sort_by à sample , et j'ai obtenu les résultats suivants.

 require 'benchmark'
range = 0...1000000
how_many = 5

Benchmark.realtime do
  range.to_a.sample(how_many)
end
=> 0.081083

Benchmark.realtime do
  (range).sort_by{rand}[0...how_many]
end
=> 2.907445
 

22voto

glenn mcdonald Points 8933

Juste pour vous donner une idée sur la vitesse, j'ai couru quatre versions de ce:

  1. À l'aide des Ensembles, à l'instar de Ryan suggestion.
  2. L'utilisation d'un Tableau légèrement plus grande que nécessaire, puis de le faire uniq! à la fin.
  3. À l'aide d'une table de Hachage, comme Kyle suggéré.
  4. Création d'un Tableau de la taille requise, puis à les trier de façon aléatoire, comme une suggestion de Kent (mais sans le superflu "- 0.5", qui ne fait rien).

Ils sont tous rapidement à de très petites échelles, donc je leur ai demandé de créer une liste de 1 000 000 de numéros. Voici les temps, en secondes:

  1. Jeux: 628
  2. Tableau + uniq: 629
  3. Hash: 645
  4. Tableau fixe + tri: 8

Et non, ce dernier n'est pas une faute de frappe. Donc, si vous vous souciez de la vitesse, et c'est OK pour les nombres entiers de 0 à quoi que ce soit alors mon code exact était:

a = (0...1000000).sort_by{rand}

4voto

Bryan Larsen Points 2630

Oui, il est possible de le faire sans boucle et sans garder une trace des numéros qui ont été choisis. Cela s'appelle un registre à décalage à rétroaction linéaire: http://stackoverflow.com/questions/693880/create-random-number-sequence-with-no-repeats

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X