Miller-Rabin 确定性素数检验 (C)
Miller-Rabin deterministic primality test (C)
我发现了一个 C 语言程序,它实现了 Miller-Rabin 素数测试的确定性变体 here。但是,修改后的代码(如下所示)在处理大于 2^32 的数字时不起作用,即使我使用 unsigned long long 数据类型来存储我的数字。它应该能够容纳最多 2^64 的整数。哪里出错了?
简而言之:我的问题是我的代码正确地确定了一个数字是否为质数,但前提是它小于 2^32,但事实并非如此,因为我最多可以存储 2^ 64
unsigned long long power(unsigned long long a, unsigned long long n, unsigned long long mod)
{
unsigned long long power = a;
unsigned long long result = 1;
while (n)
{
if (n & 1)
result = (result * power) % mod;
power = (power * power) % mod;
n >>= 1;
}
return result;
}
// n−1 = 2^s * d with d odd by factoring powers of 2 from n−1
unsigned long long witness(unsigned long long n, unsigned long long s, unsigned long long d, unsigned long long a)
{
unsigned long long x = power(a, d, n);
unsigned long long y;
while (s) {
y = (x * x) % n;
if (y == 1 && x != 1 && x != n-1)
return 0;
x = y;
--s;
}
if (y != 1)
return 0;
return 1;
}
/*
* if n < 1,373,653, it is enough to test a = 2 and 3;
* if n < 9,080,191, it is enough to test a = 31 and 73;
* if n < 4,759,123,141, it is enough to test a = 2, 7, and 61;
* if n < 1,122,004,669,633, it is enough to test a = 2, 13, 23, and 1662803;
* if n < 2,152,302,898,747, it is enough to test a = 2, 3, 5, 7, and 11;
* if n < 3,474,749,660,383, it is enough to test a = 2, 3, 5, 7, 11, and 13;
* if n < 341,550,071,728,321, it is enough to test a = 2, 3, 5, 7, 11, 13, and 17.
*/
int is_prime_mr(unsigned long long n)
{
if (((!(n & 1)) && n != 2 ) || (n < 2) || (n % 3 == 0 && n != 3))
return 0;
if (n <= 3)
return 1;
unsigned long long d = n / 2;
unsigned long long s = 1;
while (!(d & 1)) {
d /= 2;
++s;
}
unsigned long long a1 = 4759123141;
unsigned long long a2 = 1122004669633;
unsigned long long a3 = 2152302898747;
unsigned long long a4 = 3474749660383;
if (n < 1373653)
return witness(n, s, d, 2) && witness(n, s, d, 3);
if (n < 9080191)
return witness(n, s, d, 31) && witness(n, s, d, 73);
if (n < a1)
return witness(n, s, d, 2) && witness(n, s, d, 7) && witness(n, s, d, 61);
if (n < a2)
return witness(n, s, d, 2) && witness(n, s, d, 13) && witness(n, s, d, 23) && witness(n, s, d, 1662803);
if (n < a3)
return witness(n, s, d, 2) && witness(n, s, d, 3) && witness(n, s, d, 5) && witness(n, s, d, 7) && witness(n, s, d, 11);
if (n < a4)
return witness(n, s, d, 2) && witness(n, s, d, 3) && witness(n, s, d, 5) && witness(n, s, d, 7) && witness(n, s, d, 11) && witness(n, s, d, 13);
return witness(n, s, d, 2) && witness(n, s, d, 3) && witness(n, s, d, 5) && witness(n, s, d, 7) && witness(n, s, d, 11) && witness(n, s, d, 13) && witness(n, s, d, 17);
}
int main()
{
unsigned long long in1 = 4294967291;
unsigned long long in2 = 4294967311;
int a = is_prime_mr(in1); //4294967291 is the last prime below 2^32, should return 1 and does so correctly
printf("%d\n",a);
int b = is_prime_mr(in2); //4294967311 is the first prime after 2^32, should also return 1 but returns 0
printf("%d",b);
return 0;
}
正如@Michael Dorgan 所建议的,问题出在两个 64 位整数的乘法上,使用 powermod
.
的不同实现解决了这个问题
//computes (a*b) (mod n)
unsigned long long mul_mod(unsigned long long a, unsigned long long b, unsigned long long m)
{
unsigned long long d = 0, mp2 = m >> 1;
if (a >= m) a %= m;
if (b >= m) b %= m;
for (int i = 0; i < 64; ++i)
{
d = (d > mp2) ? (d << 1) - m : d << 1;
if (a & 0x8000000000000000ULL)
d += b;
if (d >= m) d -= m;
a <<= 1;
}
return d;
}
//computes (a^b) (mod m)
unsigned long long powermod(unsigned long long a, unsigned long long b, unsigned long long m)
{
unsigned long long r = m==1?0:1;
while (b > 0) {
if (b & 1)
r = mul_mod(r, a, m);
b = b >> 1;
a = mul_mod(a, a, m);
}
return r;
}
我的代码现在适用于最大 2^64 的整数。
我发现了一个 C 语言程序,它实现了 Miller-Rabin 素数测试的确定性变体 here。但是,修改后的代码(如下所示)在处理大于 2^32 的数字时不起作用,即使我使用 unsigned long long 数据类型来存储我的数字。它应该能够容纳最多 2^64 的整数。哪里出错了?
简而言之:我的问题是我的代码正确地确定了一个数字是否为质数,但前提是它小于 2^32,但事实并非如此,因为我最多可以存储 2^ 64
unsigned long long power(unsigned long long a, unsigned long long n, unsigned long long mod)
{
unsigned long long power = a;
unsigned long long result = 1;
while (n)
{
if (n & 1)
result = (result * power) % mod;
power = (power * power) % mod;
n >>= 1;
}
return result;
}
// n−1 = 2^s * d with d odd by factoring powers of 2 from n−1
unsigned long long witness(unsigned long long n, unsigned long long s, unsigned long long d, unsigned long long a)
{
unsigned long long x = power(a, d, n);
unsigned long long y;
while (s) {
y = (x * x) % n;
if (y == 1 && x != 1 && x != n-1)
return 0;
x = y;
--s;
}
if (y != 1)
return 0;
return 1;
}
/*
* if n < 1,373,653, it is enough to test a = 2 and 3;
* if n < 9,080,191, it is enough to test a = 31 and 73;
* if n < 4,759,123,141, it is enough to test a = 2, 7, and 61;
* if n < 1,122,004,669,633, it is enough to test a = 2, 13, 23, and 1662803;
* if n < 2,152,302,898,747, it is enough to test a = 2, 3, 5, 7, and 11;
* if n < 3,474,749,660,383, it is enough to test a = 2, 3, 5, 7, 11, and 13;
* if n < 341,550,071,728,321, it is enough to test a = 2, 3, 5, 7, 11, 13, and 17.
*/
int is_prime_mr(unsigned long long n)
{
if (((!(n & 1)) && n != 2 ) || (n < 2) || (n % 3 == 0 && n != 3))
return 0;
if (n <= 3)
return 1;
unsigned long long d = n / 2;
unsigned long long s = 1;
while (!(d & 1)) {
d /= 2;
++s;
}
unsigned long long a1 = 4759123141;
unsigned long long a2 = 1122004669633;
unsigned long long a3 = 2152302898747;
unsigned long long a4 = 3474749660383;
if (n < 1373653)
return witness(n, s, d, 2) && witness(n, s, d, 3);
if (n < 9080191)
return witness(n, s, d, 31) && witness(n, s, d, 73);
if (n < a1)
return witness(n, s, d, 2) && witness(n, s, d, 7) && witness(n, s, d, 61);
if (n < a2)
return witness(n, s, d, 2) && witness(n, s, d, 13) && witness(n, s, d, 23) && witness(n, s, d, 1662803);
if (n < a3)
return witness(n, s, d, 2) && witness(n, s, d, 3) && witness(n, s, d, 5) && witness(n, s, d, 7) && witness(n, s, d, 11);
if (n < a4)
return witness(n, s, d, 2) && witness(n, s, d, 3) && witness(n, s, d, 5) && witness(n, s, d, 7) && witness(n, s, d, 11) && witness(n, s, d, 13);
return witness(n, s, d, 2) && witness(n, s, d, 3) && witness(n, s, d, 5) && witness(n, s, d, 7) && witness(n, s, d, 11) && witness(n, s, d, 13) && witness(n, s, d, 17);
}
int main()
{
unsigned long long in1 = 4294967291;
unsigned long long in2 = 4294967311;
int a = is_prime_mr(in1); //4294967291 is the last prime below 2^32, should return 1 and does so correctly
printf("%d\n",a);
int b = is_prime_mr(in2); //4294967311 is the first prime after 2^32, should also return 1 but returns 0
printf("%d",b);
return 0;
}
正如@Michael Dorgan 所建议的,问题出在两个 64 位整数的乘法上,使用 powermod
.
//computes (a*b) (mod n)
unsigned long long mul_mod(unsigned long long a, unsigned long long b, unsigned long long m)
{
unsigned long long d = 0, mp2 = m >> 1;
if (a >= m) a %= m;
if (b >= m) b %= m;
for (int i = 0; i < 64; ++i)
{
d = (d > mp2) ? (d << 1) - m : d << 1;
if (a & 0x8000000000000000ULL)
d += b;
if (d >= m) d -= m;
a <<= 1;
}
return d;
}
//computes (a^b) (mod m)
unsigned long long powermod(unsigned long long a, unsigned long long b, unsigned long long m)
{
unsigned long long r = m==1?0:1;
while (b > 0) {
if (b & 1)
r = mul_mod(r, a, m);
b = b >> 1;
a = mul_mod(a, a, m);
}
return r;
}
我的代码现在适用于最大 2^64 的整数。