C++ - бесконечный цикл алгоритма Ро Брент-Полларда

У меня есть следующая функция. Я взял его с двух сайтов и попытался адаптировать к своему, но это не очень хорошо сработало.

Когда я проверяю unsigned long int max - 2 или но как число 4294967293, он помещает следующий код в бесконечный цикл, где ys будет продолжать возвращаться к тому же значению.

Я очень новичок в алгоритмах факторизации, и пошаговая помощь в том, почему я получаю бесконечный цикл, была бы отличной.

Следующий код - это просто моя функция "ро". У меня есть еще одна функция, называемая gdc, которая такая же, как и любая другая gdc рекурсивная функция.

unsigned long int rho(unsigned long int n) 
{
    if (n == 1) return n;
    if (n % 2 == 0) return 2;

    unsigned long int y = rand() % n;
    unsigned long int x;
    unsigned long long int ys = y;
    unsigned long int c;
    do
        c = rand() % n;
    while (c == 0 || c == n - 2);
    unsigned long int m = 1000;
    unsigned long int d = 1;
    unsigned long int q = 1;
    unsigned long int r = 1;
    while (d == 1)
    {
        x = y;
        for (int i = 0; i < r; i++)
        {
            y = y * y % n;
            y += c;
            if (y < c)
                y += (std::numeric_limits<unsigned long>::max() - n) + 1;
            y %= n;
        }
        int j = 0;
        while (j < r && d == 1)
        {
            ys = y;
            for (int i = 0; i < m && i < (r-j); i++)
            {
                y = y * y % n;
                y += c;
                if (y < c)
                    y += (std::numeric_limits<unsigned long>::max() - n) + 1;
                y %= n;
                q *= ((x>y) ? x - y : y - x) % n;
            }
            d = gcd(q, n);
            j += m;
        }
        r *= 2;
    }
    if (d == n)
    {
        do
        {
            ys = ys * ys % n;
            std::cout << ys << std::endl;
            ys += c;
            if (ys < c)
                ys += (std::numeric_limits<unsigned long>::max() - n) + 1;
            ys %= n;
            d = gcd( ((x>ys) ? x - ys : ys - x) , n);
        } while (d == 1);
    }
    return d;
}

Примеры, из которых я адаптировал свой код:

РЕДАКТИРОВАТЬ

Я сделал так, как предложил Амд, привел в порядок свой код и переместил повторяющиеся строки во вспомогательные функции. Однако я все еще получаю бесконечный цикл для части d==n внизу. По какой-то причине f(ys) в конечном итоге возвращает то же самое, что и ранее, поэтому он продолжает циклически перебирать ряд значений.

uint64_t rho(uint64_t n)
{
    if (n == 1) return n;
    if (n % 2 == 0) return 2;

    uint64_t y = rand() % n;
    uint64_t x;
    unsigned long long int ys = y;
    uint64_t c;
    do c = rand() % n; while (c == 0 || c == n - 2);
    uint64_t m = 1000;
    uint64_t d = 1;
    uint64_t q = 1;
    uint64_t r = 1;
    do 
    {
        x = y;
        for (int i = 0; i <= r; i++)
            y = f(y, c, n);

        int j = 0;
        do 
        {
            ys = y;
            for (int i = 0; i <= min(m, r-j); i++)
            {
                y = f(y, c, n);
                q *= (abs(x,y) % n);
            }
            d = gcd(q, n);
            j += m;
        } while (j < r && d == 1);
        r *= 2;
    } while (d == 1);
    if (d == n)
    {
        do
        {
            ys = f(ys, c, n);
            d = gcd(abs(x, ys), n);
        } while (d == 1);
    }
    return d;
}

person ben    schedule 07.05.2016    source источник
comment
Наивные выражения, такие как y * y % n, не вычисляют по модулю n, если n достаточно велико, за исключением того, что код слишком длинный для серьезной проверки.   -  person harold    schedule 07.05.2016
comment
Ну, я могу вычислить max unsigned long int просто отлично. Это просто max unsigned long - 2, который терпит неудачу, так что это определенно не так.   -  person ben    schedule 07.05.2016
comment
Может случайно сработать. Это не делает это правильным.   -  person harold    schedule 07.05.2016


Ответы (2)


Он всегда должен заканчиваться. К тому времени, когда вы дойдете до этой точки в алгоритме, x будет внутри цикла (поскольку y началось с x и прошло весь путь, чтобы обнаружить цикл, используя обнаружение цикла Брента). Значение ys началось с y, которое началось с x, поэтому оно также будет продолжаться по циклу и в конечном итоге снова встретится с x (см. Обнаружение цикла Флойда или Черепахи и Зайца). В этот момент у вас будет gcd(ys,x)==x, и этот последний цикл завершится.

В опубликованной реализации есть несколько ошибок, которые, как я полагаю, могут вызывать проблему:

x = y;
for (int i = 0; i < r; i++)                // should be strictly less than
    ...

    ys = y;
    for (int i = 0; i < min(m, r-j); i++)  // again, strictly less than
    {
        y = f(y, c, n);
        q = (q*abs(x,y)) % n;  // needs "mod" operator AFTER multiplication
    }
    ...

Вы также можете заменить инициализацию c на

uint64_t c = (rand() % (n-3))+1

если вам нужно число в диапазоне [1,n-3].

Вот оригинал статьи Ричарда П. Брента: Улучшенный метод Монте-Карло Алгоритм факторизации

person Antonio Sanchez    schedule 05.11.2016

Редактировать:
это работает для меня и не попадает в бесконечный цикл:

#include<iostream>
#include<stdint.h>

#define min(a,b) (a<b?a:b)
#define abs(x,y) (x > y? x - y : y - x)

uint64_t gcd(uint64_t m, uint64_t n) {
    while (true) {
        int r = m % n;
        if (r == 0) {
            return n;
        }
        m = n;
        n = r;
    }
}
uint64_t f(uint64_t y, uint64_t c, uint64_t n) {
    y = (y * y) % n;
    y += c;
    if (y < c)
        y += (std::numeric_limits<uint32_t>::max() - n) + 1;
    y %= n;
    return y;
}

uint64_t rho(uint64_t n)
{
    if (n == 1) return n;
    if (n % 2 == 0) return 2;

    uint64_t y = rand() % n;
    uint64_t x;
    uint64_t ys = y;
    uint64_t c;
    do c = rand() % n; while (c == 0 || c == n - 2);
    uint64_t m = 1000;
    uint64_t d = 1;
    uint64_t q = 1;
    uint64_t r = 1;
    do
    {
        x = y;
        for (int i = 0; i <= r; i++)
            y = f(y, c, n);

        int j = 0;
        do
        {
            ys = y;
            for (int i = 0; i <= min(m, r - j); i++)
            {
                y = f(y, c, n);
                q *= (abs(x, y) % n);
            }
            d = gcd(q, n);
            j += m;
        } while (j < r && d == 1);
        r *= 2;
    } while (d == 1);
    if (d == n)
    {
        do
        {
            ys = f(ys, c, n);
            d = gcd(abs(x, ys), n);
        } while (d == 1);
    }
    return d;
}
int main() {
    std::cout << rho(std::numeric_limits<uint32_t>::max() - 2) << "\n";     //9241
}

Старый:
в соответствии с усовершенствованным алгоритмом факторизации Монте-Карло: http://wwwmaths.anu.edu.au/~brent/pd/rpb051i.pdf
pp:182-183
ошибка: ys = x * x % n;
правильно: ys = ys * ys % n; // ys=f(ys)

пожалуйста, используйте uint32_t или uint64_t... из stdint.h для хорошего и чистого стиля кодирования:

#include<iostream>
#include<stdint.h>

int gcd(int m, int n) {
    while (true) {
        int r = m % n;
        if (r == 0) {
            return n;
        }
        m = n;
        n = r;
    }
}

#define min(a,b) (a<b?a:b)
#define abs(x,y) (x > y? x - y : y - x) 

uint32_t f(uint32_t y, uint32_t c, uint32_t n) {
    y = (y * y) % n;
    y += c;
    if (y < c)
        y += (std::numeric_limits<uint32_t>::max() - n) + 1;
    y %= n;
    return y;
}

//http://wwwmaths.anu.edu.au/~brent/pd/rpb051i.pdf
//pp:182-183
uint32_t rho(uint32_t n) {
    if (n == 1) return n;
    if (n % 2 == 0) return 2;

    uint32_t y = rand() % n; // y = x0
    uint32_t x;
    uint64_t ys = y;
    uint32_t c;
    do { c = rand() % n; } while (c == 0 || c == n - 2);
    uint32_t m = 1000;
    uint32_t d = 1;
    uint32_t q = 1;
    uint32_t r = 1;
    do
    {
        x = y;
        for (int i = 1; i <= r; i++)  y = f(y, c, n);
        int j = 0;
        do
        {
            ys = y;
            for (int i = 1; i <= min(m, r - j); i++)
            {
                y = f(y, c, n);
                q = q * abs(x, y) % n;
            }
            d = gcd(q, n);
            j += m;
        } while (j < r && d == 1);
        r *= 2;
    } while (d == 1);
    if (d == n)
    {
        do
        {
            ys = f(ys, c, n);//not:     ys = f(x,c,n);      
            d = gcd(abs(x, ys), n);
        } while (d == 1);
    }
    return d;
}
person Community    schedule 07.05.2016
comment
Приветствую это, однако это то, что у меня было в моем коде. У меня было ys вместо x для начала, и в другом посте я увидел, что они использовали x, поэтому я попробовал. В любом случае я все еще получаю бесконечные циклы - person ben; 07.05.2016
comment
Я рекомендую сначала очистить ваш код (например, использовать некоторые вспомогательные функции, такие как f(x) и рекомендации, как указано выше (это поможет найти ошибки), а затем попытаться отладить бесконечный цикл, чтобы найти стратегию выхода из бесконечного цикла, если вы все еще проблема, не стесняйтесь публиковать свой новый код. - person ; 07.05.2016
comment
Только что добавил правку. Я нашел, что вызывает это, но я действительно не понимаю, почему - person ben; 07.05.2016