花费太多时间处理大量数字

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!值,即使在结果增长极快的情况下也是如此。您应该修改您的算法:计算从2n 的每个数字的素因子分解,并递归添加 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

注意:避免gotos

【讨论】:

为清晰起见进行了编辑,包括示例。【参考方案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 文件时花费的时间太长且占用的内存太多

查询Hive处理的大量数据

处理存储在 RDD [String] 中的记录时,spark collect 方法花费了太多时间

Node.js Express 应用程序在负载测试下花费太多时间来处理出站 HTTP 请求

处理Solr中的大量ID