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

[讨论] 阶乘和圆周率

  [复制链接]
发表于 2008-12-17 15:14:22 | 显示全部楼层
发现50#用的程序是每10^7计数一次 所以以前说的每4秒 10^8错误 是40秒
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-12-18 08:00:01 | 显示全部楼层
当前界限下的阶乘77773146302!, 对应的对数0.497149872686531 开始数字3141592653534 检验: (15:09) gp > n = 77773146302 %26 = 77773146302 (07:55) gp > (n+1/2)*log(n) + 1/2*log(2*pi) - n + 1/(12*n) - 1/(360*n^3) %27 = 1872548868987.616634238479898278998546965437108456887088346 (07:55) gp > %27 / log(10) %28 = 813237640895.4971498726865435859265735865657796251777623316 (07:55) gp > %28 - floor(%28) %29 = 0.497149872686543585926573586565779625177762331512 (07:56) gp > 10^%29 %30 = 3.14159265353488687304651737046701462136950721154

评分

参与人数 1鲜花 +1 收起 理由
mathe + 1 社区有你更精彩

查看全部评分

毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-12-18 08:33:02 | 显示全部楼层
原帖由 无心人 于 2008-12-17 14:08 发表 另外一个问题,似乎目前的库,对long double都不支持 我修改程序用long double,取不到对数值 而目前的double精度恐怕支持不了多久了
真正的 long double 有 80 bits,至少需要 10 字节存储, 所以一般编译器不支持。 但可以通过内嵌汇编得到80 bits精度的浮点数, 可惜FPU设计成栈模式,手工编写调试汇编代码非常累人(我之前曾写过一些)! 以下是 FPU 中一些可能对本主题讨论话题有帮助的指令: FLDL2E Load the log base 2 of e (Napierian constant) FLDL2T Load the log base 2 of Ten FLDLG2 Load the log base 10 of 2 (common log of 2) FLDLN2 Load the log base e of 2 (natural log of 2) FLDPI Load the value of PI FSQRT Square root of ST(0) FYL2X Y*Log2X FYL2XP1 Y*Log2(X+1)
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-12-18 09:06:09 | 显示全部楼层
写汇编代码在 gcc里并不方便 在windows下编译GMP和MPFR也不方便 呵呵 两难
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-12-18 09:17:27 | 显示全部楼层
下一个选择就是5000亿左右了, 估计是极限了 仅仅在两个界限的对数值最后四位有差别 [2780, 4162] 每次加1000个,再调整一次 估计差别值在范围上很小 有错过的可能 ============================= 刚才加大了输出的精度 发现是6位的差别 我再去查下double的具体数值
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-12-20 21:10:13 | 显示全部楼层
今天有时间测试了下程序的时间
  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;
  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. hi = log10(hi);
  63. low = log10(low);
  64. printf("起始位置:%.0f\n当前界限[%.17f, %.17f]\n", start, low, hi);
  65. a = start;
  66. b = 1.0;
  67. while (a >= 10.0)
  68. {
  69. a /= 10.0;
  70. b /= 10.0;
  71. }
  72. lf = 2.0;
  73. gettimeofday(&start_time, NULL);
  74. for (i = 0; i < 1000; i ++)
  75. lf += stirling(lf + 100000000.0);
  76. gettimeofday(&end_time, NULL);
  77. used_time = ((end_time.tv_sec - start_time.tv_sec)*1000000.0 + (end_time.tv_usec - start_time.tv_usec)) / 1000.0;
  78. printf("高精度stirling函数执行时间(毫秒): %8.6lf, 测试值:%.8lf\n", used_time, lf);
  79. lf = stirling(start);
  80. printf("起始对数: %.17f\n", lf);
  81. gettimeofday(&start_time, NULL);
  82. while (1)
  83. {
  84. start += 1.0;
  85. a = log10(start*b);
  86. if (a >= 1.0)
  87. {
  88. b /= 10.0;
  89. a -= 1.0;
  90. }
  91. i ++;
  92. lf += a;
  93. if (lf >= 1.0)
  94. lf -= 1.0;
  95. // printf("[%.0f, %.15f] ", start, lf);
  96. if ((lf >= low) && (lf < hi))
  97. {
  98. find = 1;
  99. break;
  100. }
  101. if (i >= 1000)
  102. {
  103. lf = stirling(start);
  104. i = 0;
  105. j ++;
  106. if (j >= 1000)
  107. {
  108. if (test1 == 0)
  109. {
  110. gettimeofday(&end_time, NULL);
  111. used_time = ((end_time.tv_sec - start_time.tv_sec)*1000000.0 + (end_time.tv_usec - start_time.tv_usec))/1000000.0;
  112. printf("每10^6次查找执行时间(秒): %.6lf\n", used_time);
  113. test1 = 1;
  114. }
  115. }
  116. }
  117. }
  118. if (find)
  119. {
  120. printf("\n当前界限下的阶乘%.0f!, 对应的对数%.15f", start, lf);
  121. }
  122. mpfr_clear(ln10);
  123. mpfr_clear(cA);
  124. mpfr_clear(cT);
  125. return 1;
  126. }
复制代码
=============================================== E:\math>factpi1 请输入起始搜索数字:100000000 请输入上界: 3.1415927 请输入下界: 3.1415926 起始位置:100000000 当前界限[0.49714986528586863, 0.49714987910989134] 高精度stirling函数执行时间(毫秒): 40.058000, 测试值:493.74586181 起始对数: 0.20876475177585671 每10^6次查找执行时间(秒): 0.460662 当前界限下的阶乘149832529!, 对应的对数0.497149873593380 ================================================= 对应机器P4 2.0/1G DDR400 软件XP 编译器MinGW + GCC 4.3.2 使用GMP 4.2.4 + MPFR 2.3.2, 编译为静态包 =================================================== 可以看到MPFR下,每1000次stirling函数计算仅0.04秒 而double下每1000*1000次叠加对数值是需要0.46秒 可计算出高精度浮点的运算时间仅是double叠加对数的100倍 所以在目前情况下,普通函数计算称为瓶颈 另外,根据执行时间判断 每次计算需要800-1000时钟周期
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-12-20 21:50:52 | 显示全部楼层
使用-ffast-math选项编译后的输出 请输入起始搜索数字:100000000 请输入上界: 3.1415927 请输入下界: 3.1415926 起始位置:100000000 当前界限[0.49714986528586863, 0.49714987910989134] 高精度stirling函数执行时间(毫秒): 30.043000, 测试值:493.74586181 起始对数: 0.20876475177585671 每10^6次查找执行时间(秒): 0.150216 当前界限下的阶乘143984543!, 对应的对数0.497149871271711
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-12-20 21:55:52 | 显示全部楼层
57#同样条件下 但修改程序如下
  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;
  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. hi = log10(hi);
  63. low = log10(low);
  64. printf("起始位置:%.0f\n当前界限[%.17f, %.17f]\n", start, low, hi);
  65. a = start;
  66. b = 1.0;
  67. while (a >= 10.0)
  68. {
  69. a /= 10.0;
  70. b *= 10.0; ////////////////////////////////////修改
  71. }
  72. lf = 2.0;
  73. gettimeofday(&start_time, NULL);
  74. for (i = 0; i < 1000; i ++)
  75. lf += stirling(lf + 100000000.0);
  76. gettimeofday(&end_time, NULL);
  77. used_time = ((end_time.tv_sec - start_time.tv_sec)*1000000.0 + (end_time.tv_usec - start_time.tv_usec)) / 1000.0;
  78. printf("高精度stirling函数执行时间(毫秒): %8.6lf, 测试值:%.8lf\n", used_time, lf);
  79. lf = stirling(start);
  80. printf("起始对数: %.17f\n", lf);
  81. gettimeofday(&start_time, NULL);
  82. while (1)
  83. {
  84. start += 1.0;
  85. a = log10(start/b); //////////////////////////////修改
  86. if (a >= 1.0)
  87. {
  88. b *= 10.0; //////////////////////////////修改
  89. a -= 1.0;
  90. }
  91. i ++;
  92. lf += a;
  93. if (lf >= 1.0)
  94. lf -= 1.0;
  95. // printf("[%.0f, %.15f] ", start, lf);
  96. if ((lf >= low) && (lf < hi))
  97. {
  98. find = 1;
  99. break;
  100. }
  101. if (i >= 1000)
  102. {
  103. // lf = stirling(start);
  104. i = 0;
  105. j ++;
  106. if (j >= 1000)
  107. {
  108. if (test1 == 0)
  109. {
  110. gettimeofday(&end_time, NULL);
  111. used_time = ((end_time.tv_sec - start_time.tv_sec)*1000000.0 + (end_time.tv_usec - start_time.tv_usec))/1000000.0;
  112. printf("每10^6次查找执行时间(秒): %.6lf\n", used_time);
  113. test1 = 1;
  114. }
  115. }
  116. }
  117. }
  118. if (find)
  119. {
  120. printf("\n当前界限下的阶乘%.0f!, 对应的对数%.15f", start, lf);
  121. }
  122. mpfr_clear(ln10);
  123. mpfr_clear(cA);
  124. mpfr_clear(cT);
  125. return 1;
  126. }
复制代码
请输入起始搜索数字:100000000 请输入上界: 3.1415927 请输入下界: 3.1415926 起始位置:100000000 当前界限[0.49714986528586863, 0.49714987910989134] 高精度stirling函数执行时间(毫秒): 30.043000, 测试值:493.74586181 起始对数: 0.20876475177585671 每10^6次查找执行时间(秒): 0.230332 当前界限下的阶乘149832529!, 对应的对数0.497149873593496
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-12-20 21:57:48 | 显示全部楼层
可以看到 乘法和除法在浮点运算下 对时间的影响是很大的 还有一个可以修改优化的地方是对取10的对数 改为取2的对数再乘以log10(2) 但不知道标准函数有取2的对数的函数么? 还是需要自己写汇编
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-12-20 22:13:45 | 显示全部楼层
去掉-ffast-math,同时增加了对求10的对数的优化
  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. }
复制代码
========================================= 请输入起始搜索数字:100000000 请输入上界: 3.1415927 请输入下界: 3.1415926 起始位置:100000000 当前界限[0.49714986528586863, 0.49714987910989134] 高精度stirling函数执行时间(毫秒): 30.044000, 测试值:493.74586181 起始对数: 0.20876475177585671 每10^6次查找执行时间(秒): 0.240346 当前界限下的阶乘149832529!, 对应的对数0.497149877671004
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
您需要登录后才可以回帖 登录 | 欢迎注册

本版积分规则

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

GMT+8, 2024-11-21 21:34 , Processed in 0.030400 second(s), 16 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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