【问题标题】:Fermat primality test费马素性检验
【发布时间】:2013-03-03 03:19:15
【问题描述】:

我曾尝试为Fermat primality test 编写代码,但显然失败了。 所以,如果我理解得很好:如果p 是素数,那么((a^p)-a)%p=0 其中p%a!=0。 我的代码似乎没问题,因此很可能我误解了基础知识。我在这里错过了什么?

private bool IsPrime(int candidate)
    {
        //checking if candidate = 0 || 1 || 2
        int a = candidate + 1; //candidate can't be divisor of candidate+1
        if ((Math.Pow(a, candidate) - a) % candidate == 0) return true;
        return false;
    }

【问题讨论】:

  • “但显然失败了”。为何如此?哪个输入失败了?
  • 除了非常小的输入外,您无法以这种方式实现模幂运算。它会很快溢出,并且对于任何不是 2 的幂的候选者来说都是非常具有破坏性的。你需要通过平方取幂,使用 64 位中间值。
  • @Kevin 尝试从 4 到 20。4 到 13 为真。 14到15是假的。 16 对。 17 到 20 错误。
  • @harold 如何处理?
  • 所有素数都将满足该测试。并非所有通过测试的候选人都是素数。您必须测试许多as,然后才能说一个数字是可能素数,即使这样,您也会将一类合数(“卡迈克尔数”)误认为是素数。

标签: c# math primes


【解决方案1】:

阅读Fermat primality test 上的维基百科文章,您必须选择一个a,该a 比您正在测试的候选人,而不是更多。

此外,正如 MattW 所评论的那样,仅测试一个 a 不会给你一个关于候选人是否是素数的结论性答案。您必须测试许多可能的as,然后才能确定一个数字可能是素数。即便如此,some numbers 可能看起来是素数,但实际上是合数。

【讨论】:

  • 由于我们正在工作 mod candidatecandidate + 11 是一致的。这很糟糕 - 每个候选人都应该通过,因为 1 pow n = 1 for all n;测试开始失败的候选人> = 14是有限精度数学开始崩溃的标志。值得一提的是,15 pow 14 需要 54 位尾数,而 IEEE 双精度只有 52。
【解决方案2】:

您的基本算法是正确的,但如果您想对非平凡数字执行此操作,则必须使用比 int 更大的数据类型。

您不应该像以前那样实现模幂运算,因为中间结果很大。这是模幂运算的平方乘法算法:

function powerMod(b, e, m)
    x := 1
    while e > 0
        if e%2 == 1
            x, e := (x*b)%m, e-1
        else b, e := (b*b)%m, e//2
    return x

例如,437^13 (mod 1741) = 819。如果你使用上面显示的算法,没有中间结果会大于 1740 * 1740 = 3027600。但是如果你先进行取幂,中间结果为437^13 是 21196232792890476235164446315006597,你可能想避免。

即便如此,费马检验也不完美。有一些合数,即 Carmichael 数,无论您选择什么见证,它总是会报告质数。如果您想要更好的方法,请寻找 Miller-Rabin 测试。我在我的博客上谦虚地推荐this essay 使用素数编程。

【讨论】:

  • 感谢算法。我知道测试不完善。
  • 如果选择的碱基与数字不互质,卡迈克尔数将被检测为复合数。不幸的是,对于像211*421*631 这样的小数字来说,这已经不太可能很快发生了。
【解决方案3】:

您正在处理非常大的数字,并试图将它们存储在双精度数中,即只有 64 位。 替身会尽力保持你的号码,但你会失去一些准确性。

另一种方法:

请记住,mod 运算符可以多次应用,但仍然给出相同的结果。 因此,为避免获得大量数字,您可以在计算功率时应用 mod 运算符。

类似:

private bool IsPrime(int candidate)
{
    //checking if candidate = 0 || 1 || 2
    int a = candidate - 1; //candidate can't be divisor of candidate - 1

    int result = 1;
    for(int i = 0; i < candidate; i++)
    {
        result = result * a;

        //Notice that without the following line, 
        //this method is essentially the same as your own.
        //All this line does is keeps the numbers small and manageable.
        result = result % candidate;
    }

    result -= a;
    return result == 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-10-16
    • 1970-01-01
    • 2011-04-30
    • 1970-01-01
    • 2021-02-18
    • 1970-01-01
    • 2020-09-25
    相关资源
    最近更新 更多