2016-03-06 17 views
5

Có số nguyên dài m = 38941629971148227236N. Tôi muốn tạo một số điện tử giữa 1 < e < m và kiểm tra e nếu đáp ứng yêu cầu này: gcd (e, m) = 1. Phương pháp của tôi là sử dụng (dài (rand m)) để tạo ra điện tử một cách ngẫu nhiên, tôi nhận được một cảnh báo:Cách tạo số ngẫu nhiên dài trong clojure

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

Mã của tôi là:

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

tôi biết kết quả ra khỏi phạm vi trong thời gian dài, nhưng tôi không biết là có cách nào để giải quyết vấn đề này?

Trả lời

4

Vấn đề nằm ở (long (rand m)) vì giá trị ngẫu nhiên bạn chọn thường lớn hơn nhiều so với có thể vừa trong một thời gian dài. Bạn muốn làm cho một bigint không phải là một thời gian dài. Dưới đây là một trong những con đường xung quanh nó:

(bigint (bigdec (rand 38941629971148227236N))) 

Lưu ý rằng việc lựa chọn số ngẫu nhiên theo cách này thực sự tạo ra một đôi được chuyển đổi sang một bigdec được chuyển đổi sang một bigit. Như vậy, miền của các giá trị ngẫu nhiên có thể bị giới hạn. Sử dụng số double làm số ngẫu nhiên cơ sở có nghĩa là không phải tất cả các bigints có thể sẽ được tạo. Nếu bạn muốn thực sự bigint lựa chọn ngẫu nhiên, hãy nhìn vào this answer ... nhưng nếu bạn không quan tâm quá nhiều chừng nào bạn nhận được một bigint trong phạm vi quyền này có thể làm việc cho bạn:

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

Bạn có thể xây dựng nó bằng cách sử dụng kiến ​​thức từ the answer on generating random java.math.BigInteger để có một giải pháp hiệu quả hơn:

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

Sau đó, mã của bạn có thể tái sử dụng chức năng đó:

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

cách tiếp cận này để generat e số ngẫu nhiên và sau đó kiểm tra nếu nó trong phạm vi mong muốn có một nhược điểm rằng nếu bạn đang rất không may mắn vòng lặp của bạn sẽ mất rất nhiều lần lặp. Bạn có thể mở rộng nó để có một giới hạn về số lần thử lại và thất bại với một ngoại lệ khi nó vượt quá.

+0

Câu trả lời thực sự hay. Đối với 'giới hạn' lớn, số lần thử lại không phải là vấn đề. – muhuk

+1

Thực ra tôi đã sai, nó có thể là một vấn đề khi mọi thứ tăng gấp đôi không gian tìm kiếm. Và 'giới hạn' lớn hơn sẽ trở nên tồi tệ hơn. – muhuk