Domanda

Ieri ho chiesto a questa domanda e non ho mai avuto una risposta di cui ero davvero contento. Mi piacerebbe davvero sapere come generare un elenco di N numeri casuali univoci utilizzando un linguaggio funzionale come Ruby senza dover essere estremamente imperativo nello stile.

Dato che non ho visto nulla che mi piacesse davvero, ho scritto la soluzione che stavo cercando in 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);
            }
        }

Puoi tradurre il mio LINQ in Ruby? Pitone? Qualche altro linguaggio di programmazione funzionale?

Nota: prova a non usare troppi loop e condizionali, altrimenti la soluzione è banale. Inoltre, preferirei vedere una soluzione in cui non è necessario generare un array molto più grande di N in modo da poter semplicemente rimuovere i duplicati e tagliarlo a N.

So di essere pignolo, ma mi piacerebbe davvero vedere alcune soluzioni eleganti a questo problema. Grazie!

Modifica
Perché tutti i downvotes?

Inizialmente il mio esempio di codice aveva Distinct () dopo Take () che, come molti hanno sottolineato, poteva lasciarmi con un elenco vuoto. Ho cambiato l'ordine in cui questi metodi sono chiamati per riflettere ciò che intendevo in primo luogo.

Apologia:
Mi è stato detto che questo post è risultato piuttosto snob. Non stavo cercando di insinuare che LINQ fosse migliore di Ruby / Python; o che la mia soluzione è molto migliore di quella di tutti gli altri. Il mio intento è solo quello di imparare a farlo (con alcuni vincoli) in Ruby. Mi dispiace se mi sono imbattuto in un coglione.

È stato utile?

Soluzione

In Ruby:

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

Aggiorna : ecco un modo leggermente diverso:     a = (0 ... 100) .entries.sort_by {rand} [0 ... 5]

Modifica

e in Ruby 1.9 puoi farlo:

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

Altri suggerimenti

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

Questo dovrebbe produrre 5 valori univoci nell'intervallo 0 - 99 . L'oggetto xrange genera valori come richiesto, quindi non viene utilizzata memoria per i valori che non sono campionati.

Hmm ... Che ne dici di (Python):

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

Rinuncerò alle soluzioni più semplici usando il modulo 'random' dato che lo prendo non è proprio quello che stai cercando. Ecco cosa penso che tu stia cercando in 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
... 
>>>

Per mostrarti come funziona:

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

Ecco un'altra soluzione di Ruby:

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

Penso che, con la tua dichiarazione LINQ, Distinct rimuoverà i duplicati dopo che sono già stati acquisiti 5, quindi non sei sicuro di recuperarne 5. Qualcuno può correggermi se sbaglio, però.

EDIT: Ok, solo per divertimento, uno più breve e veloce (e ancora usando iteratori).

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 so, una linea dovrebbe essere lasciata agli amanti del Perl, ma penso che questa sia abbastanza potente, vero?

Vecchio messaggio qui:

Mio Dio, quanto è complicato tutto ciò! Siamo 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])

Godetevi

EDIT: come notato dai commentatori, questa è una traduzione esatta del codice della domanda.

Per evitare il problema che abbiamo riscontrato rimuovendo i duplicati dopo aver generato l'elenco, risultando in una quantità insufficiente di dati, puoi scegliere un altro modo:

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]

In Ruby 1.9:

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

Python con Numeric Python:

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

Voilà! Sicuro che potresti fare qualcosa di simile in uno stile di programmazione funzionale ma ... perché?

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]

Bene, prima riscrivi LINQ in Python. Quindi la tua soluzione è di tipo 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 metti tutti i metodi semplici sopra in un modulo chiamato LINQ.py, puoi stupire i tuoi amici.

(Dichiarazione di non responsabilità: ovviamente, questo non è in realtà riscrivere LINQ in Python. La gente ha l'idea sbagliata che LINQ sia solo un mucchio di metodi di estensione banali e alcune nuove sintassi. La parte veramente avanzata di LINQ , tuttavia, è la generazione automatica di SQL in modo che quando si esegue una query su un database, è il database che implementa Distinct () anziché il lato client.)

Ecco una traslitterazione dalla tua soluzione a Python.

Innanzitutto, un generatore che crea numeri casuali. Questo non è molto Pythonic, ma è una buona corrispondenza con il tuo codice di esempio.

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

Ecco un ciclo client che raccoglie un set di 5 valori distinti. Questa non è, di nuovo, l'implementazione più Pythonic.

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

Non è chiaro il motivo per cui si desidera utilizzare un generatore per numeri casuali: questa è una delle poche cose che è così semplice che un generatore non lo semplifica.

Una versione più Pythonic potrebbe essere qualcosa del tipo:

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

Se i requisiti sono generare 5 valori e trovarli distinti tra questi 5, allora qualcosa di simile

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

Forse questo soddisferà le tue esigenze e sembrerà un po 'più lineare:

from numpy import random,unique

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

randomGenerator = GetRandomNumbers()

myRandomNumbers = randomGenerator.next()

Ecco un'altra versione di Python, che corrisponde maggiormente alla struttura del tuo codice C #. Non esiste un metodo integrato per fornire risultati distinti, quindi ho aggiunto una funzione per farlo.

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

Non riesco davvero a leggere il tuo LINQ, ma penso che stai cercando di ottenere 5 numeri casuali fino a 100 e quindi rimuovere i duplicati.

Ecco una soluzione per questo:

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

Ma forse stai effettivamente cercando 5 numeri casuali distinti tra 0 e 100. Nel qual caso:

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

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

Ora, questo potrebbe violare il tuo senso di "non troppi loop", " ma presumibilmente Take e Distinct ti stanno solo nascondendo il looping. Sarebbe abbastanza semplice aggiungere metodi a Enumerable per nascondere il ciclo while.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top