找回密码
 欢迎注册
楼主: medie2005

[讨论] 阶乘和圆周率

  [复制链接]
发表于 2008-12-20 22:15:42 | 显示全部楼层
可以发现上面几个例子的结果是两个不同的输出 测试发现正确的输出是 149832529!!!! 但计算器计算发现,似乎此时的对数值精度并不高
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-12-20 22:23:15 | 显示全部楼层
重新修改成
  1. #include <gmp.h>
  2. #include <mpfr.h>
  3. #include <stdio.h>
  4. #include <math.h>
  5. #include <sys/time.h>
  6. #define DefaultBitLength 128
  7. mpfr_t ln10, cA, cT;
  8. //=1/2ln(2π)+(n+1/2)ln(n)-n+1/12n
  9. //n > 10^8 误差<1/360Z^3 = 10^(-26)
  10. long double stirling(double n)
  11. {
  12. mpfr_t a, b, c, s;
  13. long double r;
  14. mpfr_init2(a, DefaultBitLength);
  15. mpfr_init2(b, DefaultBitLength);
  16. mpfr_set_d(a, n, GMP_RNDD); //a=n
  17. mpfr_init2(c, DefaultBitLength);
  18. mpfr_init2(s, DefaultBitLength);
  19. mpfr_div(s, cT, a, GMP_RNDD); //s = 1/12a = 1/12n
  20. mpfr_sub(s, s, a, GMP_RNDD); //s = s - a = - n + 1/12n
  21. mpfr_log(c, a, GMP_RNDD); //c = ln(n)
  22. mpfr_set_d(b, 0.5, GMP_RNDD);
  23. mpfr_add(b, a, b, GMP_RNDD);
  24. mpfr_mul(b, b, c, GMP_RNDD); //b = (n + 1/2)ln(n)
  25. mpfr_add(s, s, b, GMP_RNDD); //s = (n + 1/2)ln(n) - n + 1/12n
  26. mpfr_add(s, s, cA, GMP_RNDD); //最终结果s=1/2ln(2*pi) + (n + 1/2)ln(n) - n + 1/12n
  27. mpfr_mul(s, s, ln10, GMP_RNDD); //转换为log10
  28. mpfr_floor(b, s);
  29. mpfr_sub(s, s, b, GMP_RNDD); //只要小数
  30. r = mpfr_get_ld(s, GMP_RNDD);
  31. mpfr_clear(a);
  32. mpfr_clear(b);
  33. mpfr_clear(c);
  34. mpfr_clear(s);
  35. return r;
  36. }
  37. int main(void)
  38. {
  39. double a, b, low, hi, lf, t;
  40. double start, used_time, LT2;
  41. int find = 0, test1 = 0;
  42. long i = 0, j = 0;
  43. struct timeval start_time, end_time;
  44. mpfr_init2(ln10, DefaultBitLength);
  45. mpfr_init2(cA, DefaultBitLength);
  46. mpfr_init2(cT, DefaultBitLength);
  47. mpfr_set_ui(ln10, 10, GMP_RNDD);
  48. mpfr_set_ui(cT, 1, GMP_RNDD);
  49. mpfr_log(ln10, ln10, GMP_RNDD);
  50. mpfr_div(ln10, cT, ln10, GMP_RNDD);
  51. mpfr_const_pi(cA, GMP_RNDD);
  52. mpfr_mul_ui(cA, cA, 2, GMP_RNDD);
  53. mpfr_log(cA, cA, GMP_RNDD);
  54. mpfr_div_ui(cA, cA, 2, GMP_RNDD);
  55. mpfr_div_ui(cT, cT, 12, GMP_RNDD);
  56. printf("请输入起始搜索数字:");
  57. scanf("%lf", &start);
  58. printf("请输入上界: ");
  59. scanf("%lf", &hi);
  60. printf("请输入下界: ");
  61. scanf("%lf", &low);
  62. LT2 = log10(2);
  63. hi = log10(hi);
  64. low = log10(low);
  65. printf("起始位置:%.0f\n当前界限[%.17f, %.17f]\n", start, low, hi);
  66. a = start;
  67. b = 1.0;
  68. while (a >= 10.0)
  69. {
  70. a /= 10.0;
  71. b *= 10.0; ///////////////////////////////////
  72. }
  73. lf = 2.0;
  74. gettimeofday(&start_time, NULL);
  75. for (i = 0; i < 1000; i ++)
  76. lf += stirling(lf + 100000000.0);
  77. gettimeofday(&end_time, NULL);
  78. used_time = ((end_time.tv_sec - start_time.tv_sec)*1000000.0 + (end_time.tv_usec - start_time.tv_usec)) / 1000.0;
  79. printf("高精度stirling函数执行时间(毫秒): %8.6lf, 测试值:%.8lf\n", used_time, lf);
  80. lf = stirling(start);
  81. printf("起始对数: %.17f\n", lf);
  82. gettimeofday(&start_time, NULL);
  83. while (1)
  84. {
  85. start += 1.0;
  86. a = log2(start/b) * LT2; ///////////////////////////
  87. if (a >= 1.0)
  88. {
  89. b *= 10.0; ////////////////////////////////
  90. a -= 1.0;
  91. }
  92. i ++;
  93. lf += a;
  94. if (lf >= 1.0)
  95. lf -= 1.0;
  96. // printf("[%.0f, %.15f] ", start, lf);
  97. if ((lf >= low) && (lf < hi))
  98. {
  99. find = 1;
  100. break;
  101. }
  102. if (i >= 1000)
  103. {
  104. // lf = stirling(start);
  105. i = 0;
  106. j ++;
  107. if (j >= 1000)
  108. {
  109. if (test1 == 0)
  110. {
  111. gettimeofday(&end_time, NULL);
  112. used_time = ((end_time.tv_sec - start_time.tv_sec)*1000000.0 + (end_time.tv_usec - start_time.tv_usec))/1000000.0;
  113. printf("每10^6次查找执行时间(秒): %.6lf\n", used_time);
  114. test1 = 1;
  115. }
  116. }
  117. }
  118. }
  119. if (find)
  120. {
  121. printf("\n当前界限下的阶乘%.0f!, 对应的对数%.15f", start, lf);
  122. }
  123. mpfr_clear(ln10);
  124. mpfr_clear(cA);
  125. mpfr_clear(cT);
  126. return 1;
  127. }
复制代码
编译命令 gcc factPi1.c -o factpi1.exe -lmpfr -lgmp -lm ====================================================== 请输入起始搜索数字:100000000 请输入上界: 3.1415927 请输入下界: 3.1415926 起始位置:100000000 当前界限[0.49714986528586863, 0.49714987910989134] 高精度stirling函数执行时间(毫秒): 30.043000, 测试值:493.74586181 起始对数: 0.20876475177585671 每10^6次查找执行时间(秒): 0.300432 当前界限下的阶乘149832529!, 对应的对数0.497149873638380 ============================================ 计算器得到的值 0.4971498735933375434005 ============================================ 直接使用10的对数 其他条件不变的输出 即使用 a = log10(start/b);// * LT2;取代a = log2(start / b) * LT2; ============================================ 请输入起始搜索数字:100000000 请输入上界: 3.1415927 请输入下界: 3.1415926 起始位置:100000000 当前界限[0.49714986528586863, 0.49714987910989134] 高精度stirling函数执行时间(毫秒): 30.044000, 测试值:493.74586181 起始对数: 0.20876475177585671 每10^6次查找执行时间(秒): 0.450648 当前界限下的阶乘149832529!, 对应的对数0.497149873595150 =========================================== 精度大些,但消耗时间多了 即使使用log10(2)的高精度值直接乘而不是用对数函数 也不能增大精度,所以似乎库函数存在某些优化????
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-12-20 22:44:22 | 显示全部楼层
程序可以确定为62#程序但 使用a = log10(start/b);// * LT2;取代a = log2(start / b) * LT2; 此时 不同的优化结果如下 ========================================= 不使用任何优化 每10^6次查找执行时间(秒): 0.470677 当前界限下的阶乘149832529!, 对应的对数0.4971 4987 3595 150 ========================================= 带-O2 每10^6次查找执行时间(秒): 0.450648 当前界限下的阶乘149832529!, 对应的对数0.4971 4987 3593 083 =========================================== 带-O2 -ffast-math 每10^6次查找执行时间(秒): 0.160230 当前界限下的阶乘149832529!, 对应的对数0.4971 4987 3593 496 =========================================== 对比计算器得到的数值0.4971 4987 3593 3375 4340 05 ========================================== 反而是最后一种情况又快, 精度又高 呵呵
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-12-21 21:37:29 | 显示全部楼层
修改了代码 使用gcc -O2 -ffast-math -march=pentium4 factpi1.c -o factpi1.exe -lmpfr -lgmp
  1. #include <gmp.h>
  2. #include <mpfr.h>
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <math.h>
  6. #include <sys/time.h>
  7. #define DefaultBitLength 128
  8. #define TEST
  9. mpfr_t ln10, cA, cT;
  10. FILE * fp;
  11. //=1/2ln(2π)+(n+1/2)ln(n)-n+1/12n
  12. //n > 10^8 误差<1/360Z^3 = 10^(-26)
  13. long double stirling(double n)
  14. {
  15. mpfr_t a, b, c, s;
  16. long double r;
  17. mpfr_init2(a, DefaultBitLength);
  18. mpfr_init2(b, DefaultBitLength);
  19. mpfr_set_d(a, n, GMP_RNDD); //a=n
  20. mpfr_init2(c, DefaultBitLength);
  21. mpfr_init2(s, DefaultBitLength);
  22. mpfr_div(s, cT, a, GMP_RNDD); //s = 1/12a = 1/12n
  23. mpfr_sub(s, s, a, GMP_RNDD); //s = s - a = - n + 1/12n
  24. mpfr_log(c, a, GMP_RNDD); //c = ln(n)
  25. mpfr_set_d(b, 0.5, GMP_RNDD);
  26. mpfr_add(b, a, b, GMP_RNDD);
  27. mpfr_mul(b, b, c, GMP_RNDD); //b = (n + 1/2)ln(n)
  28. mpfr_add(s, s, b, GMP_RNDD); //s = (n + 1/2)ln(n) - n + 1/12n
  29. mpfr_add(s, s, cA, GMP_RNDD); //最终结果s=1/2ln(2*pi) + (n + 1/2)ln(n) - n + 1/12n
  30. mpfr_mul(s, s, ln10, GMP_RNDD); //转换为log10
  31. mpfr_floor(b, s);
  32. mpfr_sub(s, s, b, GMP_RNDD); //只要小数
  33. r = mpfr_get_ld(s, GMP_RNDD);
  34. mpfr_clear(a);
  35. mpfr_clear(b);
  36. mpfr_clear(c);
  37. mpfr_clear(s);
  38. return r;
  39. }
  40. int main(void)
  41. {
  42. double a, b, low, hi, lf, t;
  43. double start, used_time;
  44. int find = 0, test1 = 0;
  45. long i, j = 0;
  46. struct timeval start_time, end_time;
  47. mpfr_init2(ln10, DefaultBitLength);
  48. mpfr_init2(cA, DefaultBitLength);
  49. mpfr_init2(cT, DefaultBitLength);
  50. mpfr_set_ui(ln10, 10, GMP_RNDD);
  51. mpfr_set_ui(cT, 1, GMP_RNDD);
  52. mpfr_log(ln10, ln10, GMP_RNDD);
  53. mpfr_div(ln10, cT, ln10, GMP_RNDD);
  54. mpfr_const_pi(cA, GMP_RNDD);
  55. mpfr_mul_ui(cA, cA, 2, GMP_RNDD);
  56. mpfr_log(cA, cA, GMP_RNDD);
  57. mpfr_div_ui(cA, cA, 2, GMP_RNDD);
  58. mpfr_div_ui(cT, cT, 12, GMP_RNDD);
  59. printf("请输入起始搜索数字:");
  60. scanf("%lf", &start);
  61. printf("请输入上界: ");
  62. scanf("%lf", &hi);
  63. printf("请输入下界: ");
  64. scanf("%lf", &low);
  65. fp = fopen("factpi.log", "a+t");
  66. printf("起始位置:%.0f\n当前界限[%.17f, %.17f]\n", start, low, hi);
  67. fprintf(fp, "\n///////////////////////////////////////////////////////////////\n");
  68. fprintf(fp, "起始位置:%.0f\n当前界限[%.17f, %.17f]\n", start, low, hi);
  69. hi = log10(hi);
  70. low = log10(low);
  71. printf("当前对数界限[%.17f, %.17f]\n", low, hi);
  72. fprintf(fp, "当前对数界限[%.17f, %.17f]\n", low, hi);
  73. a = start;
  74. b = 1.0;
  75. while (a >= 10.0)
  76. {
  77. a /= 10.0;
  78. b *= 10.0;
  79. }
  80. lf = 2.0;
  81. gettimeofday(&start_time, NULL);
  82. for (i = 0; i < 1000; i ++)
  83. lf += stirling(lf + 100000000.0);
  84. gettimeofday(&end_time, NULL);
  85. used_time = ((end_time.tv_sec - start_time.tv_sec)*1000000.0 + (end_time.tv_usec - start_time.tv_usec)) / 1000.0;
  86. printf("高精度stirling函数执行时间(毫秒): %8.6lf, 测试值:%.8lf\n", used_time, lf);
  87. fprintf(fp, "高精度stirling函数执行时间(毫秒): %8.6lf, 测试值:%.8lf\n", used_time, lf);
  88. lf = stirling(start);
  89. printf("起始对数: %.17f\n", lf);
  90. fprintf(fp, "起始对数: %.17f\n", lf);
  91. fflush(fp);
  92. gettimeofday(&start_time, NULL);
  93. i = 0;
  94. while (1)
  95. {
  96. if ((a = log10((start += 1.0)/b)) >= 1.0)
  97. {
  98. b *= 10.0;
  99. a -= 1.0;
  100. }
  101. if ((lf += a) >= 1.0)
  102. lf -= 1.0;
  103. // printf("[%.0f, %.15f] ", start, lf);
  104. if ((lf >= low) && (lf < hi))
  105. {
  106. find = 1;
  107. break;
  108. }
  109. if (( ++i) >= 1000)
  110. {
  111. lf = stirling(start);
  112. i = 0;
  113. j ++;
  114. #ifdef TEST
  115. if (j >= 1000)
  116. {
  117. if (test1 == 0)
  118. {
  119. gettimeofday(&end_time, NULL);
  120. used_time = ((end_time.tv_sec - start_time.tv_sec)*1000000.0 + (end_time.tv_usec - start_time.tv_usec))/1000000.0;
  121. printf("每10^6次查找执行时间(秒): %.6lf\n", used_time);
  122. test1 = 1;
  123. }
  124. }
  125. #else
  126. if (j >= 100000000)
  127. {
  128. j = 0;
  129. fprintf(fp, "当前搜索数值: %.0f\n", start);
  130. fflush(fp);
  131. }
  132. #endif
  133. }
  134. }
  135. if (find)
  136. {
  137. printf("\n当前界限下的阶乘%.0f!, 对应的对数%.15f", start, lf);
  138. fprintf(fp, "\n当前界限下的阶乘%.0f!, 对应的对数%.15f", start, lf);
  139. }
  140. mpfr_clear(ln10);
  141. mpfr_clear(cA);
  142. mpfr_clear(cT);
  143. fclose(fp);
  144. return 1;
  145. }
复制代码
========================================================== 请输入起始搜索数字:100000000 请输入上界: 3.1415927 请输入下界: 3.1415926 起始位置:100000000 当前界限[3.14159260000000010, 3.14159269999999990] 当前对数界限[0.49714986528586863, 0.49714987910989134] 高精度stirling函数执行时间(毫秒): 30.044000, 测试值:493.74586181 起始对数: 0.20876475177585671 每10^6次查找执行时间(秒): 0.170244 当前界限下的阶乘149832529!, 对应的对数0.497149873593338 ======================================================== 经过查询汇编代码,程序已经被gcc优化到了很好的程度 相对以前,执行时间已经缩小了很多了 使用了P6以上的fcomi和直接的log指令, 另外,其最终的对数值精度提高了 ========================================================== P4 2.0 /1G DDR/XP SP3
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-12-22 07:43:57 | 显示全部楼层
d = 314159265358, n = 1154318938997!, 对应的对数0.497149872693353,首数字3141592653584138 ============================================================= 通过PARI验证 (09:52) gp > n = 1154318938997 %38 = 1154318938997 (07:44) gp > (n+1/2)*log(n) + 1/2*log(2*pi) - n + 1/(12*n) - 1/(360*n^3) %39 = 30906348934959.82738115133341720201016520726795684547468467 (07:44) gp > %39 / log(10) %40 = 13422456798229.49714987269335219103203453332675692373542468 (07:44) gp > %40 - floor(%40) %41 = 0.497149872693352191032034533326756923735424682255 (07:44) gp > 10^(%41) %42 = 3.14159265358413885452821840174232968895743730673
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-12-22 08:39:24 | 显示全部楼层
用来计算的机器是 Core 2 XEON 1.6G / 1G DDR2 下一个将在10万亿附近产生 目前增加到10000个数字校正一次 每10 ^8的时间是7.8125秒 如果还是每1000校正一次 则是9.375秒 ========================== 差距是两天 呵呵 ========================== 依据是对最新求得的结果1154318938997 使用10000间隔校正 从之前的11543000000重新搜索 结果对数是0.497149872693352 而GP得到的结果是0.497149872693353 =========================== 目前的对数范围 [0.49714987269402422 , 0.49714987269416244] 差距是13822 但因为所有代码均被gcc优化成汇编代码,而gcc默认是63位浮点 所以实际上的差距是1382200 我想足够10000间隔校正而不丢失分辨精度吧 ============================= 呵呵,求出下个结果,就不再计算了 再下个至少需要100天
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-12-22 11:00:46 | 显示全部楼层
在这十天的空隙里 先贴点以10^n开始的 d = 1, n = 5 d = 10, n = 27 d = 100, n = 197 d = 1000, n = 197 d = 10000, n = 7399 //////////////////////////////////////////// 呵呵,下面的似乎是错误的 看来需要校正结果
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-12-23 08:00:46 | 显示全部楼层
竟然昨天下午5:37就得到了数据 ============================= 请输入起始搜索数字:1154318938997 请输入上界: 3.141592653590 请输入下界: 3.141592653589 起始位置:1154318938997 当前界限[3.14159265358900000, 3.14159265359000010] 当前对数界限[0.49714987269402422, 0.49714987269416244] 高精度stirling函数执行时间(毫秒): 15.625000, 测试值:493.74586181 起始对数: 0.49714987269335220 每10^6次查找执行时间(秒): 0.078125 当前界限下的阶乘1544607046599!, 对应的对数0.497149872694156 ================================================= GP的验证 (11:04) gp > stirling1(n) = (n+1/2)*log(n) + 1/2*log(2*pi) - n + 1/(12*n) - 1/( 360*n^3) (11:04) gp > stirling(n) = stirling1(n) / log(10) - floor(stirling1(n) / log(10 )) (11:06) gp > stirling(1544607046599) %57 = 0.497149872694155502964270816144690329633515005612 (07:54) gp > 10^%57 %58 = 3.14159265358994983986193148792491264569132697520
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-12-23 09:23:25 | 显示全部楼层
看兄弟比较孤寂的,回复一个。 可惜没有这么好的软硬件条件,所以只能欣赏而已。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-12-23 09:30:31 | 显示全部楼层
呵呵 这个情况 导致了我还有继续计算下去的必要 再算一个,直到结果超越10万亿
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
您需要登录后才可以回帖 登录 | 欢迎注册

本版积分规则

小黑屋|手机版|数学研发网 ( 苏ICP备07505100号 )

GMT+8, 2024-11-21 20:36 , Processed in 0.028678 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表