计算100阶乘中的数字总和

时间:2015-01-19 20:20:30

标签: c factorial

编辑 - 更改了标题以匹配实际的问题陈述。

我编写了一个计算100中数字总和的函数!但我似乎有两个大问题。

  1. 100的实际结果!仅对前几个数字准确(实际结果为933262154439441526816992388562667004907179699643816214685929638976155993229915608948273976156518286253697920827223758251185210916864000000000000000000000000)

  2. 我将结果数字的数字相加的方法并没有输出正确的结果。

  3. 这是我目前的代码:

    void factorialSum()
    {
        double fact100 = factorial(100);
        double suma = 0;
    
        printf("100! is equal to: %.0f", fact100);
    
        while (fact100 > 0)
        {
            double temporal = fmod(fact100, 10);
            suma = suma + temporal;
            fact100 = fact100/10;
        }
        printf("\nThe sum of all digits in 100! is: %.0f", suma);
    }
    

    函数factorial()定义为:

    double factorial (double n)
    {
        double mult = 1;
        double i = n;
    
        while (i>=1)
        {
            mult *= i;
            i = i - 1;
        }
        return mult;
    }
    

    该程序输出93326215443944102188325606108575267240944254854960571509166910400407995064242937148632694030450512898042989296944474898258737204311236641477561877016501813248作为100的结果!并说它的数字总和等于666。

    感谢任何帮助,谢谢。

4 个答案:

答案 0 :(得分:3)

在C中,double通常具有53位精度,对应于16或17位精度。因此,一旦超出22!double就无法再代表确切的结果,如下面的代码所示。请注意,在23!处,尾随零消失,因为double不再代表确切的值。

#include <stdio.h>
#include <stdint.h>

int main( void )
{
    double y;

    y = 1;
    for ( int i = 2; i < 30; i++ )
    {
        y *= i;
        printf( "%2d %32.0lf\n", i, y );
    }
}

这是程序的输出

 2                                2
 3                                6
 4                               24
 5                              120
 6                              720
 7                             5040
 8                            40320
 9                           362880
10                          3628800
11                         39916800
12                        479001600
13                       6227020800
14                      87178291200
15                    1307674368000
16                   20922789888000
17                  355687428096000
18                 6402373705728000
19               121645100408832000
20              2432902008176640000
21             51090942171709440000
22           1124000727777607680000
23          25852016738884978212864
24         620448401733239409999872
25       15511210043330986055303168
26      403291461126605650322784256
27    10888869450418351940239884288
28   304888344611713836734530715648
29  8841761993739700772720181510144

如果要计算100!的确切值,则需要使用数字数组(又名bignums)来进行计算。您可以找到要使用的bignum库,也可以自己实现bignum乘法。关于bignums的维基百科文章提供了pseudocode来计算阶乘。

答案 1 :(得分:2)

#include <stdio.h>
#include <stdlib.h>

typedef unsigned char byte;

int main(){
    size_t size =16;//initial size
    size_t temp, len;
    byte *n = malloc(size);
    byte i, carry = 0;
    *n = 1;
    len = 1;

    for(i=2;i<=100; ++i){
        size_t k;
        for(k = 0; k < len; ++k){
            if(k == size){
                n = realloc(n, size*=2);//expand, check omitted
            }
            temp = n[k] * i + carry;//Calculation is performed on the promoted to int
            carry = (temp >= 10) ? temp / 10 : 0;//or simply temp/10
            n[k] = temp % 10;
        }
        while(carry){
            if(k == size){
                n = realloc(n, size*=2);
            }
            temp = carry;
            carry = (temp >= 10) ? temp / 10 : 0;
            n[k++] = temp % 10;
            ++len;
        }
    }
    temp = 0;
    while(len){
        printf("%u", n[--len]);
        temp += n[len];
    }
    puts("");
    printf("sum=%zu\n", temp);
    free(n);

    return 0;
}
#if 0
anticipate in advance the required size
100! = 1*2*...99*100
size > (log(1)+log(2)+...log(99)+log(100))/log(10)
(∫1->100 log(x)dx)/log(10)
f(x)=log(x) => F(x)=x(log(x)-1)+C
(∫1->101 log(x)dx)/log(10) = (101*log(101)-101+1)/log(10) ≒ 159.00..
160 digits are sufficient.
http://en.wikipedia.org/wiki/Stirling%27s_approximation
log10(n!)≒log10(√(2nπ)(n/e)^n)=log10(√(2nπ))+nlog10(n/e)=157.9696..
size=158
#endif

答案 2 :(得分:1)

正如其他人所提到的,您可以为此编写自己的bignum库(使用字节数组),也可以使用类似OpenSSL的BIGNUM实现。

这是事实函数的OpenSSL版本(几乎在任何发行版上都使用gcc main.c -lcrypto编译)。您只需要包含<openssl/bn.h>

BIGNUM* fact (unsigned long n, BN_CTX* ctx) {

    BIGNUM *mult, *i, *one;

    mult = BN_new();
    i = BN_new();
    one = BN_new();

    BN_one(one);

    BN_set_word(mult, 1L);
    BN_set_word(i, n);


    while (BN_cmp(i, one) >= 0) {
        BIGNUM *a, *b;

        a = BN_new();
        b = BN_new();

        BN_mul(a, i, mult, ctx);

        BN_sub(b, i, one);

        BN_free(mult);
        BN_free(i);
        mult = a;
        i = b;
    }

    BN_free(one);
    BN_free(i);

    return mult;
}

然后,您可以使用BN_bn2bin生成此数字的字符串表示形式,然后您可以使用它来计算数字的总和。

答案 3 :(得分:0)

这可能会有所帮助。假设你有一个像2356这样的数字。你怎么能添加它的数字。好吧,你可以提取最低有效数字,将其添加到结果并将其丢弃(右移)。你通过数字模式10提取最小数字。你移动除以10.所以2356 mod 10 = 6和2356/10 = 235。

使用mod非常简单,只需查看产品中的数字,修改它们并将这些数字相乘即可。例如12 * 6 mod 10 =(12 mod 10)*(6 mod 10)= 2 * 6 = 12然后在最后采用最后一个mod:12 mod 10 = 2.如果你要乘以12 * 6你将获得72,等于2 mod 10.

硬部分除以10.现在,如果产品包含10的倍数(例如100),则将该数字除以10即可完成。如果不存在这样的数字,你仍然可以除以10,但这会搞砸我猜的结果。因此,如果您可以找到如何执行此操作,则可以在不实施BIGNUM的情况下解决问题。如果没有,那么它也不是那么难,你只需要实现一个函数来将两个BIGNUM一起添加(乘法只是重复添加)。