Pollard rho整数分解

3
我正在尝试在C/C++中实现Pollard Rho整数分解。谷歌给出了该问题的Java实现,此处链接
由于我不太熟悉Java,所以我编写了这个C++实现。我的C++实现对大多数情况都有效,但是对于像“9999”这样的数字则无效。
我知道C++没有Biginteger类,因此无法像Java那样具有完整功能,但我想要分解15位数字,这对于unsigned long long来说已经足够。
请指出我的实现中有什么错误。

你能否把你的C代码粘贴到问题中,以便后人查看?(或者这样做是否不合适?我是一个相对较新的SO用户;也许60行的代码粘贴是不被允许的。) - Mark Dickinson
1
C++也有大整数实现。最受欢迎的可能是GNU提供的实现:http://gmplib.org/。 - Manuel
3个回答

10

问题就在这里:

#define abs(x) (x>0)?(x):(-x)

你的abs宏中缺少一些括号。试试这样:

#define abs(x) ((x)>0 ? (x) : -(x))

考虑使用 abs(x-xx) 时在 x-xx <= 0 的情况下会发生什么。

此外,为什么你的 gcd 函数返回 int 而不是 BigInteger?

你还应该注意,假设 unsigned long long 是一个 64 位整数类型,这段代码对于 N 大于 2**32 的情况将无法正确工作:如果 x(或 xx)大于或等于 2**32,那么 x*x 将在模 2**64 的情况下溢出,从而给出 x*x % N 的错误值。


4
在可能的情况下,内联函数比宏更可取,这就是原因。 - BlueRaja - Danny Pflughoeft
只是为了明确,真正重要的不是缺少括号,而是放错位置的括号紧挨着减号。为了安全地执行乘法,请查找SqrMod和MultiplyMod。 - xan

2

我注意到一个区别:Java代码将cx赋值为new BigInteger(N.bitLength(), random),而C++代码使用rand() % N,这是一个较小的随机范围。对于值9999,二进制表示为10011100001111,因此Java代码将使cx的最大值为16383。


rand() 的较小范围可能会影响性能,当 N 大于 32767 时。但是,你关于当 N=9999 时发生的事情的解释对我来说毫无意义。 - President James K. Polk
我犯了一个差一错误:9999十进制是10011100001111,需要14位来表示。如果N=9999,则BigInteger(N.bitLength(), random)将生成0到2^14-1之间的整数,而rand() % N将生成0到9998之间的整数。这不是代码中的关键错误,但它也不是Java版本的准确翻译。 - Adrian Cox

0
你可以尝试这个约100行的Pollard Rho C实现:
这里有一些帮助程序:
#include <stdlib.h>
#include <stdint.h>

typedef uint_fast64_t num ;

static inline num mul_mod(num a, num b, const num mod) {
    // Return (a * b) % mod, avoiding overflow errors while doing modular multiplication.
    num res = 0, tmp;
    for (b %= mod; a; a & 1 ? b >= mod - res ? res -= mod : 0, res += b : 0, a >>= 1, (tmp = b) >= mod - b ? tmp -= mod : 0, b += tmp);
    return res % mod;
}

static inline num square_root(num n) {
    // Return the number that was multiplied by itself to reach N.
    num a = 0, b, c;
    for (b = 1ULL << 62; b; c = a + b, n -= c &= -(n >= c), a = (a >> 1) | (c & b), b >>= 2);
    // Variable n contains the remainder.
    return a;
}

有一个必需的质数检查器

static  int is_prime(const num n, size_t iterations) {
    // Perform a Miller-Rabin (strong probable prime) test.
    num a = 0, b, c, d, e, f; int h, i;
    if ((n == 1) == (n & 1)) return n == 2;
    for (b = c = n - 1, h = 0; !(b & 1); b >>= 1, ++h);
    for (; iterations--;) {
        for (size_t g = 0; g < sizeof(a); ((char*)&a)[g++] = rand()); // random input.
        do for (d = e = 1 + a % c, f = n; (d %= f) && (f %= d););
        while (d > 1 && f > 1);
        for (d = f = 1; f <= b; f <<= 1);
        for (; f >>= 1; d = mul_mod(d, d, n), f & b && (d = mul_mod(e, d, n)));
        if (d == 1) continue;
        for (i = h; i-- && d != c; d = mul_mod(d, d, n));
        if (d != c) return 0;
    }
    return 1;
}

有两个因数分解函数:

static inline num factor_worker_2(const num n, size_t limit) {
    // Perform a Pollard's Rho probabilistic test.
    size_t a = -1, b = 2;
    num c, d = 1 + rand(), e = 1, f = 1;
    for (c = d %= n; f == 1 && --limit; d = c, b <<= 1, a = -1) {
        for (; f |= e, f == 1 && ++a != b;) {
            c = mul_mod(c, c, n);
            for (++c, c *= c != n, e = n, f = c > d ? c - d : d - c; (f %= e) && (e %= f););
        }
    }
    return f;
}

static inline num factor_worker_1(const num n) {
    // Perform a trial divisions test on N.
    static const num list[] = {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, 1};
    size_t i;
    for (i = -1; n % list[++i];);
    return list[i];
}

有一个因式分解的管理器

num factor(const num n) {
// Basic factorization manager, detect primes, perfect squares, execute workers.
    num res;
    switch (n) {
        case 0: case 1: case 2: case 3:
            res = 1; break;
        default:
            res = factor_worker_1(n);
            if (res == 1 && !is_prime(n, 20)) {
                res = square_root(n);
                if (res * res != n)
                    for(;res = factor_worker_2(n, -1), res == 1 || res == n;);
            }
    }
    return res;
}

有一个 主函数

#include <assert.h>
#include <stdio.h>

int main(void) {
    num N;
    N = 951818131364430049;
    printf("factor is %zu\n", factor(N));
}

为了尝试它

// You can put it into a main.c file then compile + execute :
// gcc -O3 -std=c99 -Wall -pedantic main.c ; ./a.out ;

这里是源代码,获取更多信息,请谢谢。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接