In: Advanced Math
factorize the integer 2896753 by using the Quadratic Sieve method
Code Language: Java
 import java.util.Scanner; import java.util.ArrayList; import java.util.HashSet; /* **This program will run a quadratic sieve to **factor some numbers** 
*/ public class QuadraticSieve { //This is the range the matrix runs on. //50,000 works great because it works for all numbers //in the range of long without being too large private static final long SIEVERANGE = 50000; //This is a private class //to be used as a touple for //saving an ArrayList and it's integer index //and some other things private static class Pair<X, Y> { private final X x; private final Y y; public Pair(X x, Y y) { this.x = x; this.y = y; } public X getX(){ return x;} public Y getY(){ return y;} } public static void main(String[] args) { //Grab number N to be factored long n;         //Grab the factor base long factorbase; //take from command line                //if no factobase is given, assume 1000         if(args.length == 1)    {               n = Long.parseLong(args[0]);            factorbase = 1000;      } else if(args.length == 2) { n = Long.parseLong(args[0]); factorbase = Long.parseLong(args[1]); } //else take from scanner else { Scanner input = new Scanner(System.in); System.out.println("Enter an N: "); n = input.nextLong();     System.out.println("Enter a factorbase: "); factorbase = input.nextLong(); } //Find R long R = (long)Math.sqrt(n); //generate a list of primes up to factorbase ArrayList<Integer> primes = eratosthenesSieve(factorbase); //for(int i = 0; i < primes.size(); i++) //System.out.println(primes.get(i) + " "); //generate a list that satisfies the eqn ArrayList<Long> bSmooth = findSmoothness(n); ArrayList<Long> residues = calcResiduals(primes, bSmooth); //Refactor and Gauss ArrayList<Pair<ArrayList<Long>, Integer>> refactoredResidual       = refactor(residues, bSmooth, primes); //reduce the matrix mod 2 ArrayList<Pair<ArrayList<Integer>, Integer>> reducedResidual         = reduceModTwo(refactoredResidual); //print reduced residual matrix //System.out.println("Reduced matrix is: "); //int i = 0; //for(Pair<ArrayList<Integer>,Integer> pair : reducedResidual) //{ //System.out.print(i + " "); //System.out.print("["); //for(long j : pair.getX()) //System.out.print(j + ", "); //System.out.println("]"); //i++; //} //perform Gauss-Jordan elimination on the matrix //build list of hits I.E. (Index, r1, r2, r3, etc) ArrayList<ArrayList<Integer>> gaussHits = gauss(reducedResidual); //rebuild equation HashSet<Long> factors = rebuildEquation(gaussHits, n); System.out.print("The factors are: " ); for(long factor : factors) System.out.print(factor + ", "); System.out.println(); } /** * This method will perform an eratostheness Sieve to calculate all primes * under the input factobase * * @param size This is the size to create the array of primes * @return     An ArrayList of primes under size */ public static ArrayList<Integer> eratosthenesSieve(long size) { ArrayList<Integer> primes = new ArrayList<Integer>(); //populate a list with i = 2 to n for(int i = 2; i < size; i++) primes.add(i); //set p equal to 2? int p = 2; //loop while(Math.pow(p, 2) < size) { for(int i = primes.indexOf(p) + 1; i < primes.size(); i++) { ////System.out.println(primes.get(i) +": divided by " + p); if(primes.get(i) % p == 0) { primes.remove(i); } } p++; } primes.add(0, -1); return primes; } /** *This method will find the smooth numbers between the range *specified * *@param numToFactor       the input number we are factoring *@return      An ArrayList of longs representing the smooth values found */ public static ArrayList<Long> findSmoothness(long numToFactor) { //Offset = R. Change later ArrayList<Long> Qs = new ArrayList<Long>(); //System.out.println("Root is " + Math.sqrt(numToFactor)                                        //+ "\tindex\t" + "(r+n)^2\t" + "(r+n)^2 - N)"); for(long i = -SIEVERANGE; i <= SIEVERANGE; i++) { //store log of Q possibly long Q = ((long)(Math.pow((Math.sqrt(numToFactor) + i), 2))); long save = Q - numToFactor; Qs.add(save); //System.out.println(i +"\t" + (Qs.size() - 1) + "\t" + Q + "\t" + save); } return Qs; } /** *This method will determine which numbers are smooth by repeatedly dividing *by primes in the factor base to obtain residuals of each number. * *@param smoothlist An ArrayList of smooth *@return        An ArrayList of longs representing the newly calculated residuals */ public static ArrayList<Long> calcResiduals(ArrayList<Integer> primes,                                                                                          ArrayList<Long> smoothlist) { //ArrayList<Pair<Integer, Integer>> residuals  //= new ArrayList<Pair<Integer,Integer>>(); ArrayList<Long> copy = new ArrayList<Long>(); for(long i : smoothlist) copy.add(i); int start; for(int i : primes) { if(i == -1) continue; //System.out.println("Sieve with " + i); for(int j = 0; j < copy.size(); j++) { if(copy.get(j) % i == 0)      { start = j; int index = j; do{ long temp = copy.get(j); while(temp != 0 && temp % i == 0) { //System.out.println("Temp: " + temp + "\ti: " + i                                                                  //+ "temp % i = " + (temp%i)); //System.out.println("Divide\t" + i + "\t "                                                              //+(j-(smoothlist.size()/2)) + "\t "                                                            //+ temp + "\t" + (temp / i)); temp = temp / i; } copy.set(j, temp); j = (j + i) % copy.size(); } while(j != start);   } } //System.out.print("Array ["); /* for(long num : copy) { //System.out.print(num + ", "); } */ //System.out.println("]"); } //Take abs value of the array for(int i = 0; i < copy.size(); i++) copy.set(i, Math.abs(copy.get(i))); return copy; } /** *This method will create a matrix of the prime factorization of numbers *that are smooth over the factorbase. *It stores the exponents of each prime factor of each number. * *@param Residues      An ArrayList of reisdues calculated from *                      calcResidues *@param original   the original ArrayList of the smooth numbers *@param primes     the list of primes up to the factorbase  *@return       an ArrayList of pairs containing the rows and their *           corresponding integer index in case the rows get *              rearranged later */ public static ArrayList<Pair<ArrayList<Long>,Integer>>                            refactor(ArrayList<Long> residues, ArrayList<Long> original,                                                 ArrayList<Integer> primes) { //initialize arraylist and fill with zero arryalists ArrayList<Pair<ArrayList<Long>, Integer>> exponents  = new ArrayList<Pair<ArrayList<Long>, Integer>>(); //cheating long zero = 0; for(int i = 0; i < residues.size(); i++) { if(residues.get(i) == 1) { ArrayList<Long> exponent = new ArrayList<Long>(); for(int index = 0; index < primes.size(); index++) exponent.add(zero);                 //System.out.println("Smooth\t" + (i - (original.size()/2))                                              //+ "\t" + i + "\t" + original.get(i)); long temp = original.get(i);    if(temp < 0)         {       temp = temp * -1;       exponent.set(0,new Long(1));    } //pIndex set to 1 to skip the "prime" -1 for(int pIndex = 1; pIndex < primes.size(); pIndex++) { while(temp % primes.get(pIndex) == 0)     {       //System.out.println("DIVIDE\t" + pIndex +"\t" + primes.get(pIndex) + "\t" + temp);     temp = temp / primes.get(pIndex);       exponent.set(pIndex, (exponent.get(pIndex))+1);         }               } exponents.add(new Pair<ArrayList<Long>, Integer>(exponent, i)); } //System.out.print("["); /* if(exponents.size() > 0) { for(long whatever : exponents.get(exponents.size() -1).getX()) { //System.out.print(whatever +", "); } //System.out.println("]"); } */ } return exponents; } /** *This method reduces the prime factorization matrix mod 2 * (so the matrix only stores the parity of the exponents). * *@param      An ArrayList of Pairs representing the matrix and the indices *@return  An ArrayList of Pairs of ArrayLists and their corresponding *           indices (in case of the loss of the index from reduction */ public static ArrayList<Pair<ArrayList<Integer>, Integer>> reduceModTwo                                                                                   (ArrayList<Pair<ArrayList<Long>,                                                                                     Integer>> matrix) { ArrayList<Pair<ArrayList<Integer>, Integer>> modTwo        = new ArrayList<Pair<ArrayList<Integer>, Integer>>(); for(int i = 0; i < matrix.size(); i++) { //System.out.print("Row: " + i + "\t["); for(int j = 0; j < matrix.get(i).getX().size(); j++) { //System.out.print(matrix.get(i).getX().get(j) + ", "); matrix.get(i).getX().set(j, matrix.get(i).getX().get(j) % 2); } //System.out.print("]\nReduced\t["); //for(long temp : matrix.get(i).getX()) //System.out.print(temp + ", "); //System.out.println("]"); } for(Pair<ArrayList<Long>, Integer> pair : matrix) { ArrayList<Integer> ints = new ArrayList<Integer>(); for(long number : pair.getX()) ints.add((int)number); modTwo.add(new Pair<ArrayList<Integer>, Integer>(ints, pair.getY())); } return modTwo; } /** *This method uses Gaussian elimination to return a list *of row combinations that sum to zero. These represent *products that are square and can be used to rebuild the equation x^2 = y^2. * *@param array    The array to perform gauss elimination on *@returns     An ArrayList of ArrayLists representing the row reduction *                     operations performed on the matrix */ public static ArrayList<ArrayList<Integer>> gauss                             (ArrayList<Pair<ArrayList<Integer>,Integer>> array) { ArrayList<Pair<ArrayList<Integer>, ArrayList<Integer>>> reducedMatrix   = new ArrayList<Pair<ArrayList<Integer>, ArrayList<Integer>>>(); //initialize ArrayList for(int i = 0; i < array.size(); i++) { ArrayList<Integer> rowReductions = new ArrayList<Integer>(); //first value in the index is the original index of said row rowReductions.add(array.get(i).getY()); reducedMatrix.add(new Pair<ArrayList<Integer>,                                                ArrayList<Integer>>(array.get(i).getX(),                                                                                       rowReductions)); } int j = 0; for(int i = 0; i < reducedMatrix.size(); i++) { if(j >= reducedMatrix.get(i).getX().size()) break; if(reducedMatrix.get(i).getX().get(j) == 0) { //index of the next row that has a 1 to swap for the zero int oneRow; for(oneRow = i + 1; oneRow < reducedMatrix.size(); oneRow++) { //break at 1 to preserve oneRow for swapping if(reducedMatrix.get(oneRow).getX().get(j) == 1) break; } //if it's not equal to the size, we did find a 1 if(oneRow < reducedMatrix.size()) { //one found, swap rows Pair<ArrayList<Integer>, ArrayList<Integer>> temp           = reducedMatrix.get(i); reducedMatrix.set(i, reducedMatrix.get(oneRow)); reducedMatrix.set(oneRow, temp); } //else move to the next column else { i--; j++; continue; } } //else we have 1, zero out the column for(int row = i + 1; row < reducedMatrix.size(); row++) { if(reducedMatrix.get(row).getX().get(j) == 0) continue; reducedMatrix.get(row).getX().set(j,0); reducedMatrix.get(row).getY().add(i); } j++; } ArrayList<ArrayList<Integer>> zeroSums = new ArrayList<ArrayList<Integer>>(); for(int i = 0; i < reducedMatrix.size(); i++) { for(j = 0; j < reducedMatrix.get(i).getX().size(); j++) { if(reducedMatrix.get(i).getX().get(j) != 0) { //break out of the loop by ending condition j = reducedMatrix.get(i).getX().size(); continue; } } zeroSums.add(reducedMatrix.get(i).getY()); } return zeroSums; } /** * This method will rebuild the equation x^2 = y^2 * *@param bigN         The number to be factored *@param gaussHits     The matrix of row reduce operations from gauss *@return                 A HashSet containing the factors of N */ public static HashSet<Long> rebuildEquation                              (ArrayList<ArrayList<Integer>> gaussHits, long bigN) { long R = ((long)(Math.sqrt(bigN))) / SIEVERANGE; HashSet<Long> factors = new HashSet<Long>(); for(ArrayList<Integer> hit : gaussHits) { int lhs = 1; int rhs = 1; for(int num = 1; num < hit.size(); num++) { lhs *= Math.pow(R + (hit.get(num)- R), 2); rhs *= Math.pow(R + (hit.get(num) - R), 2) - bigN; } long x = (long)(Math.sqrt(Math.abs(lhs))); long y = (long)(Math.sqrt(Math.abs(rhs))); if(x == y) continue; long factor1 = (gcd(bigN, (x+y))); long factor2 = (gcd(bigN, (x-y))); ////System.out.print("Hit\t["); //for(long number : hit) // //System.out.print(number + ", "); //System.out.println("]\n LHS: " + lhs + ".\t RHS: " + rhs); //System.out.println("X: " + x + "\tY: " + y); //System.out.println("gcd(" + bigN +", " + (x+y) +": " + factor1 + //"\ngcd(" + bigN + ", " + (x-y) + ": " + factor2); factors.add(Math.abs(factor1)); factors.add(Math.abs(factor2)); //check if the factors make other factors! if(bigN % factor1 == 0) factors.add(Math.abs(bigN/factor1)); if(bigN % factor2 == 0) factors.add(Math.abs(bigN/factor2)); if(factors.size() > 1) factors = completeFactors(factors); } return factors; } /*** *This method divides all factors by other factors to ensure *that all prime factors are listed in the output. * *@param factors    This is the list of factors so far *@return                     A HashSet of all factors factored from the original                                             factors */ public static HashSet<Long> completeFactors(HashSet<Long> factors) { HashSet<Long> newFactors = new HashSet<Long>(); for(long fact : factors) newFactors.add(fact); for(long fact : factors) for(long fact2 : factors) if(fact > fact2 && fact % fact2 == 0) newFactors.add(fact / fact2); return newFactors; } /** * This method will run the euclidean algorithm to * factor two integer into their greatest common denominator * *@param a     An integer to be reduced *@param b      An integer to be reduced *@return       The greatest common denominator between a and b */ public static long gcd(long a, long b) { while(b != 0) { long t = b; b = a % b; a = t; } return a; } }
Output: The factors are 1, 2357, 2896753, 1229