Generating Random Large Primes
March 31, 2017
Here is our version of Pocklington’s test, which takes p, k and a and returns N if N is prime or #f
if N is composite:
(define (pocklington p k a) (and (odd? p) (prime? p) (< 1 k (* 2 (+ p 1))) (positive? (modulo k p)) (let ((n (+ (* 2 k p) 1))) (and (< 1 a n) (= (expm a (/ (- n 1) 2) n) (- n 1)) (= (gcd (+ (expm a k n) 1) n) 1) n)))) ; return n if found, else #f
> (pocklington 2333 2001 2) 9336667 > (pocklington 2333 2017 2) #f
Next we have function ratchet
that repeatedly tests combinations of k and a until it finds a prime. Instead of using the pocklington
function given above, we use a smaller internal function, because the full function does too much work:
(define (ratchet p lo hi verbose?) (define (pock p k a n) (and (= (expm a (/ (- n 1) 2) n) (- n 1)) (= (gcd (+ (expm a k n) 1) n) 1))) (let loop ((k (randint lo hi))) (let ((n (+ (* 2 k p) 1))) (cond ((pock p k 2 n) (when verbose? (display (list p k 2)) (newline)) n) ((pock p k 3 n) (when verbose? (display (list p k 3)) (newline)) n) (else (loop (randint lo hi)))))))
Now all that’s left is to work the ratchet. Note the computation of the range of k, which may not be 1 ≤ k < 2 × p on the last ratcheting:
(define (rand-prime lo hi . args) (let ((verbose? (if (pair? args) (car args) #f))) (if (< hi 10000) (fortune (drop-while (lambda (x) (< x lo)) (primes hi))) (let loop ((p (fortune (cdr (primes 10000))))) (if (<= lo p) p (let ((k-lo (ceiling (/ (- lo 1) 2 p))) (k-hi (floor (/ (- hi 1) 2 p)))) (loop (ratchet p (if (< (* 2 p) k-lo) 1 k-lo) (min (* 2 p) k-hi) verbose?))))))))
We are careful to handle small ranges properly, and we choose the initial prime randomly from the primes less than ten thousand. Here’s an example computation of a 50-digit prime:
> (rand-prime #e1e49 #e1e50 #t) (607 1022 2) (1240709 147645 2) (366368960611 227126727978 2) (166424366512554387349117 236422002865808466771968 3) (78692764113142983989384966585012763035290304513 618 2) 97264256443844728210879818699075775111618816378069
You can run the program at http://ideone.com/eVR8RA, where you will also see contributions from the Standard Prelude, a Sieve of Eratosthenes, a Miller-Rabin prime number checker, and the 147 random number generator from the previous exercise.
The function given above can fail if the range for k on the last ratchet is small and yields no primes. At an interactive prompt, simply interrupt the computation and try again. For a non-interactive program, it would be necessary to timeout and restart a runaway computation, but we won’t do that because there is no standard timer in Scheme.
By the way, Eric Weisstein’s entry on Pocklington’s Criterion at Wolfram’s MathWorld is wrong; it omits some of the conditions on a. I sent a message to notify MathWorld of the error.
Simplistic Python v3.x
Forgot to mention a possible result:
The trial division routine is completely out of order. Sorry! Here’s the corrected procedure:
Here is my favorite simple primality checker:
It uses a prime wheel, so it should be about twice as fast as yours.
@programmingpraxis: Awesome! Thanks you!
Also, I’vve found another bug in my source. The conditions check should read like this:
The GCD bit still bothers me. If k and N are large and of different size, this will take forever and possibly raise a MemoryError.
Your gcd is fine, and will execute quickly; Knuth guarantees it. Most Python programmers would write it like this:
@Millbrae: By the way, I blindly copied the prime checker from my wheel factorization program, and it does too much work. Here’s a corrected version:
And here is the corresponding function that factors integers, which is what I blindly copied from:
All these functions have appeared on Programming Praxis previously.
Thanks again, PP!
Yet there has to be some faster way to get GCD(a**k + 1, N) checked – if k and N are large. Python and PyPy take forever to finish for number 10**12 < n < 10**18.
[Quote from the description]Henry Pocklington’s Criterion, which dates to 1914, gives us a way to find such primes quickly.[/Quote]
… quickly … Quickly … QUICKLY
@Millbrae: If that gcd isn’t fast enough, you might want to look at this.
@Millbrae: If you do gcd(pow(a, k+1, N), N) there are no memory problems and it runs a lot faster.
Thanks a lot, Paul! I’ve been thinking abuot something like this:
Haven’t tested it though…
Latest code in Python:
Sample output:
I’ve tested N at https://www.alpertron.com.ar/ECM.HTM. Everything’s turned out fine.
Additionally, some numbers. If you manually set p = 4089548364052918515677660474680192167456016670586738083090737083455827, the above code will output…
N is then a 140-digit prime ;)
@Milbrae: It should not take six minutes to generate a random 35-digit prime. You seem to misunderstand the algorithm. Start with a small prime, and use it to generate a larger prime. Then recursively use the larger prime that you just generated to generate an even larger prime. And so on. Continue the “ratcheting” process until the end result is the desired size. My Scheme implementation generates a random 35-digit prime in 15ms, which is four orders of magnitude faster, and generates a 140-digit prime in a little bit less than a second:
Well, then maybe this will suffice…
Sample output:
@Milbrae: Much faster. But the result is 212 digits, not the requested 200. Look at my computation of k-lo and k-hi.
Or slightly changed, showing only the generated prime (and its length)
Sample output:
[…] studied Pocklington’s Criterion, which lets us quickly find large random primes, in a previous exercise. That algorithm generates a certified prime — a number that is proven to be prime — rather than […]