问题 计算机如何评估大量数字?


如果我在Wolfram Alpha中输入一个值,例如1234567 ^ 98787878,它可以为我提供一些细节。这包括十进制近似,总长度,最后一位数等。您如何评估这么大的数字?据我所知,编程语言必须有一个特殊的数据类型才能存储数字,更不用说将它添加到其他东西了。虽然我可以看到人们如何接近两个非常大的数字,我看不出有多大的数字被评估。

可以通过重复添加来计算10 ^ 2。然而,诸如上述示例之类的数字将需要巨大的循环。有人可以解释如何评估这么大的数字吗?另外,有人如何创建自定义大型数据类型以支持C#中的大数字?


7423
2017-08-20 11:34


起源



答案:


嗯,这很容易,你可以自己完成

  1. 位数 可以通过获得 对数

    以来 A^B = 10 ^ (B * log(A, 10)) 

    我们可以计算 (A = 1234567; B = 98787878) 在我们的情况下

    B * log(A, 10)  =  98787878 * log(1234567, 10)  =  601767807.4709646...

    integer part + 1  (601767807 + 1 = 601767808 是位数

  2. 第一比方说 数字 可以通过 对数 以及;  现在我们应该分析一小部分

    B * log(A, 10)  =  98787878 * log(1234567, 10)  =  601767807.4709646...

    f = 0.4709646...

    第一个数字是 10^f (删除小数点)= 29577...

  3. 持续比方说 数字 可以作为相应的获得

    最后五位= A^B rem 10^5

    A rem 10^5  =  1234567 rem 10^5  = `34567

    A ^ B rem 10 ^ 5**=**((A rem 10 ^ 5)^ B)rem 10 ^ 5**=** (34567 ^ 98787878)rem 10 ^ 5=45009`

    最后五位数 45009

    你可能会发现 BigInteger.ModPow (C#)在这里非常有用

最后

1234567 ^ 98787878 = 29577 ... 45009(601767808位数)


11
2017-08-20 12:27



很好的答案!我完全没有得到这个部分 - A rem 10^5 = ((A rem 10^5)^B) rem 10^5。怎么解释? - Sundeep
@Sundeep:似乎应该有一个换行符:我先计算(A rem 10 ^ 5)然后计算((A ^ B)rem 10 ^ 5)。我编辑了这篇文章。 - Dmitry Bychenko


答案:


嗯,这很容易,你可以自己完成

  1. 位数 可以通过获得 对数

    以来 A^B = 10 ^ (B * log(A, 10)) 

    我们可以计算 (A = 1234567; B = 98787878) 在我们的情况下

    B * log(A, 10)  =  98787878 * log(1234567, 10)  =  601767807.4709646...

    integer part + 1  (601767807 + 1 = 601767808 是位数

  2. 第一比方说 数字 可以通过 对数 以及;  现在我们应该分析一小部分

    B * log(A, 10)  =  98787878 * log(1234567, 10)  =  601767807.4709646...

    f = 0.4709646...

    第一个数字是 10^f (删除小数点)= 29577...

  3. 持续比方说 数字 可以作为相应的获得

    最后五位= A^B rem 10^5

    A rem 10^5  =  1234567 rem 10^5  = `34567

    A ^ B rem 10 ^ 5**=**((A rem 10 ^ 5)^ B)rem 10 ^ 5**=** (34567 ^ 98787878)rem 10 ^ 5=45009`

    最后五位数 45009

    你可能会发现 BigInteger.ModPow (C#)在这里非常有用

最后

1234567 ^ 98787878 = 29577 ... 45009(601767808位数)


11
2017-08-20 12:27



很好的答案!我完全没有得到这个部分 - A rem 10^5 = ((A rem 10^5)^B) rem 10^5。怎么解释? - Sundeep
@Sundeep:似乎应该有一个换行符:我先计算(A rem 10 ^ 5)然后计算((A ^ B)rem 10 ^ 5)。我编辑了这篇文章。 - Dmitry Bychenko


通常存在为任意大整数提供bignum数据类型的库(例如映射数字) k*n...(k+1)*n-1, k=0..<some m depending on n and number magnitude> 一个大小的机器字 n 重新定义算术运算)。对于c#,您可能感兴趣 的BigInteger

取幂可以递归分解:

pow(a,2*b)   = pow(a,b) * pow(a,b);
pow(a,2*b+1) = pow(a,b) * pow(a,b) * a;

还有一些数论结果已经产生了特殊算法来确定大数的性质而不实际计算它们(确切地说:它们的完整十进制扩展)。


3
2017-08-20 11:40



那么这是否意味着计算大量指数没有花哨的捷径?我假设Wolfram Alpha必须有一个庞大的分布式系统,仅用于计算大数? - Sam
@Sam是和否。在某些特定情况下有一些快捷方式,但一般情况下你必须进行完全乘法,而Wolfram Alpha可能有大型数据中心来回答查询。但是,除了计算大数字之外,其中大部分都会做其他事情,而你的例子,1234567 ^ 98787878,只需要二十二个bignum乘法,但如果完全评估,它确实会产生~150兆字节。一台计算机可以在合理的时间内完成这项工作,“巨大的分布式”部分只会进入图片,因为有数千人同时运行这些查询。
给定的算法 是 一个奇特的捷径,因为它允许重用中期结果。 - collapsar
@collapsar这是一个更有效的算法,但我不会称之为快捷方式。例如,一个快捷方式是采用OP表达式并为您提供最后几位而不计算数百万其他数字的东西。
@delnan:点了,它更适合作为优化。 - collapsar


要计算有多少位数,可以使用以下表达式:

decimal_digits(n) = 1 + floor(log_10(n))

这给出了:

decimal_digits(1234567^98787878) = 1 + floor(log_10(1234567^98787878))
                                 = 1 + floor(98787878 * log_10(1234567))
                                 = 1 + floor(98787878 * 6.0915146640862625)
                                 = 1 + floor(601767807.4709647)
                                 = 601767808

通过进行指数mod 10 ^ k来计算尾随k个数字,这使得中间结果不会变得太大。

将使用(软件)浮点实现来计算近似值,该实现有效地将^(98787878 log_a(1234567))评估为某个数字a的某个固定精度,这使得算法很好地工作(通常为2或e或10)。这也避免了在任何时候实际使用数百万个数字的需要。


3
2017-08-20 11:43





这有很多库,并且在python的情况下内置了该功能。您似乎主要关注此类数字的大小以及执行计算所需的时间,例如示例中的指数。所以我会解释一下。

表示 您可以使用数组来保存大数字的所有数字。更有效的方法是使用32位无符号整数数组并存储大数字的“32位块”。您可以将这些块视为具有2 ^ 32个不同数字或字符的数字系统中的单个数字。我在当天的8位Atari800上使用了一个字节数组来执行此操作。

做数学 显然,您可以通过循环所有数字并将一个数组的元素添加到另一个数组并跟踪进位来添加两个这样的数字。一旦你知道如何添加,你就可以编写代码来通过乘以数字进行“手动”乘法并将结果放在正确的位置并添加很多 - 但软件可以很快地完成所有这些操作。比在纸上手动使用的算法还要快。纸张乘法是O(n ^ 2),其他方法是O(n * log(n))。至于指数,你当然可以乘以相同的数字数百万次,但这些乘法中的每一次都将使用前面提到的乘法函数。有更快的方法可以进行求幂,这需要更少的乘法。例如,您可以通过计算(((x ^ 2)^ 2)^ 2)^ 2来计算x ^ 16,其仅涉及4个实际(大整数)乘法。

在实践中 尝试自己编写这些函数很有趣,也很有教育意义,但在实践中,您需要使用已经过优化和验证的现有库。


0
2017-08-22 17:42





我认为答案的一部分在于问题本身:)要存储这些表达式,您可以单独存储基数(或尾数)和指数,就像科学记数法一样。扩展到那个,你不可能完全评估表达式并存储这么大的数字,但是,理论上你可以预测结果表达式的某些属性。我将带您了解您谈到的每个属性:

  1. 十进制近似:可以通过评估简单的对数值来计算。
  2. 表达式a ^ b的总位数可以通过公式计算 数字=地板函数(1 + Log10(a ^ b)),其中floor函数是小于数字的最接近的整数。对于例如10 ^ 5中的位数是6。
  3. 最后的数字:这些可以通过线性增加的指数的表达形成算术级数的事实来计算。对于例如在单位;对于7 ^ x的指数重复7,9,3,1。因此,您可以计算出如果x%4为0,则最后一位为1。 有人可以为大数字创建自定义数据类型,我不能说,但我相信,这个数字不会被评估和存储。

-1
2017-08-20 11:55



实际上,这些大整数可以使用bignum库存储(通常是)整数形式。有很多这样的工具。
@woodchips - 是的,你是对的,我高估了这个数字。精度库只能容纳最大缓冲区的大小。如果使用数字位数(以及指数为2的指数进行计算),则需要大约40MB来存储它。 - Sahil M