Multiple Polynomial Quadratic Sieve

June 21, 2013

We’ll only hit the highlights here; see the next page for the full code.

We begin with the sieve function, which changes from the prior versions. We are back to a straight sieve, rather than a segmented sieve, both for clarity of code and also because the sieve is very much smaller with mpqs than with the basic qs. The primary difference is in the way that a relation is built, which we packaged in a local function:

(define (sieve n fb ts ls e f m a b q-inv) ; => (values rels parts)
  (define (make-rel x ys)
    (cons (modulo (* (+ (* a x) b) q-inv) n) ys))
  ; a relation, whether full or partial, has (ax+b)/q in its car and a
  ; list of factors of g(x)=ax^2+2bx+c, in descending order, in its cdr;
  ; a large prime, if it exists, is at the cadr of the relation
  (let* ((c (/ (- (* b b) n) a)) (rels (list)) (parts (list))
         (sieve (make-vector (+ m m 1) (+ e e))))
    (do ((fb (cdr fb) (cdr fb)) (ts ts (cdr ts)) (ls ls (cdr ls))
         (invs (map (lambda (f) (inverse a f)) (cdr fb)) (cdr invs)))
        ((null? fb))
      (let ((f (car fb)) (t (car ts)) (l (car ls)) (v (car invs)))
        (do ((i (modulo (* v (- t b)) f) (+ i f))) ((<= (+ m m 1) i))
          (vector-set! sieve i (+ (vector-ref sieve i) l)))
        (do ((i (modulo (* v (- (- t) b)) f) (+ i f))) ((<= (+ m m 1) i))
          (vector-set! sieve i (+ (vector-ref sieve i) l)))))
    (do ((i 0 (+ i 1))) ((= (+ m m 1) i))
      (let* ((x (- i m)) (g (+ (* a x x) (* 2 b x) c)))
        (if (< (ilog 2 g) (vector-ref sieve i))
          (let* ((ys (smooth g fb)) (rel (make-rel x ys)))
            (if (pair? ys)
              (if (<= (car ys) f)
                  (set! rels (cons rel rels))
                  (set! parts (cons rel parts))))))))
    (values rels parts)))

If you look closely, you will see the calculation of the modular inverses of all the primes in the factor base, repeated for each polynomial. This calculation is a major part of the cost of sieving; in a future exercise we will see a way to nearly eliminate that cost.

The other big change is the qs function, which now has to compute q, a, b and c. Variable q counts up from the square root of n.

(define (qs n f m)
  (define (make-odd q) (if (odd? q) q (+ q 1)))
  (call-with-values
    (lambda () (factor-base n f))
    (lambda (fb ts ls e len-fb)
      (when verbose? (display "Factor base of ")
        (display len-fb) (display " primes.") (newline))
      (let loop ((q (make-odd (isqrt (quotient (isqrt (+ n n)) m)))) (rels (list)) (parts (list)))
        (if (not (and (prime? q) (= (jacobi n q) 1))) (loop (+ q 2) rels parts)
          (let* ((a (* q q)) (b (apply min (lift-root n q))) (q-inv (inverse q n)))
            (when verbose? (display "q=") (display q) (display ": "))
            (call-with-values
              (lambda () (sieve n fb ts ls e f m a b q-inv))
              (lambda (rs ps)
                (let* ((rels (append rs rels)) (parts (append ps parts))
                       (matches (match parts)) (len-rels (length rels))
                       (len-parts (length parts)) (len-matches (length matches)))
                  (when verbose? (display len-rels) (display " smooths, ")
                    (display len-parts) (display " partials, ")
                    (display len-matches) (display " matches.") (newline))
                  (if (< (+ len-rels len-matches -10) len-fb) (loop (+ q 2) rels parts)
                    (let ((fact (solve n f fb (append rels matches))))
                      (if fact fact (loop (+ q 2) rels parts)))))))))))))

Here is an example:

> (qs 294729242679158229936006281 1500 100000)
Factor base of 122 primes.
q=15601: 11 smooths, 504 partials, 5 matches.
q=15607: 24 smooths, 1006 partials, 30 matches.
q=15619: 34 smooths, 1477 partials, 55 matches.
q=15629: 46 smooths, 1942 partials, 97 matches.
2971215073

You can clearly see the power of the multiple polynomial quadratic sieve as compared to the basic quadratic sieve. In the previous exercise, we used f = 2000; here, we reduce f to 1500. We also reduced m from 3000000 to 100000. Quite an improvement.

You can see the entire program assembled at the next page, or run the program at http://programmingpraxis.codepad.org/j3nstX1Y.

Advertisement

Pages: 1 2 3

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: