计算100阶乘的数字总和

编辑 – 更改标题以匹配实际问题陈述。

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

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

  2. 我添加结果数字的数字的方法不会输出正确的结果。

这是我目前的代码:

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。

感谢任何帮助,谢谢。

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

 #include  #include  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!的确切值100! 你需要使用数字数组(也就是bignums )来进行计算。 您可以找到要使用的bignum库,也可以自己实现bignum乘法。 关于bignums的维基百科文章提供了用于计算阶乘的伪代码 。

 #include  #include  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 

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

这是事实函数的OpenSSL版本(几乎所有发行版都使用gcc main.c -lcrypto编译)。 您只需要包含

 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生成此数字的字符串表示,然后您可以使用它来计算数字的总和。

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

使用mod非常简单,只需查看产品中的数字,修改它们并将mods相乘即可。 例如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一起添加(乘法只是重复加法)。