【问题标题】:Miller Rabin Primality test米勒拉宾素性检验
【发布时间】:2011-10-22 16:12:01
【问题描述】:

我用升 C 写了一个米勒拉宾素数测试,但它在每个输入上都返回 false。

代码如下:

    static Boolean MilRab(UInt64 n)
    {
        UInt64[] ar = new UInt64[] { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 };
        for (int i = 0; i < 10; i++)
        {
            if (Tanu(ar[i], n) == true) return false;
        }
        return true;
    }//MilRab

    static Boolean Tanu(UInt64 a, UInt64 n)
    {
        UInt64 b = n - 1;
        UInt64 d = 1;
        UInt64 x;
        Int16 i;
        for (i = 63; i >= 0; i--) if (((b >> i) & 1) == 1) break;

        for (;i>=0;i--)
        {
            x = d;
            d = ((d * d) % n);
            if (d == 1 && x != 1 && x != n - 1) return true;
            if (b>>i == 1) d = (d * a) % n;
        }
        if (d != 1) return true;
      return false;
    }//Tanu

您认为问题可能是什么?我花了一天的时间进行调试,这让我发疯。谢谢。

【问题讨论】:

  • (d * d) % n(d * a) % n 中,乘法可以(并且通常会)在以n 为模减少之前将模2^64 包装起来,这是不正确的(对于几乎所有n)。
  • 我推荐使用 BigInteger 结构体。
  • 我使用了 BigInteger.Modpow 和 BigInteger 乘法,它运行良好。谢谢。
  • 在 SO 上发布代码时,请尽量保留重要的变量名。

标签: c#


【解决方案1】:

检查这个实现是否有 int 和 BigInteger

http://rosettacode.org/wiki/Miller-Rabin_primality_test#C.23

【讨论】:

    【解决方案2】:

    正如阿米尔所说,

    http://rosettacode.org/wiki/Miller-Rabin_primality_test#C.23

    有解决办法。但是.. 我试过了,即使对于像 79 这样的非常小的输入值,它也会给出错误的结果(79 是素数,但反复出现不是素数)。原因是功率溢出(例如 11^78 类似于 1E+81,不能用 32 位或 64 位整数表示)。

    所以这里

    public static class RabinMiller
    {
        public static bool IsPrime(int n, int k)
        {
            if(n < 2)
            {
                return false;
            }
            if(n != 2 && n % 2 == 0)
            {
                return false;
            }
            int s = n - 1;
            while(s % 2 == 0)
            {
                s >>= 1;
            }
            Random r = new Random();
            for (int i = 0; i < k; i++)
            {
                double a = r.Next((int)n - 1) + 1;
                int temp = s;
                int mod = (int)Math.Pow(a, (double)temp) % n;
                while(temp != n - 1 && mod != 1 && mod != n - 1)
                {
                    mod = (mod * mod) % n;
                    temp = temp * 2;
                }
                if(mod != n - 1 && temp % 2 == 0)
                {
                    return false;
                }
            }
            return true;
        }
    }
    

    你应该添加一个函数

        static int ModuloPower(int a, int b, int n)
        {
            // return (a^b)%n
            int res = 1;
            for (int i = 0; i < b; ++i)
                res = (res * a) % n;
            return res;
        }
    

    并将电源/模数线更改为

        int mod = ModuloPower(a, temp, n); // (int)Math.Pow(a, (double)temp) % n;
    

    这当然可以再次优化。请参阅 rosettacode 网站上其他语言的代码示例,了解一些更巧妙的方法来更快地计算功率/模数。

    (我尝试在那里编辑代码但必须注册,所以我在这里发布。)

    【讨论】:

    • 发布代码 with 更正而不是错误代码不是更有意义吗?
    • 我注意到这在 46817 上失败了......也许在某个地方又发生了一次溢出。
    • @Sunsetquest 您给出的数字大约是 15.5 位,所以我认为它可能会溢出 32 位(如果您放弃符号,则为 31)。您可以将“int res”设为“int64 res”,并且应该获得更大的数字以使该算法起作用,或者可能使用 uint64,我不确定 C# 命名,ulong 应该是正确的。
    • 你是对的。现在使用 Int64 至少达到 650,000...可能要高得多。
    • 仅供参考,我在 Rosettacode 站点 rosettacode.org/wiki/Miller-Rabin_primality_test#C.23 上应用了您的修复和其他一些代码更新。
    猜你喜欢
    • 2017-04-24
    • 2014-07-28
    • 2011-12-02
    • 1970-01-01
    • 1970-01-01
    • 2018-11-05
    • 2011-04-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多