calgorithmfactorization

Pollard Rho factorization method implementation in C


Can anyone help me out with the pollard rho implementation? I have implemented this in C. It's working fine for numbers upto 10 digits but it's not able to handle greater numbers.

Please help me out to improve it to carry out factorization of numbers upto 18 digits . My code is this:

#include<stdio.h>
#include<math.h>
int gcd(int a, int b)
{
    if(b==0) return a ;
    else
    return(gcd(b,a%b)) ;
}

long long int mod(long long int a , long long int b , long long int n )
{    
     long long int x=1 , y=a ;
     while(b>0)
     {
        if(b%2==1)  x = ((x%n)*(y%n))%n ;
        y = ((y%n)*(y%n))%n ;
         b/=2 ;
     }
     return x%n ;
}

int isprimes(long long int u)
{  
    if(u==3)
    return 1 ;
     int a = 2 , i ;
     long long int k , t = 0 , r , p ;
     k = u-1 ;
     while(k%2==0)
     { k/=2 ; t++ ; }

         while(a<=3)                                                              /*der are no strong pseudoprimes common in base 2 and base 3*/
         {   
         r = mod(a,k,u) ;
             for(i = 1 ; i<=t ; i++)
             {
                  p = ((r%u)*(r%u))%u ;
                  if((p==1)&&(r!=1)&&(r!=(u-1)))
                  {  return 0 ; }
                  r = p ;
             }
          if(p!=1)
          return 0 ;
         else
          a++ ;
         } 

          if(a==4)
          return 1 ;

}

long long int pol(long long int u)
{ 
  long long int x = 2 , k , i , a , y , c , s;
  int d = 1 ;
   k = 2 ;
   i = 1 ;
   y = x ;
   a = u ;
   if(isprimes(u)==1)
   { 
     return 1;
   }
   c=-1 ;
   s = 2 ;
   while(1)
   {
     i++;
     x=((x%u)*(x%u)-1)% u ;

     d = gcd(abs(y-x),u) ;

     if(d!=1&&d!=u)
     { printf("%d ",d);
       while(a%d==0) { a=a/d;  }

        x = 2 ;
        k = 2 ;
        i = 1 ;
        y = x ;
        if(a==1)
        { return 0 ; }
        if(isprimes(a)!=0)
        { return a ; }
        u=a ;

     }
     if(i==k)
     {y = x ; k*=2 ; c = x ;}                                                       /*floyd cycle detection*/
        if(c==x)                                                                 
     { x = ++s ; }
    }
    return ;

}

int main()
{
   long long int t ;
    long long int i , n , j , k , a , b  , u ;
    while(scanf("%lld",&n)&&n!=0)
    { u = n ; k = 0 ;
    while(u%2==0)
       {  u/=2 ; k = 1 ; }
      if(k==1) printf("2 ") ;
      if(u!=1)
      t = pol(u) ;
        if(u!=1) 
      {
           if(t==1)
           { printf("%lld",u) ; }
           else
           if(t!=0)
           { printf("%lld",t) ; }
      }
          printf("\n");
    }
    return 0;
}

sorry for the long code ..... I am a new coder.


Solution

  • When you're multiplying two numbers modulo m, the intermediate product can become nearly m^2. So if you use a 64-bit unsigned integer type, the maximal modulus it can handle is 2^32, if the modulus is larger, overflow may happen. It will be rare when the modulus is only slightly larger, but that makes it only less obvious, you cannot rely on being lucky if the modulus allows the possibility of overflow.

    You can gain a larger range by a factor of two if you choose a representative of the residue class modulo m of absolute value at most m/2 or something equivalent:

    uint64_t mod_mul(uint64_t x, uint64_t y, uint64_t m)
    {
        int neg = 0;
        // if x is too large, choose m-x and note that we need one negation for that at the end
        if (x > m/2) {
            x = m - x;
            neg = !neg;
        }
        // if y is too large, choose m-y and note that we need one negation for that at the end
        if (y > m/2) {
            y = m - y;
            neg = !neg;
        }
        uint64_t prod = (x * y) % m;
        // if we had negated _one_ factor, and the product isn't 0 (mod m), negate
        if (neg && prod) {
            prod = m - prod;
        }
        return prod;
    }
    

    So that would allow moduli of up to 2^33 with a 64-bit unsigned type. Not a big step.

    The recommended solution to the problem is the use of a big-integer library, for example GMP is available as a distribution package on most if not all Linux distros, and also (relatively) easily installable on Windows.

    If that is not an option (really, are you sure?), you can get it to work for larger moduli (up to 2^63 for an unsigned 64-bit integer type) using Russian peasant multiplication:

    x * y = 2 * (x * (y/2)) + (x * (y % 2))
    

    so for the calculation, you only need that 2*(m-1) doesn't overflow.

    uint64_t mod_mult(uint64_t x, uint64_t y, uint64_t m)
    {
        if (y == 0) return 0;
        if (y == 1) return x % m;
        uint64_t temp = mod_mult(x,y/2,m);
        temp = (2*temp) % m;
        if (y % 2 == 1) {
            temp = (temp + x) % m;
        }
        return temp;
    }
    

    Note however that this algorithm needs O(log y) steps, so it's rather slow in practice. For smaller m you can speed it up, if 2^k*(m-1) doesn't overflow, you can proceed in steps of k bits instead of single bits (x*y = ((x * (y >> k)) << k) + (x * (y & ((1 << k)-1)))), which is a good improvement if your moduli are never larger than 48 or 56 bits, say.

    Using that variant of modular multiplication, your algorithm will work for larger numbers (but it will be significantly slower). You can also try test for the size of the modulus and/or the factors to determine which method to use, if m < 2^32 or x < (2^64-1)/y, the simple (x * y) % m will do.