Pergunta

Ontem, pedi esta questão e nunca realmente tem uma resposta que eu estava muito feliz com. Eu realmente gostaria de saber como gerar uma lista de N números aleatórios exclusivos usando uma linguagem funcional como Ruby sem ter que ser extremamente imperativo em grande estilo.

Desde que eu não vi nada que eu realmente gostei, eu escrevi a solução que eu estava procurando em 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);
            }
        }

Pode traduzir meu LINQ para Ruby? Pitão? Qualquer outra linguagem de programação funcional?

Nota: Por favor, tente não usar muitos loops e condicionais - caso contrário, a solução é trivial. Além disso, eu prefiro ver uma solução onde você não tem para gerar um conjunto muito maior do que N para que você possa, em seguida, basta remover as duplicatas e prepará-la para baixo para N.

Eu sei que estou sendo exigente, mas eu realmente gosto de ver algumas soluções elegantes para este problema. Obrigado!

Editar:
Por todos os downvotes?

Originalmente o meu exemplo de código teve a nítida () após a tomada () que, como muitos por pontas, poderia me deixar com uma lista vazia. Eu mudei a ordem em que esses métodos são chamados para refletir o que eu quis dizer, em primeiro lugar.

Apology:
Eu tenho dito este post veio em como em vez esnobe. Eu não estava tentando dar a entender que LINQ é melhor do que Ruby / Python; ou que a minha solução é muito melhor do que todos os outros. Minha intenção é apenas para aprender como fazer isso (com algumas restrições) em Ruby. Me desculpe se eu vim transversalmente como um idiota.

Foi útil?

Solução

Em Ruby:

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

Atualizar : Aqui está uma maneira um pouco diferente: a = (0 ... 100) .entries.sort_by {rand} [0 ... 5]

EDIT:

e no Ruby 1.9 você pode fazer isso:

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

Outras dicas

>>> import random
>>> print random.sample(xrange(100), 5)
[61, 54, 91, 72, 85]

Este deve render 5 valores originais no 0 — 99 gama. O objeto xrange gera valores, conforme solicitado por isso nenhuma memória é usado para valores que não são amostrados.

Hmm ... Como sobre (Python):

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

Vou renunciar as soluções mais simples usando o módulo 'aleatória' desde que eu levá-la de que não é realmente o que você está depois. Aqui está o que eu acho que você está procurando em 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 como funciona:

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

Aqui está outra solução Ruby:

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

Eu acho que, com a sua declaração de LINQ, a nítida irá remover duplicatas após 5 já foram tomadas, para que você não está garantido para obter 5 volta. Alguém pode me corrija se eu estiver errado, porém.

Edição:. Ok, só por diversão, um mais curto e mais rápido um (e continua 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]

Sim, eu sei, one-liners deve ser deixada para os amantes do Perl, mas eu acho que este é bastante poderoso, não é?

mensagem de Old aqui:

Meu deus, o quão complicado é tudo isso! Vamos ser pythônico:

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

Aproveite

EDIT:. Como comentaristas notaram, esta é uma tradução exata do código da pergunta

Para evitar o problema que temos através da remoção de duplicatas depois de gerar a lista, resultando em muito poucos dados, você pode escolher outra 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]

Em Ruby 1.9:

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

Python com Numeric Python:

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

Voilà! Claro que você poderia fazer algo semelhante em um estilo de programação funcional, mas ... 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]

Bem, primeiro você reescrever LINQ em Python. Então a solução é um one-liner:)

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

Se você colocar todos os métodos simples acima em um módulo chamado LINQ.py, você pode impressionar seus amigos.

(Disclaimer: é claro, isso não é realmente reescrevendo LINQ em Python As pessoas têm a idéia errada de que LINQ é apenas um monte de métodos de extensão triviais e alguns nova sintaxe A parte realmente avançado de LINQ.. , no entanto, é a geração de SQL automática para que quando você está consultando um banco de dados, é o banco de dados que implementa distintas () em vez do lado do cliente.)

Aqui está uma transliteração da sua solução para Python.

Em primeiro lugar, um gerador que cria números aleatórios. Isso não é muito Pythonic, mas é um bom jogo com o seu código de exemplo.

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

Aqui está um loop cliente que recolhe um conjunto de 5 valores distintos. Este é - de novo -. Não a implementação mais Pythonic

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

Não é claro por que você quer usar um gerador de números aleatórios - que é uma das poucas coisas que é tão simples que um gerador não simplificá-lo.

Uma versão mais Pythonic poderia ser algo como:

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

Se os requisitos são para gerar 5 valores e encontrar distinta entre aqueles 5, em seguida, algo como

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

Talvez isto irá atender as suas necessidades e olhar um pouco mais linqish:

from numpy import random,unique

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

randomGenerator = GetRandomNumbers()

myRandomNumbers = randomGenerator.next()

Aqui está outra versão python, mais de perto combinando a estrutura de seu código C #. Não há um builtin para dar resultados distintos, então eu adicionei uma função para fazer isso.

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

Eu realmente não posso ler sua LINQ, mas eu acho que você está tentando obter 5 números aleatórios até 100 e, em seguida, remover duplicatas.

Aqui está uma solução para isso:

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

Mas talvez você está realmente à procura de 5 números aleatórias distintas entre 0 e 100. Em que caso:

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

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

Agora, este pode violar o seu sentido de "não muitos loops," mas provavelmente Tome e distinta apenas estão escondendo o looping de você. Seria fácil o suficiente para apenas adicionar métodos para Enumerable para esconder o loop while.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top