从费马素数测试到Miller-Rabin素数测试

素数测试的基础是费马小定理:

对于素数p,任意0<a<p,有a^p \bmod p \equiv a ;变化形式为a^{p-1} \bmod p \equiv 1

费马小定理逆定理不成立,所以费马测试不保证正确。强化版测试方法就是Miller-Rabin素数测试:

在测试a^{p-1} \bmod p \equiv 1通过,且p-1还有因数2的情况下,进一步测试a^{(p-1)/2} \bmod p \equiv 1,如此重复除2的测试直到指数不再被2整除或者mod余数不再是1。最后那次测试如果mod结果不是1和p-1则可否定p是素数。

从费马测试到Miller-Rabin测试的理论依据,网上很多博文都瞎抄了个二次探测定理,然而根本不适用,实际的推论过程如下。

初次测试指数n_{0}=p-1 ,其后每次测试都除2,依次记为n_{1},n_{2},n_{3}, ...

对于n_{i},如果可以继续除2测试,即n_i/2=n_{i+1},那么可以做如下变换

a^{n_i} \bmod p \equiv 1

(a^{n_i}-1) \bmod p \equiv 0

(a^{n_{i+1}\times 2}-1) \bmod p \equiv 0

(a^{n_{i+1}}-1)(a^{n_{i+1}}+1) \bmod p \equiv 0

将mod改用整除形式表示

(a^{n_{i+1}}-1)(a^{n_{i+1}}+1) = p \times c

因为前提p是素数,无法再拆分因数,所以若左边两个括号不是直接等于p和c,就只能将c拆分因数

(a^{n_{i+1}}-1)(a^{n_{i+1}}+1) = p \times d\times e

即左边两个括号必有一个是p的整数倍,若是前者,则a^{n_{i+1}} \bmod p \equiv 1;若是后者,则a^{n_{i+1}} \bmod p \equiv p-1

特殊情况左边有一个括号是0,那么a^{n_{i+1}} =1,同样符合上述结论。

附一定范围内Miller-Rabin素数测试的a值选择,摘自wiki:

n<2047\Rightarrow a=2

n<1373653\Rightarrow a=2,3 (16bit符号数范围内确保素数判断正确)

n<4759123141\Rightarrow a=2,7,61(32bit符号数范围内确保素数判断正确)

n<18446744073709551616 \Rightarrow a=2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37(64bit符号数范围内确保素数判断正确)

c代码实现:

int fast_power_mod(int base, int exp, int divisor) {
    int rmd = 1;
    while (exp != 0) {
        if (exp & 0x1)
            rmd = (rmd * base) % divisor;
        exp = exp >> 1;
        base = (base * base) % divisor;
    }
    return rmd;
}

type_prime miller_rabin(int num) {
    //special case for small number
    if (num <= 0) return ILLEGAL;
    switch (num) {
    case 1:
        return ONE;
    case 2:
    case 3:
        return PRIME;
    default:
        break;
    }
    //for num < 4759123141, which means all int value
    const int TEST_BASE[] = {2, 7, 61}
    for (int i = 0; i < sizeof(TEST_BASE) / sizeof(TEST_BASE[0]); i++) {
        int base = TEST_BASE[i];
        if (base >= num) return PRIME;
        int exp = num - 1;
        while (true) {
            int rmd = fast_power_mod(base, exp, num);
            if (rmd == num - 1) break;
            if (rmd != 1) return COMPOSITE;
            //to here means rmd==1
            if ((exp & 0x1) == 1) break;
            exp = exp >> 1;
        }
    }
    return PRIME;
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容