Фактор большого числа эффективно с gmp - PullRequest
6 голосов
/ 29 ноября 2010

Мне нужно получить все основные множители больших чисел, которые могут легко получить до 1 тыс. Бит.Числа практически случайны, поэтому это не должно быть сложно.Как мне сделать это эффективно?Я использую C ++ с библиотекой GMP.

РЕДАКТИРОВАТЬ: я думаю, что вы все не поняли меня.
Что я имею в виду под простым числом, чтобы получить все основные факторы числа.
Извините за мой английский,на моем языке простые и множители одинаковы:)


уточнение (из другого поста ОП):

Мне нужен способ эффективного факторинга (найти простые множители числа) больших чисел (может достигать 2048 бит) с использованием C ++ и GMP (Gnu Multiple Precession lib) или менее предпочтительно любым другим способом.Числа практически случайны, поэтому маловероятно, что их будет сложно вычислить, и даже если число сложно вычислить, я могу перекатить число (хотя выбрать не могу).

Ответы [ 4 ]

9 голосов
/ 29 ноября 2010

Хорошим началом будет некоторая предварительная фильтрация с небольшими простыми числами, скажем, обо всех простых числах ниже 100 000 или около того.Просто попробуйте разделить на каждый из них (создайте таблицу, которую вы затем загрузите во время выполнения или поместите в виде статических данных в своем коде).Это может показаться медленным и глупым, но если число совершенно случайно, это даст вам очень быстрые факторы с большой вероятностью.Затем посмотрите на оставшийся номер и решите, что делать дальше.Если он достаточно мал (что означает «маленький», зависит от вас), вы можете попробовать тест на первичность (я думаю, что в GMP что-то есть), и если он дает это простое число, вы можете в большинстве случаев доверять ему.В противном случае вы должны учесть это дальше.

Если ваши цифры действительно огромны, и вы заботитесь о производительности, то вам определенно нужно реализовать что-то более сложное, чем просто глупое разделение.Посмотрите на квадратичное сито (попробуйте википедию).Это довольно просто, но очень мощно.Если у вас есть шанс, попробуйте MPQS, вариант алгоритма квадратичного сита. Этот форум является хорошим источником информации.Есть даже существующие реализации нужного вам инструмента - см., Например, это .

Обратите внимание, что числа с битами 1 КБ во всех отношениях огромны.Факторинг такого числа (даже с MPQS или другими) может занять годы, если вам повезет, и навсегда, если нет.Я думаю, что MPQS хорошо работает с числами около 100-400 битов (если они составлены из двух простых чисел, почти одинаково больших, что, конечно, является самым сложным случаем).

3 голосов
/ 05 декабря 2010

Ниже приведен пример алгоритма на Java (это не C ++ с GMP, но преобразование должно быть довольно простым), который:

  1. генерирует случайное число x битовой длины Nbits
  2. пытается выделить все основные факторы <100, сохраняя список простых факторов, которые делят x. </li>
  3. проверяет, является ли оставшийся фактор простым, используя Java isProbablePrime метод
  4. Если оставшееся произведение на множители является простым с достаточной вероятностью, нам удалось разложить x. ( STOP )
  5. В противном случае оставшийся факторный продукт определенно является составным (см. Документы isProbablePrime).
  6. Пока у нас еще есть время, мы запускаем алгоритм Полларда , пока не найдем делитель d.
  7. Если у нас заканчивается время, мы терпим неудачу. ( СТОП )
  8. Мы нашли делитель d. Таким образом, мы вычленяем d, добавляем простые множители d в список простых факторов x и переходим к шагу 4.

Все параметры этого алгоритма находятся в начале списка программ. Я искал 1024-битные случайные числа с таймаутом 250 миллисекунд, и я продолжаю запускать программу, пока не получу число x, по крайней мере, с 4 простыми факторами (иногда программа находит число с 1, 2 или 3 простыми факторами первый). С этим установленным параметром на iMac 2,66 ГГц обычно требуется около 15-20 секунд.

Алгоритм Полларда не очень эффективен, но он прост по сравнению с квадратичным ситом (QS) или общим числовым ситом (GNFS) - я просто хотел посмотреть, как работает простой алгоритм.


Почему это работает: (несмотря на утверждение многих из вас, что это сложная проблема)

Простой факт в том, что простые числа не так уж редки . Для 1024-битных чисел теорема о простых числах говорит, что примерно 1 на каждые 1024 ln 2 (= около 710) числа простые.

Так что, если я сгенерирую случайное число x, которое будет простым, и я приму вероятностное обнаружение простого числа, я успешно вычислю x.

Если это не простое число, но я быстро выделяю несколько небольших факторов, а оставшийся фактор является (вероятностно) простым, то я успешно разложил множитель x.

В противном случае я просто сдаюсь и сгенерирую новое случайное число. (что ОП говорит, что приемлемо)

У большинства успешно вычисленных чисел будет 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;
    }
}
1 голос
/ 01 сентября 2017

Вы можете использовать алгоритм факторизации Полларда p-1, если число, которое вы хотите вычислить, имеет малые простые множители.Он вычленил 30-значный простой множитель числа 2 ^ 740 + 1. 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 секунды

Еще одним алгоритмом факторинга, созданным Дж. Поллардом, был алгоритм Полларда Ро, который не так быстр, но требует очень мало места.Это также способы его укрупнения.Его сложность 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,003 с

1 голос
/ 02 января 2017

В настоящее время вы не можете рассчитывать на Bigint с GMP. Вы можете конвертировать ваши bigint в другие библиотеки и использовать их алгоритмы факторинга. Обратите внимание, что разложение целых чисел с >> 20 цифрами требует специализированных алгоритмов и почти экспоненциально медленно.

Выезд:

...