2008-09-23 17 views
8

Ayer, hice la pregunta this y nunca obtuve una respuesta con la que estaba realmente feliz. Realmente me gustaría saber cómo generar una lista de N números aleatorios únicos usando un lenguaje funcional como Ruby sin tener que ser extremadamente imperativo en estilo.¿Cómo escribo esto en Ruby/Python? O bien, ¿puedes traducir mi LINQ a Ruby/Python?

Como yo no vi nada que me gustó, he escrito la solución que estaba buscando en LINQ:


     static void Main(string[] args) 
     { 
      var temp = from q in GetRandomNumbers(100).Distinct().Take(5) select q; 
     } 

     private static IEnumerable GetRandomNumbers(int max) 
     { 
      Random r = new Random(); 
      while (true) 
      { 
       yield return r.Next(max); 
      } 
     } 

¿Se puede traducir mi LINQ a Ruby? ¿Pitón? ¿Algún otro lenguaje de programación funcional?

Nota: Intente no utilizar demasiados bucles y condicionales: de lo contrario, la solución es trivial. Además, prefiero ver una solución en la que no tiene que generar una matriz mucho más grande que N para que pueda luego simplemente eliminar los duplicados y recortar hacia abajo a N.

Sé que estoy siendo muy delicada, pero realmente me gustaría ver algunas soluciones elegantes para este problema. Gracias!

Editar:
por qué todos los downvotes?

Originalmente, mi muestra de código tenía Distinct() después de Take() que, como muchos señalaron, podría dejarme con una lista vacía. He cambiado el orden en que se llaman esos métodos para reflejar lo que quise decir en primer lugar.

Apología:
me han dicho que este post llegó a ser tan bien snob. No estaba tratando de dar a entender que LINQ es mejor que Ruby/Python; o que mi solución es mucho mejor que la de los demás. Mi intención es solo aprender a hacer esto (con ciertas restricciones) en Ruby. Lo siento si me encontré como un idiota.

+0

Sólo para que esté claro en esto: a pesar de que Python tiene algunas construcciones funcionales, como las listas por comprensión, en realidad no es un lenguaje funcional, y esto no es una problema que resolvería fácilmente en un verdadero estilo funcional en Python. –

+0

No obtengo los requisitos. ¿Es tomar N valores y ubicar los distintos valores en ese conjunto? ¿O es para localizar un conjunto de algún tamaño que tenga N valores distintos? –

+0

Si toma (5) y luego distinto ... puede terminar con 1 número. –

Respuesta

5

En Ruby:

a = (0..100).entries.sort_by {rand}.slice! 0, 5 

actualización: Aquí se presenta una forma ligeramente diferente: a = (0 ... 100) .entries.sort_by {rand} [0 ... 5]

EDIT:

y en Ruby 1.9 se puede hacer esto:

Array(0..100).sample(5) 
0

Python numérico con Python:

from numpy import * 
a = random.random_integers(0, 100, 5) 
b = unique(a) 

Voilà! Claro que podrías hacer algo similar en un estilo de programación funcional, pero ... ¿por qué?

+0

Porque esto no usa iteradores y almacena todos los enteros en la memoria. –

2

Renunciaré a las soluciones más simples utilizando el módulo 'aleatorio', ya que considero que eso no es realmente lo que buscas. Esto es lo que creo que busca en Python:

>>> import random 
>>> 
>>> def getUniqueRandomNumbers(num, highest): 
...  seen = set() 
...  while len(seen) < num: 
...   i = random.randrange(0, highest) 
...   if i not in seen: 
...    seen.add(i) 
...    yield i 
... 
>>> 

para mostrar cómo funciona:

>>> list(getUniqueRandomNumbers(10, 100)) 
[81, 57, 98, 47, 93, 31, 29, 24, 97, 10] 
13
>>> import random 
>>> print random.sample(xrange(100), 5) 
[61, 54, 91, 72, 85] 

Esto debería producir 5 valores únicos en el rango 0 — 99. El objeto xrange genera valores según lo solicitado, por lo que no se utiliza memoria para los valores que no se muestrean.

+3

xrange() de hecho no usa un generador. es una lista falsa Los generadores no son secuencias y no pueden indexarse, por lo que random.sample() fallaría si fuera uno. –

+0

Derecha, por supuesto, gracias. Corregido –

+0

¿Por qué las muestras serían únicas? No parece haber un filtro de ningún tipo para asegurar la singularidad. –

-1

Realmente no puedo leer su LINQ, pero creo que está tratando de obtener 5 números aleatorios hasta 100 y luego eliminar duplicados.

Aquí es una solución para eso:

def random(max) 
    (rand * max).to_i 
end 

# Get 5 random numbers between 0 and 100 
a = (1..5).inject([]){|acc,i| acc << random(100)} 
# Remove Duplicates 
a = a & a 

Pero tal vez en realidad estás buscando un 5 números aleatorios distintos entre 0 y 100. En este caso:

def random(max) 
    (rand * max).to_i 
end 

a = [] 
while(a.size < 5) 
    a << random(100) 
    a = a & a 
end 

Ahora, éste podría viole su sentido de "no demasiados bucles", pero presumiblemente Take and Distinct simplemente le oculta el bucle. Sería bastante fácil simplemente agregar métodos a Enumerable para ocultar el ciclo while.

+0

Verdadero: me doy cuenta de que Take & Distinct probablemente esté dando vueltas entre bastidores. Lo que quise decir es que no hay bucle que deba escribir ... Sin embargo, me gustan sus segundas soluciones. ¡Gracias! –

+0

'(rand * max) .to_i' debe escribirse como' rand max' – user102008

3

Hmm ... ¿Qué tal (Python):

s = set() 
while len(s) <= N: s.update((random.random(),)) 
+0

¿por qué no usar set.add? –

+0

¡Malo! Gracias por señalar eso ;-) –

2

Aquí hay otra solución Ruby:

a = (1..5).collect { rand(100) } 
a & a 

Creo que, con su declaración de LINQ, la Distinto será eliminar duplicados después de las 5 ya se han tomado , por lo que no está garantizado que recuperes 5. Aunque alguien puede corregirme si me equivoco.

+0

Sí, estaba preocupado por eso también. Sin embargo, aún no he podido producir una matriz con menos de 5 elementos. No significa que podría suceder, sin embargo. En el peor de los casos, podría llamar a Distinct() antes de Take(), ¿verdad? –

0
import random 

def makeRand(n): 
    rand = random.Random() 
    while 1: 
     yield rand.randint(0,n) 
    yield rand.randint(0,n)  

gen = makeRand(100)  
terms = [ gen.next() for n in range(5) ] 

print "raw list" 
print terms 
print "de-duped list" 
print list(set(terms)) 

# produces output similar to this 
# 
# raw list 
# [22, 11, 35, 55, 1] 
# de-duped list 
# [35, 11, 1, 22, 55] 
0

Bueno, primero reescribe LINQ en Python. A continuación, la solución es una sola línea :)

from random import randrange 

def Distinct(items): 
    set = {} 
    for i in items: 
     if not set.has_key(i): 
      yield i 
      set[i] = 1 

def Take(num, items): 
    for i in items: 
     if num > 0: 
      yield i 
      num = num - 1 
     else: 
      break 

def ToArray(items): 
    return [i for i in items] 

def GetRandomNumbers(max): 
    while 1: 
     yield randrange(max) 

print ToArray(Take(5, Distinct(GetRandomNumbers(100)))) 

Si pones todos los métodos anteriormente indicados en un módulo llamado LINQ.py, puede impresionar a sus amigos.

(Negación:.. Por supuesto, esto no es realidad reescribir LINQ en Python La gente tiene la idea errónea de que LINQ es sólo un montón de métodos de extensión triviales y algunos nueva sintaxis La parte realmente avanzada de LINQ, sin embargo, es generación automática de SQL para que cuando consulte una base de datos, sea la base de datos que implementa Distinct() en lugar del lado del cliente.)

+0

Agradable. Solo un pequeño comentario: debe usar un conjunto() en lugar de un hash en Distince y ToArray puede usar list(). – olt

2

EDITAR: Ok, solo por diversión, una más corta y más rápida (y aún usando iteradores)

def getRandomNumbers(max, size) : 
    pool = set() 
    return ((lambda x : pool.add(x) or x)(random.randrange(max)) for x in xrange(size) if len(a) < size) 

print [x for x in gen(100, 5)] 
[0, 10, 19, 51, 18] 

Sí, lo sé, de una sola línea se deben dejar a perl amantes, pero creo que éste es bastante potente ¿verdad?

mensaje del Antiguo aquí:

Mi dios, lo complicado es todo eso!Vamos a ser Pythonic:

import random 
def getRandomNumber(max, size, min=0) : 
    # using() and xrange = using iterators 
    return (random.randrange(min, max) for x in xrange(size)) 

print set(getRandomNumber(100, 5)) # set() removes duplicates 
set([88, 99, 29, 70, 23]) 

Disfrute

EDIT: Como notaron los comentaristas, esto es una traducción exacta del código de la pregunta.

Para evitar el problema que conseguimos mediante la eliminación de duplicados después de generar la lista, lo que resulta en muy pocos datos, puede elegir otro camino:

def getRandomNumbers(max, size) : 
    pool = [] 
    while len(pool) < size : 
     tmp = random.randrange(max) 
     if tmp not in pool : 
      yield pool.append(tmp) or tmp 

print [x for x in getRandomNumbers(5, 5)] 
[2, 1, 0, 3, 4] 
+0

Si tiene un duplicado eliminado, ¿no terminará con menos valores de los deseados? –

+0

Sí, pero él hizo lo mismo en su pregunta, por lo que es una traducción exacta. Eso es lo que se pide: una traducción. –

+0

No lo hizo: el .Take (5) se produce después de la llamada .Distinct, por lo que se dibujarán 5 elementos de una secuencia ya no unificada. – Brian

0

Aquí es una transliteración de su solución a Python.

En primer lugar, un generador que crea números aleatorios. Esto no es muy pitónico, pero es una buena coincidencia con su código de muestra.

>>> import random 
>>> def getRandomNumbers(max): 
...  while True: 
...    yield random.randrange(0,max) 

Aquí hay un bucle de cliente que recoge un conjunto de 5 valores distintos. Esto es, de nuevo, no la implementación más Pythonic.

>>> distinctSet= set() 
>>> for r in getRandomNumbers(100): 
...  distinctSet.add(r) 
...  if len(distinctSet) == 5: 
...    break 
... 
>>> distinctSet 
set([81, 66, 28, 53, 46]) 

No está claro por qué desea utilizar un generador de números aleatorios - que es una de las pocas cosas que es tan simple que un generador no simplifica la misma.

Una versión más Pythonic podría ser algo como:

distinctSet= set() 
while len(distinctSet) != 5: 
    distinctSet.add(random.randrange(0,100)) 

Si los requisitos son para generar 5 valores y encontrar distinta entre los 5, a continuación, algo así como

distinctSet= set([random.randrange(0,100) for i in range(5) ]) 
0

Tal vez esto se adapte a tu necesita y mira un poco más linqish:

from numpy import random,unique 

def GetRandomNumbers(total=5): 
    while True: 
     yield unique(random.random(total*2))[:total] 

randomGenerator = GetRandomNumbers() 

myRandomNumbers = randomGenerator.next() 
+0

No funciona con la biblioteca estándar. –

0

Aquí hay otra versión de python n, más acorde con la estructura de su código C#. No hay una función integrada para dar resultados distintos, así que he agregado una función para hacer esto.

import itertools, random 

def distinct(seq): 
    seen=set() 
    for item in seq: 
     if item not in seen: 
      seen.add(item) 
      yield item 

def getRandomNumbers(max): 
    while 1: 
     yield random.randint(0,max) 

for item in itertools.islice(distinct(getRandomNumbers(100)), 5): 
    print item 
1

En Ruby 1.9:

Array(0..100).sample(5) 
Cuestiones relacionadas