2016-03-06 7 views
5

C'è un numero intero lungo m = 38941629971148227236N. Voglio generare un numero e tra 1 < e < m, e controllare e se soddisfa questo requisito: gcd (e, m) = 1. Il mio metodo è quello di utilizzare (lunga (rand m)) per generare e in modo casuale, ho ricevuto un avvertimento:Come generare un numero lungo in modo casuale nel clojure

IllegalArgumentException Value out of range for long: 
1.7166121075068025E19 clojure.lang.RT.longCast (RT.java:1254) 

Il mio codice è:

(defn find-e [m] 
(loop [e (long (rand m))] 
    (if (= 1 (gcd e m)) e 
     (recur (long (rand m)))))) 

So che il risultato fuori portata per lungo tempo, ma non so c'è un modo per risolvere questo problema?

risposta

4

Il problema si trova con (long (rand m)) perché il valore casuale che si sta selezionando è spesso molto più grande di quello che può rientrare in un lungo. Vuoi fare un bigint non molto a lungo. Ecco un modo intorno ad esso:

(bigint (bigdec (rand 38941629971148227236N))) 

Nota che la selezione di numeri casuali in questo modo è davvero producendo un doppio che viene convertito in un bigdec che viene convertito in un bigit. In quanto tale, il dominio di possibili valori casuali è limitato. Usando un doppio come numero casuale di base significa che non verranno generati tutti i possibili bint. Se volete vero bigint selezione casuale, dare un'occhiata a this answer ... ma se non si cura troppo fino a quando si ottiene un bigint nella gamma di destra questo potrebbe funzionare per voi:

(defn find-e [m] 
    (loop [e (bigint (bigdec (rand m)))] 
    (if (= 1 (gcd e m)) 
     e 
     (recur (bigint (bigdec (rand m))))))) 
4

È potrebbe costruire utilizzando la conoscenza da the answer on generating random java.math.BigInteger di avere una soluzione più efficiente:

(defn random-bigint [limit] 
    (let [bits (.bitLength limit)] 
    (loop [result (BigInteger. bits (ThreadLocalRandom/current))] 
     (if (< result limit) 
     (bigint result) 
     (recur (BigInteger. bits (ThreadLocalRandom/current))))))) 

Poi il codice potrebbe riutilizzare questa funzione:

(defn find-e [m] 
    (loop [e (random-bigint m)] 
    (if (= 1 (gcd e m)) 
     e 
     (recur (random-bigint m))))) 

Questo approccio alla generat I numeri casuali e poi verificare se rientra nell'intervallo desiderato ha un inconveniente che se sei molto sfortunato il tuo ciclo richiederà molte iterazioni. Potresti estenderlo per avere un limite al numero di tentativi e fallire con un'eccezione quando superata.

+0

Davvero una buona risposta. Per i 'limiti' di grandi dimensioni, # di tentativi non dovrebbe essere un problema. – muhuk

+1

In realtà ho sbagliato, può essere un problema poiché ogni bit raddoppia lo spazio di ricerca. E il 'limite' più grande diventa, peggio diventa. – muhuk