【问题标题】:A console app with Biginteger calculation freezes具有 Biginteger 计算的控制台应用程序冻结
【发布时间】:2017-11-25 11:40:28
【问题描述】:

问题

具有 Biginteger 计算的控制台应用程序冻结

详情

我正在用 C# 开发一个控制台应用程序,用于测试非常大的数字(10 的几十到几百次方)是否是素数。由于默认整数类型最多只能处理 10^19(longulong)的数字,因此我使用的是 BitInteger 类。但是当我在 Visual Studio 中以调试模式运行应用程序时,应用程序会冻结。

    static void Main(string[] args)
    {
        int exp = 100;
        var bi = BigInteger.Pow(10, exp);
        var sw = new Stopwatch();
        sw.Start();
        for (int i = 0; i < 1000; i++)
        {
            bi++;
            Console.WriteLine($"{i}th try : {bi} ({sw.Elapsed.ToString("mm\\:ss\\.ff")})");

            bool b = IsPrime(bi);
            if (b)
            {
                Console.WriteLine($"{bi} is a prime number");
            }

            //GC.Collect();
        }
        sw.Stop();

        Console.Read();
    }

    static private bool IsPrime(BigInteger n)
    {
        if (n <= 1)
            return false;
        else if (n <= 3)
            return true;
        else if (n % 2 == 0 || n % 3 == 0)
            return false;
        for (BigInteger i = 5; i * i <= n; i += 6)
        {
            if (n % i == 0 || n % (i + 2) == 0)
                return false;
        }

        return true;

    }

程序是否冻结取决于变量exp。我测试了exp 的几个值。

  • exp 的值):(程序冻结的i
  • 10 : 没有冻结(应用在不到一秒的时间内完成
  • 15:没有冻结(但是控制台窗口仅每 2 秒刷新一次)
  • 17 : 没有冻结(但控制台窗口仅每 17 秒刷新一次)
  • 20 : 39
  • 25 : 13
  • 30 : 37
  • 50 : 27
  • 100 : 37

很奇怪i 不会随着exp 的增加而单调上升。所以我为exp = 100 运行了三次程序,但得到了相同的结果。这些数字似乎是可重复的和可靠的。

我知道有一些算法可以比这个更好地测试素数,但我稍后会尝试。首先,我想检查程序的整个行为。

我为此问题搜索了“biginteger console freeze c#”,找到了两篇文章。

  1. MillerRabin primality test in C#
  2. C# BigInteger and int How to save memory?

第一个说“冻结”和“Biginteger”,但答案没有多大帮助。第二个提到节省内存,所以我认为问题与垃圾收集有关。然后我在for 循环(注释掉的行)的末尾添加了GC.Collect(),但这并没有解决问题。我得到了同样的结果。

我应该如何解决这个问题?

环境

  • Windows 8
  • Visual Studio 2017
  • C#
  • .NET Framework 4.6.1

【问题讨论】:

  • mjwills,程序在 IsPrime() 的 for 循环内循环。这是通过放置一个断点来检查的。
  • 100000000000000000000000000000000000000000000000000000000000‌​00000000000000000000‌​00000000000000000003‌​6的最小因子显然是2。所以IsPrime() 应该在else if (n % 2 == 0 || n % 3 == 0) 上返回false 并且应该打印一行。我不确定000000000000000000000000000000000000000000000000000000000000‌​00000000000000000000‌​00000000000000000037。我运行程序大约 20 分钟,控制台没有显示任何新行。此外,程序正常运行直到I=35 并突然停在i=36 才有意义。 bi 的变化很小。
  • 哦,我明白了!所以你说的是第 n 行在 17 秒内弹出,第 n+1 行在 17.001 秒内弹出,第 n+2 行在 17.002 秒内弹出,第 n+3 行在 17.003 秒内弹出(列表还在继续)所以它看起来像所有的线条都在 17 秒内准确出现,这是不正确的。非常感谢!
  • 众所周知,保理是一个很难快速解决的问题;这就是为什么它是密码系统的基础。您可以做得比现在好得多,但总体上不会非常更好。

标签: c# visual-studio console-application primes freeze


【解决方案1】:

你的算法基本上说:

你能被 2 整除吗? 3呢? 5个呢? 7? 11? 13? 17? 19?

等等等等

对于较小的输入值,检查所有这些排列很快。对于较大的输入值,如果较大的输入值具有较小的因子(例如 2、3、5 或 37),它可能会很快。

但是如果大输入缺少一个小因素(因为它是素数,或者因为它的最小因素非常大),你的算法必须做很多很多的检查。基本上,它必须检查三分之一(即每六个中的两个)数字,直到输入的平方根(直到找到匹配项)。对于大数,这涉及到大量的计算。

如果花费的时间太长,您需要编写更好/更快的IsPrime 算法。 This answer 在这方面可能会有所帮助。

您还可以考虑存储 一些 known 素数的列表,以便快速查找这些数字(例如从数据库中)而不是“计算”。

回复:

(但控制台窗口仅每 17 秒刷新一次)

这是因为某些输入确实是素数 - 因此需要 17 秒左右来验证(即检查每个排列)。它看起来就像控制台每 17 秒刷新一次,但实际上它是 计算 17 秒。然后随后的计算速度非常快(因为它们不是素数) - 所以看起来它们是“作为一批”出来的。

【讨论】:

  • > "您还可以考虑存储已知大素数的列表,以便快速查找这些数字(例如从数据库中)而不是“计算”。"这是不切实际的。从 1 到 N 的素数个数的粗略估计是 N/log(N)。 N=10^100 是 10^97。整个数据的大小至少为 10^97 字节。我怀疑是否有一个服务器可以存储这么多数据。但我可以使用最多 10000 个素数列表,以便在for 循环中仅使用素数作为i
  • This is not practical. 存储 所有 它们是不切实际的,是的。但是您可以存储其中的 子集(例如,一些 的 - 存储小的没有任何意义,因为它们可以快速计算)。归根结底,它是一种优化技术——您需要决定是要将 CPU 用于问题,还是将存储用于问题,或两者兼而有之。
  • @dixhom:你怀疑有一个服务器可以存储这么多数据吗?这比宇宙中的原子数量多十亿倍。现在是消除对此事的所有疑虑的好时机。
  • 哈哈,谢谢@EricLippert(公平地说,您的评论对于所有素数都是正确的,我不能100%确定对于所有已知 素数)。
  • @EricLippert 完全同意你的看法。而且我怀疑使用所有可能的粒子,包括宇宙中的光子就足够了:)
猜你喜欢
  • 1970-01-01
  • 2014-03-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-10
  • 1970-01-01
  • 2021-11-03
  • 1970-01-01
相关资源
最近更新 更多