C++ 求最近小素数的快速算法

C++ 求最近小素数的快速算法,c++,c,algorithm,C++,C,Algorithm,如果给定的数字是10,我们必须返回7(因为它是最接近的较小素数) 我能想到的是:- Mainloop:测试给定的数字是否为素数(通过应用素数测试), 如果为素数,则返回该数字,否则将该数字递减1,然后转到Mainloop 但我必须在长整数范围内工作,这需要很多时间 还有更好的方法吗?如果我只使用上面的方法,那么我应该使用哪个素性测试?谢谢:)调查一下。这是概率性的,但如果您这样做几百次,几乎可以保证长范围内的精度 另外,如果您可以使用Java,biginger.isProbablePrime也会

如果给定的数字是10,我们必须返回7(因为它是最接近的较小素数)

我能想到的是:- Mainloop:测试给定的数字是否为素数(通过应用素数测试), 如果为素数,则返回该数字,否则将该数字递减1,然后转到Mainloop


但我必须在长整数范围内工作,这需要很多时间

还有更好的方法吗?如果我只使用上面的方法,那么我应该使用哪个素性测试?谢谢:)

调查一下。这是概率性的,但如果您这样做几百次,几乎可以保证
长范围内的精度

另外,如果您可以使用Java,
biginger.isProbablePrime
也会有所帮助。C\C++似乎没有用于测试素性的内置函数。

仔细研究。这是概率性的,但如果您这样做几百次,几乎可以保证
长范围内的精度


另外,如果您可以使用Java,
biginger.isProbablePrime
也会有所帮助。C\C++似乎没有用于测试素数的内置函数。

除上述内容外,还应注意始终存在至少一个素数p,其中
n除上述内容外,还应注意,如果输入的大小有界,则始终存在至少一个素数p,在预计算素数表中查找可能是最快的。

如果输入的大小有界,在预计算素数表中查找可能是最快的。

以下是丹尼尔·菲舍尔在评论中提到的Baillie Wagstaff伪素数测试的伪代码实现。我们先从一个简单的埃拉托斯烯筛子开始,我们以后需要它

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
数论中的雅可比函数告诉我们a是否是二次剩余模p

Gary Miller的强伪素数测试基于Pierre de Fermat的小定理,该定理指出,如果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 chains的测试。这是卢卡斯链的计算

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
然后,卢卡斯伪素数测试确定一个数是素数还是可能是复合数。像费马测试一样,它有两种风格,标准和强烈,就像费马测试一样,它也可能被愚弄,尽管费马测试的错误是一个复合数可能被错误地报告为素数,但卢卡斯测试的错误是一个素数可能被错误地报告为复合数

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
函数isLucasPseudoprime(n)#标准
d、 p,q:=selfridge(n)
如果p==0,则返回n==d
u、 v,k:=链(n,0,2,1,p,d,q,(n+1)/2)
返回u==0
函数isLucasPseudoprime(n)#强
d、 p,q:=selfridge(n)
如果p==0,则返回n==d
s、 t:=0,n+1
而t%2==0
s、 t:=s+1,t/2
u、 v,k:=链(n,1,p,1,p,d,q,t//2
如果u==0或v==0,则返回素数
r:=1
而r
然后,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
函数isPrime(n)#Baillie Wagstaff
如果n<2或isSquare(n),则返回False
对于素数中的p(100)
如果n%p==0,则返回n==p
返回isStrongPseudoprime(n,2)\
和isLucasPseudoprime(n)#标准或强
Baillie Wagstaff测试没有已知错误

一旦你有了一个好的素数测试,你可以通过从n开始倒数,在第一个素数处停止,找到小于n的最大素数


如果你对素数编程感兴趣,我会在我的博客或其他许多与素数相关的博客上推荐,你可以在博客上使用搜索功能找到它们。

下面是丹尼尔·菲舍尔在评论中提到的Baillie Wagstaff伪素数测试的伪代码实现。W我们先从一个简单的埃拉托斯烯筛子开始,这是我们以后需要的

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
数论中的雅可比函数告诉我们a是否是二次剩余模p

Gary Miller的强伪素数检验基于Pierre de Fermat的小定理,该定理指出,如果p是素数,那么对于任何a!=0,a^(p-1)==1(mod p)。Miller的检验比Fermat的b稍强一些