花费太多时间处理大量数字
Posted
技术标签:
【中文标题】花费太多时间处理大量数字【英文标题】:taking too much time for large numbers 【发布时间】:2013-08-20 15:03:25 【问题描述】:一个非负整数 N 的阶乘,用 N! 表示,是所有小于和等于 N 的正整数的乘积。任何数的阶乘都可以用其素因数的最简单形式表示。例如4!=4*3*2*1= (2^3)*(3^1) 阶乘也可以用每个素因数出现的次数来指定,因此24可以指定为(3 1)的意思3个二,1个三。 我编写的代码给出了小数字的答案,但是当数字变大时,程序不会返回答案
#include<stdio.h>
long fact(long n)
if(n==0)
return 1;
else
return(n*fact(n-1));
int main()
long a[10]=0;
long n,l=0,count=0,i,j,flag;
if(!scanf("%ld",&n))
printf("Invalid input");
goto l1;
if(n<0)
printf("Invalid input");
goto l1;
n=fact(n);
while(n%2==0)
count++;
n=n/2;
a[l]=count;
l++;
for(i=2;i<=n;i++)
count=0;
j=2;
flag=0;
while(j<i)
if(i%j==0)
flag=0;
break;
else
flag=1;
j++;
if(flag==1)
count=0;
while(n%i==0)
count++;
n=n/i;
a[l]=count;
l++;
for(i=0;i<l;i++)
printf("%ld ",a[i]);
l1:return 0;
【问题讨论】:
那是因为您使用递归解决方案来计算阶乘,这使得调用堆栈不断增长,直到它用完非常大的数字的调用堆栈。尝试使用迭代版本来计算而不是递归形式。示例: unsigned int iter_factorial(int n) int f = 1;诠释我; for(i = 1; i 感谢您的回复。但我也尝试过不使用递归,但同样的问题仍然存在 您可能会导致 long 类型的溢出:100!=9.332622e+157 unsigned long 的最大值:2^64-1 您可能会使用浮点数获得近似值... Don't use Goto! 递归调用不是问题。整数溢出是问题所在。 【参考方案1】:你不能存储 n!值,即使在结果增长极快的情况下也是如此。您应该修改您的算法:计算从2
到n
的每个数字的素因子分解,并递归添加 n-1、n-2 的分解...
例如,假设您使用 10! 进行此操作。 (省略1
)
2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10.
现在,主要因素每一个。
2 : 2
3 : 3
4 : 2 * 2
5 : 5
6 : 2 * 3
7 : 7
8 : 2 * 2 * 2
9 : 3 * 3
10 : 2 * 5
这意味着10!
等价于:
2 * 3 * 2 * 2 * 5 * 2 * 3 * 7 * 2 * 2 * 2 * 3 * 3 * 2 * 5
从上面的项中累积每个素数的计数,我们有以下,^
表示求幂,而不是 XOR 的 C 运算符)
2^8 * 3^4 * 5^2 * 7^1
所以答案是(8 4 2 1)
。检查我们的工作,我们得到:
2^8 = 256
3^4 = 81
5^2 = 25
7^1 = 7
7 * 25 * 81 * 256 = 3628800
我的计算器告诉我...
10! = 3628800
所以算法是正确的。最后请注意,我们在任何时候都不需要对任何大于 n
的数字进行质因数分解,在本例中为 10
。
注意:避免goto
s
【讨论】:
为清晰起见进行了编辑,包括示例。【参考方案2】:问题在于这一行与溢出相结合:
for(i=2;i<=n;i++)
只要 n 可以分解成大量的小素数,这很好用,条件阶乘满足。没问题。当您的阶乘不再表示为 long 时,问题就开始了。那么结果将是 long 值范围内的某个其他数字,它可能具有非常大的素数因子,或者本身就是素数。
现在我假设,你在一个 long 为 64 位的平台上工作(你可以通过打印sizeof(long)
的值来检查),所以这些大素数可以在 10^18 的范围内。即使是最现代、最快的硬件也无法在你的一生中循环遍历所有这些数字。因此,当您仍在等待时,该循环不会终止。
唯一的出路是确保您的计算不会溢出。您可以在每次乘法后检查结果除以一个因子是否产生另一个因子。如果没有,你有一个溢出,应该退出并出现错误。
【讨论】:
这个问题不需要计算大的阶乘,当然也不需要计算大数的素因数。问题是为所述阶乘的素数分解生成一个指数列表。给出的例子,4! == 24 == (3 1),表示 2^3 * 3^1,您不需要计算或分解大的阶乘来完成此操作(实际上,这也是问题本身)。根据定义,阶乘将由素数和非素数(可以因式分解为素数)组成。 我明确指出,大素数只能由于阶乘计算中的溢出而发生;一世。 e.他试图分解的数字根本不是阶乘。因此可能的运行时间很长。如果你使用unsigned char
作为类型,6!将是 208,它的素数分解为 2^4 * 13。而且 13 显然比 6 大得多。
我同意,并投了赞成票。总的来说,我们都同意他的基本问题是他选择的算法(至少听起来是这样)。对于n!
问题,无需考虑大于n
的数字就可以轻松做到这一点,听起来我们都同意这一点。找到 100,000 的素数指数列表!在任何体面的系统上应该可以在几秒钟内完成。【参考方案3】:
没有有效的大数分解算法。 虽然可能不是您预期的答案,但请查看***: http://en.wikipedia.org/wiki/Integer_factorization
【讨论】:
他的算法对阶乘有效,因为n!
没有大于n
的素数。【参考方案4】:
您的方法有两个主要障碍:
long
,甚至unsigned long
都不足以容纳大多数数字的阶乘。 long
通常存储 32 位(尽管这可能因架构而异),这使我们能够存储小于 2**32 - 1
的数字,大约为 40 亿。 13!已经比那个大了。
当编译器不进行尾递归优化时,由于在调用堆栈中为函数分配空间的开销,递归调用过多会严重影响性能。
更好的解决方案是使用大整数(可以增长到任意大小)或使用双精度浮点数并使用迭代解决方案来找到近似解决方案。
另一方面:在 C 中使用 goto
是不受欢迎的。请改用if, else, for, while, switch
。把这种想法留给汇编编程吧。
【讨论】:
第二点是静音,因为你可以很快达到溢出。一个函数调用需要 10 到 20 个 CPU 周期,他可以计算的任何阶乘都不会超过一微秒。以上是关于花费太多时间处理大量数字的主要内容,如果未能解决你的问题,请参考以下文章
Pandas 在处理 Excel 文件时花费的时间太长且占用的内存太多
处理存储在 RDD [String] 中的记录时,spark collect 方法花费了太多时间