Разложить число на 2 простых сомножителя

Одно из требований для Telegram Authentication разлагает заданное число на 2 простых сомножителя. В частности P*Q = N, where N < 2^63

Как мы можем найти меньший простой сомножитель, такой, что P < square_root(N)

Мои предложения:

1) предварительно вычислить простые числа от 3 до 2^31.5, затем проверить, если N mod P = 0

2) Найдите алгоритм для проверки простых чисел (но нам все еще нужно проверить N mod P =0)

Есть ли алгоритм для простых чисел, который хорошо подходит для этого случая?


person Charles Okwuagwu    schedule 11.08.2015    source источник
comment
Вы уверены, что это требование? Все, что они описывают, в некотором смысле похоже на RSA, но с ключами игрушечной длины. В реальном RSA важно, чтобы pq было настолько длинным, чтобы не существовало общеизвестного алгоритма, способного факторизовать его за разумное время. Однако, если вы действительно хотите разложить на множители полупростые числа меньше 2 ^ 63 (то есть в области 18 цифр), Pollard Rho на C++ может сделать это за несколько дней на ПК. Здесь, на SO, есть списки Pollard Rho. Специальное сито с числовым полем может сделать это за считанные секунды. И он слишком мал, чтобы даже заморачиваться с использованием GNFS. Проверьте msieve. Это должно вас заинтересовать.   -  person WDS    schedule 12.08.2015
comment
@wds это требование. я даже дал ссылку на раздел их страницы API.   -  person Charles Okwuagwu    schedule 12.08.2015
comment
@wds я не думаю, что вы правы с точки зрения требований времени. Нам уже дано N. Все, что нам нужно сделать, это разложить его на 2 простых числа. я сделал это методом перебора уже за 4 минуты с моим кодом vb.net. Я просто надеялся получить помощь в сокращении времени до нескольких секунд или даже меньше.   -  person Charles Okwuagwu    schedule 12.08.2015
comment
О, мне очень жаль, вы правы насчет времени. Я сделал свою оценку, исходя из собственного опыта работы с Поллардом Ро и из общей оценки числа в диапазоне 2^63, состоящего примерно из 18 цифр. Это правильно. Я вспомнил, как мой Поллард Ро занимал несколько секунд, например, с 12-значными числами. Но я неправильно запомнил важный факт. Пара секунд использовалась, когда простые множители были 12-значными, а не когда произведение было 12-значным. Моя программа в мгновение ока вычислит полупростое 18-значное число. Я опубликую это для вас.   -  person WDS    schedule 12.08.2015
comment
Код C для факторизации собственных 64-битных чисел может факторизовать 63-битные полупростые числа намного меньше секунды. gnu factor делает это с помощью Rho и SQUFOF. Мой код на github делает это с помощью различных алгоритмов, включая Rho, SQUFOF, HOLF, P-1). Однако это намного сложнее, чем код WDS.   -  person DanaJ    schedule 13.08.2015
comment
@DanaJ, пожалуйста, дайте ссылку на свой код   -  person Charles Okwuagwu    schedule 13.08.2015
comment
Я также только что обнаружил, что github.com/ricksladkey/Dirichlet вообще не отображается в моем Google ищет даже в поиске GitHub   -  person Charles Okwuagwu    schedule 13.08.2015
comment
@CharlesO, факторный код GNU - это проект NT от Нила и Торбьорна. Автономный, без GMP, 128-битный. Мой собственный код находится в github.com/danaj/Math-Prime-Util, который может быть скомпилирован отдельно (см. конец factor.c) с кодом GMP на github.com/ danaj/Math-Prime-Util-GMP. Один из моих текущих проектов — превратить все это в обычную библиотеку C.   -  person DanaJ    schedule 13.08.2015


Ответы (2)


Фу! Я только что вставил эту программу, а потом понял, что вы пометили свой вопрос C #. Это C++, версия Pollard Rho, которую я написал пару лет назад и разместил здесь на SO, чтобы помочь кому-то понять ее. Это во много раз быстрее при факторинге полупростых чисел, чем пробное деление. Как я уже сказал, я сожалею, что это C++, а не C#, но вы должны быть в состоянии понять концепцию и даже довольно легко ее портировать. В качестве бонуса в библиотеке .NET есть пространство имен для обработки произвольно больших целых чисел, где моя реализация на C++ требовала от меня найти для них стороннюю библиотеку. В любом случае, даже на C# приведенная ниже программа разбивает полупростое число порядка 2^63 на 2 простых числа менее чем за 1 секунду. Есть и более быстрые алгоритмы, чем этот, но они намного сложнее.

#include <string>
#include <stdio.h>
#include <iostream>
#include "BigIntegerLibrary.hh"

typedef BigInteger BI;
typedef BigUnsigned BU;

using std::string;
using std::cin;
using std::cout;

BU pollard(BU &numberToFactor);
BU gcda(BU differenceBetweenCongruentFunctions, BU numberToFactor);
BU f(BU &x, BU &numberToFactor, int &increment);
void initializeArrays();
BU getNumberToFactor ();
void factorComposites();
bool testForComposite (BU &num);

BU primeFactors[1000];
BU compositeFactors[1000];
BU tempFactors [1000];
int primeIndex;
int compositeIndex;
int tempIndex;
int numberOfCompositeFactors;
bool allJTestsShowComposite;

int main ()
{
    while(1)
    {
        primeIndex=0;
        compositeIndex=0;
        tempIndex=0;
        initializeArrays();
        compositeFactors[0] = getNumberToFactor();
        cout<<"\n\n";
        if (compositeFactors[0] == 0) return 0;
        numberOfCompositeFactors = 1;
        factorComposites();
    }
}

void initializeArrays()
{
    for (int i = 0; i<1000;i++)
    {
        primeFactors[i] = 0;
        compositeFactors[i]=0;
        tempFactors[i]=0;
    }
}

BU getNumberToFactor ()
{
    std::string s;
    std::cout<<"Enter the number for which you want a prime factor, or 0 to     quit: ";
    std::cin>>s;
    return stringToBigUnsigned(s);
}

void factorComposites()
{
    while (numberOfCompositeFactors!=0)
    {
        compositeIndex = 0;
        tempIndex = 0;

        // This while loop finds non-zero values in compositeFactors.
        // If they are composite, it factors them and puts one factor in     tempFactors,
        // then divides the element in compositeFactors by the same amount.
        // If the element is prime, it moves it into tempFactors (zeros the     element in compositeFactors)
        while (compositeIndex < 1000)
        {
            if(compositeFactors[compositeIndex] == 0)
            {
                compositeIndex++;
                continue;
            }
            if(testForComposite(compositeFactors[compositeIndex]) == false)
            {
                tempFactors[tempIndex] = compositeFactors[compositeIndex];
                compositeFactors[compositeIndex] = 0;
                tempIndex++;
                compositeIndex++;
            }
            else
            {
                tempFactors[tempIndex] = pollard     (compositeFactors[compositeIndex]);
                compositeFactors[compositeIndex] /= tempFactors[tempIndex];
                tempIndex++;
                compositeIndex++;
            }
        }
        compositeIndex = 0;

        // This while loop moves all remaining non-zero values from     compositeFactors into tempFactors
        // When it is done, compositeFactors should be all 0 value elements
        while (compositeIndex < 1000)
        {
            if (compositeFactors[compositeIndex] != 0)
            {
                tempFactors[tempIndex] = compositeFactors[compositeIndex];
                compositeFactors[compositeIndex] = 0;
                tempIndex++;
                compositeIndex++;
            }
            else compositeIndex++;
        }
        compositeIndex = 0;
        tempIndex = 0;
    // This while loop checks all non-zero elements in tempIndex.
    // Those that are prime are shown on screen and moved to primeFactors
    // Those that are composite are moved to compositeFactors
    // When this is done, all elements in tempFactors should be 0
    while (tempIndex<1000)
    {
        if(tempFactors[tempIndex] == 0)
        {
            tempIndex++;
            continue;
        }
        if(testForComposite(tempFactors[tempIndex]) == false)
        {
            primeFactors[primeIndex] = tempFactors[tempIndex];
            cout<<primeFactors[primeIndex]<<"\n";
            tempFactors[tempIndex]=0;
            primeIndex++;
            tempIndex++;
        }
        else
        {
            compositeFactors[compositeIndex] = tempFactors[tempIndex];
            tempFactors[tempIndex]=0;
            compositeIndex++;
            tempIndex++;
        }
    }
    compositeIndex=0;
    numberOfCompositeFactors=0;

    // This while loop just checks to be sure there are still one or more composite factors.
    // As long as there are, the outer while loop will repeat
    while(compositeIndex<1000)
    {
        if(compositeFactors[compositeIndex]!=0) numberOfCompositeFactors++;
        compositeIndex ++;
    }
}
return;
}

// The following method uses the Miller-Rabin primality test to prove with 100% confidence a given number is composite,
// or to establish with a high level of confidence -- but not 100% -- that it is prime

bool testForComposite (BU &num)
{
    BU confidenceFactor = 101;
    if (confidenceFactor >= num) confidenceFactor = num-1;
    BU a,d,s, nMinusOne;
    nMinusOne=num-1;
    d=nMinusOne;
    s=0;
    while(modexp(d,1,2)==0)
    {
        d /= 2;
        s++;
    }
    allJTestsShowComposite = true; // assume composite here until we can prove otherwise
    for (BI i = 2 ; i<=confidenceFactor;i++)
    {
        if (modexp(i,d,num) == 1) 
            continue;  // if this modulus is 1, then we cannot prove that num is composite with this value of i, so continue
        if (modexp(i,d,num) == nMinusOne)
        {
            allJTestsShowComposite = false;
            continue;
        }
        BU exponent(1);     
        for (BU j(0); j.toInt()<=s.toInt()-1;j++)
        {
            exponent *= 2;
            if (modexp(i,exponent*d,num) == nMinusOne)
            {
                // if the modulus is not right for even a single j, then break and increment i.
                allJTestsShowComposite = false;
                continue;
            }
        }
        if (allJTestsShowComposite == true) return true; // proven composite with 100% certainty, no need to continue testing
    }
    return false;
    /* not proven composite in any test, so assume prime with a possibility of error = 
    (1/4)^(number of different values of i tested).  This will be equal to the value of the
confidenceFactor variable, and the "witnesses" to the primality of the number being tested will be all integers from
2 through the value of confidenceFactor.

Note that this makes this primality test cryptographically less secure than it could be.  It is theoretically possible,
if difficult, for a malicious party to pass a known composite number for which all of the lowest n integers fail to
detect that it is composite.  A safer way is to generate random integers in the outer "for" loop and use those in place of
the variable i.  Better still if those random numbers are checked to ensure no duplicates are generated.
*/
}

BU pollard(BU &n)
{
    if (n == 4) return 2;
    BU x = 2;
    BU y = 2;
    BU d = 1;
    int increment = 1;

    while(d==1||d==n||d==0)
    {
        x = f(x,n, increment);
        y = f(y,n, increment);
        y = f(y,n, increment);
        if (y>x)
        {
            d = gcda(y-x, n);
        }
        else
        {
            d = gcda(x-y, n);
        }
        if (d==0) 
        {
            x = 2;
            y = 2;
            d = 1;
            increment++; // This changes the pseudorandom function we use to increment x and y
        }
    }
    return d;
}


BU gcda(BU a, BU b)
{
    if (a==b||a==0)
        return 0;   // If x==y or if the absolute value of (x-y) == the number to be factored, then we have failed to find
                    // a factor.  I think this is not proof of primality, so the process could be repeated with a new function.
                    // For example, by replacing x*x+1 with x*x+2, and so on.  If many such functions fail, primality is likely.

    BU currentGCD = 1;
    while (currentGCD!=0) // This while loop is based on Euclid's algorithm
    {
        currentGCD = b % a;
        b=a;
        a=currentGCD;
    }
    return b;
}

BU f(BU &x, BU &n, int &increment)
{
    return (x * x + increment) % n;
}
person WDS    schedule 12.08.2015
comment
Кстати, я не знаю, является ли это общепринятой практикой в ​​​​SO, но вот ссылка на исполняемый файл в моем Dropbox, если вы хотите попробовать его, прежде чем пытаться портировать и компилировать. Если вы возьмете его копию, вы, конечно же, захотите проверить ее на вирусы. Вы не найдете на нем никакого вируса, потому что его там нет. Но все равно это хорошая практика. dropbox.com/s/qvinsmnjy1ft82g/Pollard%20Rho% 20Complete.exe?dl=0 - person WDS; 12.08.2015
comment
с 1724114033281923457 это занимает 2+ секунды. спасибо - person Charles Okwuagwu; 12.08.2015
comment
Пожалуйста, не могли бы вы направить меня к любому более быстрому алгоритму по имени, возможно, с реализацией .net. Спасибо. - person Charles Okwuagwu; 12.08.2015
comment
К сожалению, я никогда не видел в .net ничего быстрее, чем этот Поллард Ро. Быстрее можно на 100%, но я ни разу не видел, чтобы кто-то это писал. Но чтобы дать вам представление о том, что возможно, проверьте msieve. Перейдите сюда: sourceforge.net/projects/msieve/files/msieve/ Msieve%20v1.52, скачайте и распакуйте файл msieve152_gpu.zip. Затем перейдите к этой папке в окне cmd и введите msieve152_gpu -q 1724114033281923457 и нажмите Enter. Он учитывает это число за долю секунды. Я думаю, что есть более быстрые программы, чем эта, но это самая быстрая из всех, что я когда-либо видел, и она очень хороша. - person WDS; 12.08.2015
comment
Мне бы очень хотелось увидеть, как этот программист вложил такую ​​же мощь в DLL, которую можно было бы вызывать либо как библиотеку .NET, либо, что более вероятно, через Interop. Но я не нашел ничего подобного. - person WDS; 12.08.2015
comment
Спасибо, что указали путь. BigInteger .Net упрощает реализацию. Но мне интересно, можно ли использовать UInt64 вместо BigInteger для N <2^63? - person Charles Okwuagwu; 13.08.2015
comment
Конечно, UInt64 будет работать нормально, пока никакие вычисления не возьмут значение за пределы диапазона переменной. В качестве бонуса за то, что с ним немного проще работать, UInt64 намного быстрее, чем BigInteger. - person WDS; 13.08.2015

Алгоритм Ро Полларда [VB.Net]

Очень быстро находит P, где P*Q = N, для N < 2^63

Dim rnd As New System.Random

Function PollardRho(n As BigInteger) As BigInteger
    If n Mod 2 = 0 Then Return 2

    Dim x As BigInteger = rnd.Next(1, 1000)
    Dim c As BigInteger = rnd.Next(1, 1000)
    Dim g As BigInteger = 1
    Dim y = x

    While g = 1
        x = ((x * x) Mod n + c) Mod n
        y = ((y * y) Mod n + c) Mod n
        y = ((y * y) Mod n + c) Mod n
        g = gcd(BigInteger.Abs(x - y), n)
    End While

    Return g
End Function

Function gcd(a As BigInteger, b As BigInteger) As BigInteger
    Dim r As BigInteger
    While b <> 0
        r = a Mod b
        a = b
        b = r
    End While
    Return a
End Function

Алгоритм Ричарда Брента [VB.Net] Это еще быстрее.

Function Brent(n As BigInteger) As BigInteger
    If n Mod 2 = 0 Then Return 2

    Dim y As BigInteger = rnd.Next(1, 1000)
    Dim c As BigInteger = rnd.Next(1, 1000)
    Dim m As BigInteger = rnd.Next(1, 1000)

    Dim g As BigInteger = 1
    Dim r As BigInteger = 1
    Dim q As BigInteger = 1

    Dim x As BigInteger = 0
    Dim ys As BigInteger = 0

    While g = 1
        x = y
        For i = 1 To r
            y = ((y * y) Mod n + c) Mod n
        Next
        Dim k = New BigInteger(0)
        While (k < r And g = 1)
            ys = y
            For i = 1 To BigInteger.Min(m, r - k)
                y = ((y * y) Mod n + c) Mod n
                q = q * (BigInteger.Abs(x - y)) Mod n
            Next

            g = gcd(q, n)
            k = k + m
        End While
        r = r * 2
    End While

    If g = n Then
        While True
            ys = ((ys * ys) Mod n + c) Mod n
            g = gcd(BigInteger.Abs(x - ys), n)
            If g > 1 Then
                Exit While
            End If
        End While
    End If

    Return g
End Function
person Charles Okwuagwu    schedule 13.08.2015