Pregunta

Ayer pregunté este pregunta 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 tener un estilo extremadamente imperativo.

Como no vi nada que realmente me gustara, escribí 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);
            }
        }

¿Puedes 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 sea necesario generar una matriz mucho más grande que N para luego poder eliminar los duplicados y recortarlos a N.

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

Editar:
¿Por qué todos los votos negativos?

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

Disculpa:
Me han dicho que esta publicación parecía bastante esnob.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 simplemente aprender cómo hacer esto (con ciertas restricciones) en Ruby.Lo siento si parecí un idiota.

¿Fue útil?

Solución

En Rubí:

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

Actualizar:Aquí hay una forma ligeramente diferente:a = (0...100).entradas.sort_by{rand}[0...5]

EDITAR:

y en Ruby 1.9 puedes hacer esto:

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

Otros consejos

>>> 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 xrange El objeto genera valores según lo solicitado, por lo que no se utiliza memoria para valores que no se muestrean.

Mmm...¿Qué tal (Python)?

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

Renunciaré a las soluciones más simples usando el módulo 'aleatorio' ya que supongo que eso no es realmente lo que buscas.Esto es lo que creo que estás buscando 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 mostrarte cómo funciona:

>>> list(getUniqueRandomNumbers(10, 100))
[81, 57, 98, 47, 93, 31, 29, 24, 97, 10]

Aquí hay otra solución de Ruby:

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

Creo que, con su declaración LINQ, Distinct eliminará los duplicados después de que ya se hayan tomado 5, por lo que no se garantiza que recuperará 5.Aunque alguien puede corregirme si me equivoco.

EDITAR :Ok, solo por diversión, uno más corto y rápido (y todavía 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é, las frases ingeniosas deberían dejarse en manos de los amantes de Perl, pero creo que esta es bastante poderosa, ¿no?

Mensaje antiguo aquí:

¡Dios mío, qué complicado es todo eso!Seamos pitónicos:

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

Disfrutar

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

Para evitar el problema que tuvimos al eliminar duplicados después de generar la lista, lo que resulta en muy pocos datos, puedes elegir otra forma:

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]

En Rubí 1.9:

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

Python con Python numérico:

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

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

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]

Bueno, primero reescribes LINQ en Python.Entonces tu solución es de 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 simples anteriores en un módulo llamado LINQ.py, puedes impresionar a tus amigos.

(Descargo de responsabilidad:por supuesto, esto no es de hecho reescribiendo 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 una sintaxis nueva.Sin embargo, la parte realmente avanzada de LINQ es la generación automática de SQL, de modo que cuando se consulta una base de datos, es la base de datos la que implementa Distinct() en lugar del lado del cliente).

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

Primero, un generador que crea números aleatorios.Esto no es muy Pythonic, pero coincide bien 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 recopila un conjunto de 5 valores distintos.Esta, nuevamente, no es la implementación más pitónica.

>>> 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; esa es una de las pocas cosas que es tan simple que un generador no la simplifica.

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 generar 5 valores y encontrar distintos entre esos 5, entonces algo como

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

Tal vez esto se adapte a sus necesidades y se vea 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()

Aquí hay otra versión de Python, que se asemeja más a la estructura de su código C#.No hay una función incorporada para dar resultados distintos, por lo que agregué 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

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

Aquí hay 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 quizás en realidad estés buscando 5 números aleatorios distintos entre 0 y 100.En ese caso:

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

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

Ahora bien, este podría violar su sentido de "no demasiados bucles", pero presumiblemente Take y Distinct simplemente le están ocultando el bucle.Sería bastante fácil agregar métodos a Enumerable para ocultar el bucle while.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top