[Python] come ottenere numeri dispari casuali in un intervallo dato

Enrico Franchi enrico.franchi a gmail.com
Mar 5 Gen 2010 14:00:36 CET


On Jan 5, 2010, at 1:01 PM, Stefano Dal Pra wrote:

> Perche' quella e' pura e semplice sintassi python, quindi legittima.

Attenzione ad usare il termine "sintassi" a sproposito.
Tutta e' "sintassi" python, se non sarebbe rifiutata dal compilatore
e saremmo in presenza di programmi non ben formati.

> Poi per motivi di efficienza:
> l'interprete, se non e' scemo, dovrebbe tradurre il | 1 con una
> singola istruzione macchina (or bit a bit). Se  devi generare un
> dispari e via, ok. Se ne devi fare moltissimi e piu' rapidamente
> possibile, il discorso cambia.

Ho smesso nel credere alle misure spannometriche.

1. assumi che la macchina virtuale Python *sia* scema
2. in generale in Python e' l'astrazione a pagare, non il dettaglio a basso livello.

Quindi, se voglio dire che "una cosa e' piu' veloce di un'altra" la misuro.
Finche' non mi sono reso conto che *ho* un problema di performance, la cosa non la valuto neppure.
Se la valuto, la valuto in modo oggettivo, controllato, misurato. Cerco il collo di bottiglia
e non intervengo "a caso". Trovato il collo di bottiglia, si valuta se lo speedup atteso
sia comparabile con il peggioramento del codice oppure se non sia possibile un intervento
architetturale piu' efficiente ancora.

A questo punto si procede, misurando attentamente i vantaggi in termini di performance.

In particolare, nel nostro specifico caso, salta fuori che il metodo suggerito da Marco
sia *piu' veloce* di quello suggerito da te, che asserisci essere piu' veloce non mi
e' chiaro sulla base di cosa.

% /usr/bin/python2.6 oddrandom.py
baseline : 0.0180490016937
bit manip: 2.38537406921
randrange: 2.28163599968


Certo, non *molto* piu' veloce. Su un milione di esecuzioni circa un decimo di secondo.
Fossero stati i risultati invertiti, non mi sarei preoccupato ugualmente.

Quindi, di fatto e' il classico esempio di ottimizzazione prematura. Anzi, un'ottimizzazione
che *peggiora* le performance (e la leggibilita'). 

Tra l'altro, quando si fanno simulazioni numeriche, spesso si accetta di rifare piu' volte
gli esperimenti (quindi "di fatto" peggiorando il tempo di esecuzione di un fattore lineare)
pur di provarli con diversi generatori di numeri per mettersi al riparo da problemi di 
non troppo pseudo-casualita'. Quindi, figuriamoci. :)


> Per non giocare con i bit uno puo' fare randint(0,4) * 2 + 1 e ottiene
> i dispari tra 1 e 9, tanto equiprobabili quanto randint(0,4).
> Nel caso di randint(0,9) | 1 , ogni risultato ha esattamente due
> possibilita' di uscire: era il pari immediatamente superiore a cui si
> e' aggiunto 1, oppure era direttamente dispari, e non e' cambiato.

No, hai *perso* informazione. Eventualmente una quantita' completamente trascurabile,
possibilmente una quantita' rilevante. Con la semplice assunzione che ognuno dei
32 bit sia responsabile per 1/32 della qi, il primo modo fa una trasformazione lineare
(e quindi di fatto anche la qi rimane invariata). Con il tuo trucco invece perdi 1/32 della
qi. Non tanto, ma nemmeno poco. Dopotutto, visto che si puo' evitare il tutto.

Questo *a monte* del fatto che stai scegliendo su un range ristretto di numeri, eh.


Maggiori informazioni sulla lista Python