例如: - 如果给定的数字是10,我们必须返回7(因为它是最接近的较小素数)
我能想到的是: - Mainloop:测试给定数字是否为素数(通过应用素性测试), 如果它的素数然后返回数字,否则将数字减1并转到Mainloop。
但是我必须在很长的int范围内工作并且需要花费很多时间。
是否有更好的方法,如果我应该采用上述方式,那么我应该使用哪种素性测试?谢谢:)
答案 0 :(得分:4)
如果输入的大小有界,那么在预先计算的素数表中查找可能是最快的。
答案 1 :(得分:3)
除了上述内容之外,还要注意Bertrand's postulate表示始终存在至少一个素数p n<p<2n-2
。所以这给你一个上限。
答案 2 :(得分:2)
查看Miller-Rabin primality test。这是概率性的,但如果你这样做几百次,这几乎可以保证long long
范围内的精度。
此外,如果您可以使用Java,BigInteger.isProbablePrime
可以提供帮助。 C \ C ++似乎没有用于测试素数的内置函数。
答案 3 :(得分:2)
以下是Daniel Fischer在评论中提到的Baillie-Wagstaff Pseudoprimality Test的伪代码实现。我们从一个简单的Eratosthenes筛子开始,我们将在以后需要它。
function primes(n)
ps := []
sieve := makeArray(2..n, True)
for p from 2 to n step 1
if sieve(p)
ps.append(p)
for i from p * p to n step p
sieve[i] := False
return ps
powerMod
函数将基数 b 提升为指数 e ,所有计算均以模拟 m 完成;它比先执行取幂要快得多,然后取结果的模数,因为中间计算量很大。
function powerMod(b, e, m)
x := 1
while e > 0
if e % 2 == 1
x := (b * x) % m
b := (b * b) % m
e := floor(e / 2)
return x
数论的jacobi
函数告诉 a 是否是二次余数mod p 。
function jacobi(a, p)
a := a % p
t := 1
while a != 0
while a % 2 == 0
a := a / 2
if p % 8 == 3 or p % 8 == 5
t := -t
a, p := p , a # swap
if a % 4 == 3 and p % 4 == 3
t := -t
a := a % p
if p == 1 return t else return 0
加里·米勒的强伪伪试验是基于皮埃尔·德·费马的 Little Theorem ,其中指出如果 p 是素数,那么任何 a != 0, a ^( p - 1)== 1(mod p )。米勒的测试比费马更强,因为它不能被卡迈克尔数字愚弄。
function isStrongPseudoprime(n, a)
d := n - 1; s := 0
while d % 2 == 0
d := d / 2; s := s + 1
t = powerMod(a, d, n)
if t == 1 return ProbablyPrime
while s > 0
if t == n - 1 return ProbablyPrime
t := (t * t) % n; s := s - 1
return Composite
Miller-Rabin测试执行 k 强伪测试,其中 k 通常介于10到25之间。强伪测试可能被愚弄,但如果你表现得足够多,被愚弄的可能性非常小。
function isPrime(n) # Miller-Rabin
for i from 1 to k
a := randInt(2 .. n-1)
if not isStrongPseudoprime(n, a)
return Composite
return ProbablyPrime
对于大多数目的而言,素性测试已足够,并且足够快。但是如果你想要更强一点,更快一点的东西,可以使用基于Lucas链的测试。这是卢卡斯链的计算。
function chain(n, u, v, u2, v2, d, q, m)
k := q
while m > 0
u2 := (u2 * v2) % n; v2 := (v2 * v2 - 2 * q) % n
q := (q * q) % n
if m % 2 == 1
t1 := u2 * v; t2 := u * v2
t3 := v2 * v; t4 := u2 * u * d
u, v := t1 + t2, t3 + t4
if u % 2 == 1 u := u + n
if v % 2 == 1 v := v + n
u, v, k := (u / 2) % n, (v / 2) % n), (q * k) % n
m := floor(m / 2)
return u, v, k
由于John Selfridge,使用算法初始化Lucas链是很常见的。
function selfridge(n)
d, s := 5, 1; ds := d * s
repeat
if gcd(ds, n) > 1 return ds, 0, 0
if jacobi(ds, n) == 1 return ds, 1, (1 - ds) / 4
d, s := d + 2, s * -1; ds := d * s
然后Lucas伪试验确定一个数字是素数还是复数。就像费马测试一样,它有两种标准,包括标准和强大,而且像费马测试一样,它可以被欺骗,尽管通过费马测试,错误是合成数字可能是不正确报告的素数,但是卢卡斯测试错误是一个素数可能是不正确报告的复合数。
function isLucasPseudoprime(n) # standard
d, p, q := selfridge(n)
if p == 0 return n == d
u, v, k := chain(n, 0, 2, 1, p, d, q, (n + 1) / 2)
return u == 0
function isLucasPseudoprime(n) # strong
d, p, q := selfridge(n)
if p == 0 return n == d
s, t := 0, n + 1
while t % 2 == 0
s, t := s + 1, t / 2
u, v, k := chain(n, 1, p, 1, p, d, q, t // 2
if u == 0 or v == 0 return Prime
r := 1
while r < s
v := (v * v - 2 * k) % n; k := (K * k) % n
if v == 0 return Prime
return ProbablyComposite
然后Baillie-Wagstaff测试很简单。首先检查输入是否小于2或是否为正方形(检查平方根是否为整数)。然后通过小于100的素数的试验除法快速找到大多数复合物,最后对基数2进行强烈的伪试验(一些人在3号基础上添加强伪荧光试验,更加确定),然后进行Lucas伪试验,最后确定
function isPrime(n) # Baillie-Wagstaff
if n < 2 or isSquare(n) return False
for p in primes(100)
if n % p == 0 return n == p
return isStrongPseudoprime(n, 2) \
and isLucasPseudoprime(n) # standard or strong
Baillie-Wagstaff测试没有已知错误。
一旦你有一个很好的素性测试,你可以通过从 n 倒数来找到小于 n 的最大素数,在第一个素数处停止。
如果您对使用素数进行编程感兴趣,我谦虚地在我的博客上推荐this essay,或者许多其他与素数有关的博客条目,您可以使用博客中的搜索功能找到它们。
答案 4 :(得分:-1)
看起来您正在处理this problem。
如@Ziyao Wei所述,您只需使用Miller-Rabin primality test即可解决此问题。
这是我的解决方案
#include<cstdio>
#include<cstdlib>
#include<ctime>
short T;
unsigned long long n;
inline unsigned long long multi_mod(const unsigned long long &a,unsigned long long b,const unsigned long long &n)
{
unsigned long long exp(a%n),tmp(0);
while(b)
{
if(b&1)
{
tmp+=exp;
if(tmp>n)
tmp-=n;
}
exp<<=1;
if(exp>n)
exp-=n;
b>>=1;
}
return tmp;
}
inline unsigned long long exp_mod(unsigned long long a,unsigned long long b,const unsigned long long &c)
{
unsigned long long tmp(1);
while(b)
{
if(b&1)
tmp=multi_mod(tmp,a,c);
a=multi_mod(a,a,c);
b>>=1;
}
return tmp;
}
inline bool miller_rabbin(const unsigned long long &n,short T)
{
if(n==2)
return true;
if(n<2 || !(n&1))
return false;
unsigned long long a,u(n-1),x,y;
short t(0),i;
while(!(u&1))
{
++t;
u>>=1;
}
while(T--)
{
a=rand()%(n-1)+1;
x=exp_mod(a,u,n);
for(i=0;i<t;++i)
{
y=multi_mod(x,x,n);
if(y==1 && x!=1 && x!=n-1)
return false;
x=y;
}
if(y!=1)
return false;
}
return true;
}
int main()
{
srand(time(NULL));
scanf("%hd",&T);
while(T--)
{
for(scanf("%llu",&n);!miller_rabbin(n,20);--n);
printf("%llu\n",n);
}
return 0;
}