具有很大因子的任意精度算术

时间:2014-02-10 14:21:52

标签: c# arbitrary-precision

这是一个数学问题,不是编程就是有用的东西!

我想计算非常大数的阶乘(10 ^ n,其中n> 6)。 我达到了任意精度,这对1000之类的任务非常有用。但它显然死了(StackOverflowException :))更高的值。我不是在寻找直接的答案,而是一些关于如何进一步发展的线索。

    static BigInteger factorial(BigInteger i)
    {
        if (i < 1)
            return 1;
        else
            return i * factorial(i - 1);
    }
    static void Main(string[] args)
    {
        long z = (long)Math.Pow(10, 12);
        Console.WriteLine(factorial(z));
        Console.Read();
    }

我是否必须从System.Numerics.BigInteger辞职?我想到了一些在文件中存储必要数据的方法,因为RAM显然会耗尽。此时优化非常重要。那么你会推荐什么?

另外,我需要尽可能精确的数值。忘记提到我不需要所有这些数字,只有大约20个。

3 个答案:

答案 0 :(得分:7)

正如其他答案所示,递归很容易被删除。现在的问题是:您可以将结果存储在BigInteger中,还是必须转到某种外部存储?

您需要存储的位数n!大致与n log n成比例。 (这是 Stirling的近似的弱形式。)所以让我们看看一些大小:(注意我在这篇文章的早期版本中做了一些算术错误,我在这里进行了修改。)

(10^6)! takes order of 2 x 10^6 bytes = a few megabytes
(10^12)! takes order of 3 x 10^12 bytes = a few terabytes
(10^21)! takes order of 10^22 bytes = ten billion terabytes

一些megs将适合记忆。您可以轻松掌握几TB,但您可能需要编写一个内存管理器。 100亿太字节将占用全球所有科技公司的综合资源,但它是可行的。

现在考虑计算时间。假设我们每台机器每秒可以执行一百万次乘法,并且我们可以将工作以某种方式并行化到多台机器上。

(10^6)! takes order of one second on one machine
(10^12)! takes order of 10^6 seconds on one machine = 
                     10 days on one machine = 
                     a few minutes on a thousand machines.
(10^21)! takes order of 10^15 seconds on one machine = 
                        30 million years on one machine =
                        3 years on 10 million machines
                        1 day on 10 billion machines (each with a TB drive.)

所以(10 ^ 6)!在你的掌握之中。 (10 ^ 12)!你将不得不编写自己的内存管理器和数学库,这需要一些时间来得到答案。 (10 ^ 21)!你将需要组织世界上所有的资源来解决这个问题,但它是可行的。

或者你可以找到另一种方法。

答案 1 :(得分:4)

解决方案很简单:在不使用递归的情况下计算阶乘,并且不会炸掉堆栈。

即。您没有收到此错误,因为数字太大,但是因为您有太多级别的函数调用。幸运的是,对于阶乘,没有理由以递归方式计算它们。

一旦解决了堆栈问题,您就可以担心数字格式是否可以处理“非常大”的因子。由于您不需要确切的值,因此请使用许多有效的数值近似值之一(您可以依靠它来获得所有最重要的数字)。最常见的是斯特林的近似值

enter image description here

n! ~ n^n e^{-n} sqrt(2 \pi n)

图像来自this page,,您可以在那里找到讨论,第二个更准确的公式(尽管“在大多数情况下,差异非常小”,他们说)。当然这个数字对于你来说仍然太大而无法存储,但现在你可以使用对数并在提取数字之前删除不重要的数字。或者使用近似值的Wikipedia version,它已经表示为对数。

答案 2 :(得分:1)

展开递归:

static BigInteger factorial(BigInteger n)
{
    BigInteger res = 1;
    for (BigInteger i = 2; i <= n; ++i)
        res *= i;
    return res;
}