CIS 301. Probabilistic program termination and correctness.

Probabilistic program correctness and termination.


Consider the program below:

import java.math.*;
import java.util.*;

public class Prime_Gen {

  private static KeyboardReader keyboard = new KeyboardReader();

  public static void main(String[] args) {

    Random seed;
    BigInteger p,q,n,x;

    int l = keyboard.readInt("number of bits of prime to be
    generated: ");

    do {
        seed = new Random();
        p = new BigInteger(l, seed);
    } while (!p.isProbablePrime(50));

    System.out.println("prime p is " + p);
  }
}
This program produces a prime number p between 1 and 2**l, where l is the program input. (A number p is prime is 1 and p are its only divisors; e.g. 17 and 1999 are primes, but 45 is not as it equals 5*9.) The program depends on the method isProbablePrime that returns "false" if p is not prime. If it returns "true", then p is prime with probability at least 1 - 2**(-50). This method can therefore not be captured with our approach of partial or total correctness. But the probability bound, once proved, serves as a quantitative correctness criteria. Returning to the program in the class Prime_Gen above, we cannot fit it into the framework of our chapter 4 for two reasons: We consider a few runs of this program:


nunk% java Prime_Gen
number of bits of prime to be generated: 10
prime p is 521

number of bits of prime to be generated: 50
prime p is 779086272649297

number of bits of prime to be generated: 80
prime p is 294755374770727725802903

number of bits of prime to be generated: 512
prime p is 1582669745767556434598467840683850400995794483300909462432520859046879044255382564136703926567700854183918516779360732250925951294970703995515688910221737


We could implement the method isProbablePrime with the Miller-Rabin algorithm:


import java.math.*;
import java.util.*;

public class Miller_Rabin {

  private static KeyboardReader keyboard = new KeyboardReader();
  private static final BigInteger ZERO = new BigInteger("0");
  private static final BigInteger ONE = new BigInteger("1");
  private static final BigInteger TWO = new BigInteger("2");

  public static void main(String[] args) {
    String ns = keyboard.readString("number to be tested for primality: ");
    BigInteger n = new BigInteger(ns);
    int s = keyboard.readInt("number of tests: ");
    if (is_prime(n,s)) { System.out.println(n + " is prime with probability at least " + (1 - Math.pow(2,-s))); }
    else { System.out.println(n + " is definitely not prime"); }
  }

  public static boolean is_prime(BigInteger n, int s) {
    BigInteger a;
    for (int i = 1; i <= s; ++i) {
      Random seed = new Random();
      do { a = new BigInteger(n.bitLength()-1, seed);
      } while (a.compareTo(ZERO) <= 0 || a.compareTo(n) >= 0);
      if (witness(a,n)) { return false; }
    }
    return true;
  }

  public static boolean witness(BigInteger a, BigInteger n) {

    BigInteger b = n.subtract(ONE);
    int k = b.bitLength() - 1;
    BigInteger t = new BigInteger("1");
    BigInteger x;

    for (int i = k; i >= 0; --i) {

      x = t;
      t = t.modPow(TWO,n);

      if (t.equals(ONE) && !x.equals(ONE) && !x.equals(b)) return true;
      if (b.testBit(i)) t = t.multiply(a).mod(n);
    }

    if (!t.equals(ONE)) { return true; }
    return false;
  }

}



Michael Huth (huth@cis.ksu.edu)