numeros metodos longitud listas lista funciones elemento crear como arreglos arreglo array aleatorios agregar ruby random

metodos - ¿Cómo puedo generar una lista de n números aleatorios únicos en Ruby?



numeros aleatorios en ruby (14)

Método 1

Usando el enfoque de Kent, es posible generar una matriz de longitud arbitraria manteniendo todos los valores en un rango limitado:

# Generates a random array of length n. # # @param n length of the desired array # @param lower minimum number in the array # @param upper maximum number in the array def ary_rand(n, lower, upper) values_set = (lower..upper).to_a repetition = n/(upper-lower+1) + 1 (values_set*repetition).sample n end

Método 2

Otro método, posiblemente más eficiente , modificado a partir de la misma respuesta de Kent:

def ary_rand2(n, lower, upper) v = (lower..upper).to_a (0...n).map{ v[rand(v.length)] } end

Salida

puts (ary_rand 5, 0, 9).to_s # [0, 8, 2, 5, 6] expected puts (ary_rand 5, 0, 9).to_s # [7, 8, 2, 4, 3] different result for same params puts (ary_rand 5, 0, 1).to_s # [0, 0, 1, 0, 1] repeated values from limited range puts (ary_rand 5, 9, 0).to_s # [] no such range :)

Esto es lo que tengo hasta ahora:

myArray.map!{ rand(max) }

Obviamente, sin embargo, a veces los números en la lista no son únicos. ¿Cómo puedo asegurarme de que mi lista solo contenga números únicos sin tener que crear una lista más grande de la cual simplemente elijo los n números únicos?

Editar:
Realmente me gustaría ver esto hecho sin ciclo, si es posible.


¿Qué tal una obra de teatro sobre esto? Números aleatorios únicos sin necesidad de usar Set o Hash.

x = 0 (1..100).map{|iter| x += rand(100)}.shuffle


Aquí hay una solución:

Supongamos que quiere que estos números aleatorios estén entre r_min y r_max . Para cada elemento en su lista, genere un número aleatorio r , y haga una list[i]=list[i-1]+r . Esto le daría números aleatorios que aumentan monótonamente, garantizando la singularidad siempre que

  • r+list[i-1] no pasa demasiado
  • r > 0

Para el primer elemento, usaría r_min lugar de list[i-1] . Una vez que haya terminado, puede mezclar la lista para que los elementos no estén tan obviamente en orden.

El único problema con este método es cuando r_max y todavía tienes más elementos para generar. En este caso, puede reiniciar r_min y r_max en 2 elementos adyacentes que ya ha computado, y simplemente repita el proceso. Esto ejecuta efectivamente el mismo algoritmo en un intervalo en el que ya no se usan números. Puedes seguir haciendo esto hasta que llenes la lista.


Basado en la solución anterior de Kent Fredric, esto es lo que terminé usando:

def n_unique_rand(number_to_generate, rand_upper_limit) return (0..rand_upper_limit - 1).sort_by{rand}[0..number_to_generate - 1] end

Gracias Kent.


En lugar de agregar los elementos a una lista / matriz, agréguelos a un conjunto.


Esto usa 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


No hay bucles con este método

Array.new(size) { rand(max) } require ''benchmark'' max = 1000000 size = 5 Benchmark.realtime do Array.new(size) { rand(max) } end => 1.9114e-05


Podría usar un hash para rastrear los números aleatorios que ha usado hasta ahora:

seen = {} max = 100 (1..10).map { |n| x = rand(max) while (seen[x]) x = rand(max) end x }


Por lo que es bueno saber de antemano el valor máximo, puede hacerlo de esta manera:

class NoLoopRand def initialize(max) @deck = (0..max).to_a end def getrnd return @deck.delete_at(rand(@deck.length - 1)) end end

y puede obtener datos aleatorios de esta manera:

aRndNum = NoLoopRand.new(10) puts aRndNum.getrnd

Obtendrás nil cuando todos los valores se agoten desde el mazo.


Ruby 1.9 ofrece el método de muestra Array # que devuelve un elemento o elementos seleccionados al azar de una matriz. Los resultados de #sample no incluirán el mismo elemento Array dos veces.

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

Cuando se compara con el enfoque to_a.sort_by , el método de sample parece ser significativamente más rápido. En un escenario simple, sort_by con la sample y obtuve los siguientes resultados.

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



Si tiene una lista finita de posibles números aleatorios (es decir, de 1 a 100), entonces la solución de Kent es buena.

De lo contrario, no hay otra buena manera de hacerlo sin bucle. El problema es que DEBES hacer un ciclo si obtienes un duplicado. Mi solución debe ser eficiente y el bucle no debe ser demasiado más que el tamaño de su matriz (es decir, si desea 20 números aleatorios únicos, podría tomar 25 iteraciones en promedio). Aunque el número de iteraciones empeora, más números necesidad y el máximo es menor. Aquí está mi código anterior modificado para mostrar cuántas iteraciones se necesitan para la entrada dada:

require ''set'' def rand_n(n, max) randoms = Set.new i = 0 loop do randoms << rand(max) break if randoms.size > n i += 1 end puts "Took #{i} iterations for #{n} random numbers to a max of #{max}" return randoms.to_a end

Podría escribir este código para MIRAR más como Array.map si quieres :)


Solo para darle una idea sobre la velocidad, ejecuté cuatro versiones de esto:

  1. Usando Sets, como la sugerencia de Ryan.
  2. Usando una matriz un poco más grande de lo necesario, ¡entonces haciendo uniq! al final.
  3. Usando un Hash, como sugirió Kyle.
  4. Creando una Matriz del tamaño requerido, luego ordenándola al azar, como la sugerencia de Kent (pero sin el extraño "- 0.5", que no hace nada).

Todos son rápidos a pequeña escala, así que los hice crear una lista de 1,000,000 de números. Estos son los tiempos, en segundos:

  1. Conjuntos: 628
  2. Array + uniq: 629
  3. Hash: 645
  4. Arreglo fijo + ordenación: 8

Y no, ese último no es un error tipográfico. Entonces, si te preocupa la velocidad, y está bien que los números sean enteros de 0 a lo que sea, entonces mi código exacto fue:

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


(0..50).to_a.sort{ rand() - 0.5 }[0..x]

(0..50).to_a se puede reemplazar con cualquier matriz. 0 es "valor mínimo", 50 es "valor máximo" x es "cuántos valores deseo"

por supuesto, es imposible que x sea mayor que max-min :)

En expansión de cómo funciona esto

(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 ]

Notas al pie:

Vale la pena mencionar que en el momento en que se respondió originalmente esta pregunta, en septiembre de 2008, ese Array#shuffle o no estaba disponible o no estaba ya disponible para mí, de ahí la aproximación en Array#sort

Y como resultado, hay un aluvión de ediciones sugeridas.

Asi que:

.sort{ rand() - 0.5 }

Puede ser mejor y más breve en las implementaciones de ruby ​​modernas que usan

.shuffle

Adicionalmente,

[0..x]

Puede escribirse más obviamente con Array#take as:

.take(x)

Por lo tanto, la manera más fácil de producir una secuencia de números aleatorios en un rubí moderno es:

(0..50).to_a.shuffle.take(x)