使用gmp有效地考虑大量因素

时间:2010-11-29 06:42:40

标签: c++ math primes gmp factorization

我需要获得大数的所有素数因子,这些因子可以很容易地达到1k位。 这些数字实际上是随机的,所以它应该不难。 我该如何有效地做到这一点?我使用C ++和GMP库。

编辑: 我想你们都误解了我 我的意思是数字是为了获得数字的所有主要因素 对不起我的英语,在我的语言素数和因素是相同的:))


澄清(来自OP的其他帖子):

我需要的是一种使用C ++和GMP(Gnu Multiple Precession lib)有效地计算(找到数字的素数因子)大数(可能达到2048位)的方法,或者更不用说任何其他方式。 这些数字实际上是随机的,所以几乎没有机会难以计算,即使这个数字难以计算,我也可以重新编号(尽管不能选择)。

4 个答案:

答案 0 :(得分:9)

一个良好的开端将是一些使用小素数的预过滤,比如说所有素数低于10万左右。只需尝试除以它们中的每一个(创建一个表,然后在运行时加载或将其作为代码中的静态数据)。它可能看起来很慢而且很愚蠢,但如果这个数字是完全随机的,这将给你一些非常快速的因素。然后查看剩余的数字并决定下一步该做什么。如果它很小(“小”意味着什么取决于你)你可以尝试素性测试(我认为GMP中有一些东西),如果它给它一个素数,你可以在大多数情况下信任它。否则你必须进一步考虑它。

如果你的数字非常庞大并且你关心性能,那么你肯定需要实现比仅仅是一个愚蠢的部门更复杂的东西。看看Quadratic Sieve(试试维基百科)。它非常简单但非常强大。如果您要接受挑战,请尝试使用MPQS,这是二次筛分算法的一种变体。 This forum是一个很好的信息来源。甚至现有的工具实现都需要 - see for example this

请注意,1k位的数字无论如何都是巨大的。考虑到这样的数字(即使使用MPQS或其他人),如果你是幸运的话可能需要数年,如果没有,则需要永远。我认为MPQS在大约100-400位的数字上表现良好(如果它们由两个几乎同样大的素数组成,当然这是最难的情况)。

答案 1 :(得分:3)

以下是Java中的示例算法(它不是带有GMP的C ++,但转换应该非常简单):

  1. 生成比特长x
  2. 的随机数Nbits
  3. 尝试分解所有素数因素< 100,保留一个划分x的主要因素列表。
  4. 使用Java的isProbablePrime方法
  5. 测试剩余因子是否为素数
  6. 如果剩余因子乘积具有足够概率的素数,我们已成功地将x分解。 (停止
  7. 否则剩下的因子产品肯定是复合的(参见isProbablePrime docs)。
  8. 虽然我们还有时间,但我们会运行Pollard rho algorithm,直到找到除数d。
  9. 如果我们没时间,我们就失败了。 (的 STOP
  10. 我们找到了一个除数d。因此,我们将d,将d的素因子加到x的素因子列表中,然后转到步骤4。
  11. 此算法的所有参数都在程序列表的开头附近。我查找了1024位随机数,超时为250毫秒,并且我一直运行该程序,直到我得到一个数字x至少有4个素数因子(有时程序会找到一个带有1,2或3个素因子的数字)第一)。使用此参数设置,我的2.66Ghz iMac通常需要大约15-20秒。

    Pollard的rho算法效率不高,但与quadratic sieve(QS)或general number field sieve(GNFS)相比,它很简单 - 我只想看看简单算法是如何工作的


    为什么会有效:(尽管你们很多人声称这是一个难题)

    明显的事实是prime numbers aren't that rare。对于1024位数字,素数定理表示每1024 ln 2约1(=约710) 数字是素数。

    因此,如果我生成一个素数的随机数x,并且我接受概率素数检测,那么我已成功考虑了x。

    如果它不是素数,但我很快就分解了一些小因素,剩下的因素是(概率上)素数,那么我已经成功地考虑了x。

    否则我只是放弃并生成一个新的随机数。 (OP说可以加入)

    成功考虑的大多数数字将具有1个大素数因子和一些小素因子。

    难以考虑的数字是那些没有小素数因子和至少2个大素因子的数字(这些因素包括两个大数的乘积的加密密钥; OP对加密没有任何说明),以及当我没时间的时候,我可以跳过它们。

    package com.example;
    
    import java.math.BigInteger;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Random;
    
    public class FindLargeRandomComposite {
        final static private int[] smallPrimes = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 
            31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 
            73, 79, 83, 89, 97};
    
        final static private int maxTime = 250;
        final static private int Nbits = 1024;
        final static private int minFactors = 4;
        final static private int NCERTAINTY = 4096;
    
        private interface Predicate { public boolean isTrue(); }
    
        static public void main(String[] args)
        {
            Random r = new Random();
            boolean found = false;
            BigInteger x=null;
            List<BigInteger> factors=null;
            long startTime = System.currentTimeMillis();
            while (!found)
            {
                x = new BigInteger(Nbits, r);
                factors = new ArrayList<BigInteger>();
                Predicate keepRunning = new Predicate() {
                    final private long stopTime = System.currentTimeMillis() + maxTime;
                    public boolean isTrue() {
                        return System.currentTimeMillis() < stopTime;
                    }
                };
                found = factor(x, factors, keepRunning);
                System.out.println((found?(factors.size()+" factors "):"not factored ")+x+"= product: "+factors);
                if (factors.size() < minFactors)
                    found = false;
            }
            long stopTime = System.currentTimeMillis();
            System.out.println("Product verification: "+(x.equals(product(factors))?"passed":"failed"));
            System.out.println("elapsed time: "+(stopTime-startTime)+" msec");
        }
    
        private static BigInteger product(List<BigInteger> factors) {
            BigInteger result = BigInteger.ONE;
            for (BigInteger f : factors)
                result = result.multiply(f);
            return result;
        }
    
        private static BigInteger findFactor(BigInteger x, List<BigInteger> factors,
                BigInteger divisor)
        {
            BigInteger[] qr = x.divideAndRemainder(divisor);
            if (qr[1].equals(BigInteger.ZERO))
            {
                factors.add(divisor);
                return qr[0];
            }
            else
                return x;
        }
    
        private static BigInteger findRepeatedFactor(BigInteger x,
                List<BigInteger> factors, BigInteger p) {
            BigInteger xprev = null;
            while (xprev != x)
            {
                xprev = x;
                x = findFactor(x, factors, p);
            }
            return x;
        }
    
        private static BigInteger f(BigInteger x, BigInteger n)
        {
            return x.multiply(x).add(BigInteger.ONE).mod(n);
        }
    
        private static BigInteger gcd(BigInteger a, BigInteger b) {
            while (!b.equals(BigInteger.ZERO))
            {
                BigInteger nextb = a.mod(b);
                a = b;
                b = nextb;
            }
            return a;
        }
        private static BigInteger tryPollardRho(BigInteger n,
                List<BigInteger> factors, Predicate keepRunning) {
            BigInteger x = new BigInteger("2");
            BigInteger y = x;
            BigInteger d = BigInteger.ONE;
            while (d.equals(BigInteger.ONE) && keepRunning.isTrue())
            {
                x = f(x,n);
                y = f(f(y,n),n);
                d = gcd(x.subtract(y).abs(), n);
            }
            if (d.equals(n))
                return x;
            BigInteger[] qr = n.divideAndRemainder(d);
            if (!qr[1].equals(BigInteger.ZERO))
                throw new IllegalStateException("Huh?");
            // d is a factor of x. But it may not be prime, so run it through the factoring algorithm.
            factor(d, factors, keepRunning);
            return qr[0];
        }
    
        private static boolean factor(BigInteger x0, List<BigInteger> factors,
                Predicate keepRunning) {
    
            BigInteger x = x0;
            for (int p0 : smallPrimes)
            {
                BigInteger p = new BigInteger(Integer.toString(p0));
                x = findRepeatedFactor(x, factors, p);          
            }
            boolean done = false;
            while (!done && keepRunning.isTrue())
            {
                done = x.equals(BigInteger.ONE) || x.isProbablePrime(NCERTAINTY);
                if (!done)
                {
                    x = tryPollardRho(x, factors, keepRunning);
                }
            }
            if (!x.equals(BigInteger.ONE))
                factors.add(x);
            return done;
        }
    }
    

答案 2 :(得分:1)

目前你不能用gMP来考虑bigint。您可以将bigint转换为其他库并使用其分解算法。请注意,使用>&gt;&gt; 20位数的整数分解需要专门的算法,并且接近指数级慢。

退房:

答案 3 :(得分:1)

如果要计算的数字具有小的素数因子,则可以使用Pollard p-1因子分解算法。它已经考虑了数字2 ^ 740 + 1的30位素因子.ECM是类似但是次指数的算法,但实现更加困难。算法基于绑定b设置为的时间量。它将考虑具有因子p的任何数,其中p-1是b-平滑的。

//Pollard p - 1 factorization algorithm

void factor(mpz_t g, mpz_t n, long b)
{
    //sieve for primes
    std::vector<bool> r;

    for(int i = 0; i < b; i++)
        r.push_back(true);


    for(int i = 2; i < ceil(sqrt(b - 1)); i++)
        if(r.at(i) == true)
            for(int j = i * i; j < b; j += i)
                r.at(j) = false;

    std::vector<long> p;
    std::vector<long> a;
    for(int i = 2; i < b; i++)
        if(r[i] == true)
        {
            p.push_back(i);//Append the prime on to the vector
            int temp = floor(log(b) / log(i)); //temp = logb(i)

            // put primes in to sieve
            // a = the maximum power for p ^ a < bound b
            if(temp == 0)
                a.push_back(1);
            else
                a.push_back(temp);                
        }

    int m = p.size();//m = number of primes under bound b

    mpz_t c;// c is the number Which will be exponated
    mpz_init(c);
    long two = 2;
    mpz_set_ui(c, two);// set c to 2

    int z = 0;
    long x = 2;

    // loop c until a factor is found
    for(;;)
    {
    mpz_set_si( c, x);

    //powering ladder
    for(long i = 0; i < m; i++)
        for(long j = 0; j < a[i]; j++)
            mpz_powm_ui(c , c, (p[i]), n);

    //check if a factor has been found;
    mpz_sub_ui(c ,c,1);
    mpz_gcd(g ,c, n);
    mpz_add_ui(c , c, 1);

    //if g is a factor return else increment c
    if((mpz_cmp_si(g,1)) > 0 && (mpz_cmp(g,n)) < 0)
        return;
    else if (x > b)
        break;
    else
        x++;
    }

}


int main()
{
    mpz_t x;
    mpz_t g;

    //intialize g and x
    mpz_init(g);
    mpz_init_set_str(x,"167698757698757868925234234253423534235342655234234235342353423546435347",10);

    //p-1 will factor x as long as it has a factor p where p - 1 is b-smooth(has all prime factors less than bound b)
    factor(g , x, 1000);

    //output the factor, it will output 1 if algorithm fails
    mpz_out_str(NULL, 10, g);

    return 0;
}

输出 - 7465647 执行时间 - 0.003秒

由J.Pollard创建的另一个Factoring算法是Pollards Rho算法,它不是那么快但需要很小的空间。他们也是实现这一目标的方法。其复杂度为O(n ^ 1/4)

//Pollard rho factoring algorithm
void rho(mpz_t g, mpz_t n)
{
    mpz_t x;
    mpz_t y;
    mpz_init_set_ui(x ,2);
    mpz_init_set_ui(y ,2);//initialize x and y as 2
    mpz_set_ui(g , 1);
    mpz_t temp;
    mpz_init(temp);

    if(mpz_probab_prime_p(n,25) != 0)
        return;//test if n is prime with miller rabin test

    int count;
    int t1 = 0;
    int t2 = 1;
    int nextTerm = t1 + t2;
    while(mpz_cmp_ui(g,1) < 1)
    {
        f(x,n);//x is changed
        f(y,n);//y is going through the sequence twice as fast
        f(y,n);

        if(count == nextTerm)//calculate gcd every fibonacci number
        {
            mpz_sub(temp,x,y);
            mpz_gcd(g , temp, n);

            t1 = t2;
            t2 = nextTerm;
            nextTerm = t1 + t2;//calculate next fibonacci number
        }

        count ++;
    }

    return;
}

int main()
{
    mpz_t x;
    mpz_t g;

    //intialize g and x
    mpz_init(g);
    mpz_init_set_str(x,"167698757698757868925234234253423",10);


    rho(g , x);

    //output the factor, it will output 1 if algorithm fails
    mpz_out_str(NULL, 10, g);

    return 0;
}

输出 - 353 执行时间 - 0.003s