找回密码
 欢迎注册
楼主: 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.    
  15.     mpfr_init2(a, DefaultBitLength);
  16.     mpfr_init2(b, DefaultBitLength);
  17.     mpfr_set_d(a, n, GMP_RNDD); //a=n
  18.     mpfr_init2(c, DefaultBitLength);
  19.     mpfr_init2(s, DefaultBitLength);

  20.     mpfr_div(s, cT, a, GMP_RNDD); //s = 1/12a = 1/12n   
  21.     mpfr_sub(s, s, a, GMP_RNDD); //s = s - a = - n + 1/12n
  22.    
  23.     mpfr_log(c, a, GMP_RNDD); //c = ln(n)

  24.     mpfr_set_d(b, 0.5, GMP_RNDD);
  25.     mpfr_add(b, a, b, GMP_RNDD);
  26.     mpfr_mul(b, b, c, GMP_RNDD); //b = (n + 1/2)ln(n)
  27.    
  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.    
  34.     r = mpfr_get_ld(s, GMP_RNDD);
  35.    
  36.     mpfr_clear(a);
  37.     mpfr_clear(b);
  38.     mpfr_clear(c);
  39.     mpfr_clear(s);
  40.     return r;
  41. }

  42. int main(void)
  43. {
  44.   double a, b, low, hi, lf, t;
  45.   double start, used_time, LT2;
  46.   int find = 0, test1 = 0;
  47.   long i = 0, j = 0;
  48.   struct timeval start_time, end_time;

  49.   mpfr_init2(ln10, DefaultBitLength);
  50.   mpfr_init2(cA, DefaultBitLength);
  51.   mpfr_init2(cT, DefaultBitLength);
  52.   mpfr_set_ui(ln10, 10, GMP_RNDD);
  53.   mpfr_set_ui(cT, 1, GMP_RNDD);

  54.   mpfr_log(ln10, ln10, GMP_RNDD);
  55.   mpfr_div(ln10, cT, ln10, GMP_RNDD);
  56.   
  57.   mpfr_const_pi(cA, GMP_RNDD);
  58.   mpfr_mul_ui(cA, cA, 2, GMP_RNDD);
  59.   mpfr_log(cA, cA, GMP_RNDD);
  60.   mpfr_div_ui(cA, cA, 2, GMP_RNDD);
  61.   
  62.   mpfr_div_ui(cT, cT, 12, GMP_RNDD);
  63.   
  64.   printf("请输入起始搜索数字:");
  65.   scanf("%lf", &start);
  66.   printf("请输入上界: ");
  67.   scanf("%lf", &hi);
  68.   printf("请输入下界: ");
  69.   scanf("%lf", &low);
  70.   
  71.   LT2 = log10(2);
  72.   hi = log10(hi);
  73.   low = log10(low);
  74.   
  75.   printf("起始位置:%.0f\n当前界限[%.17f, %.17f]\n", start, low, hi);
  76.   a = start;
  77.   b = 1.0;
  78.   while (a >= 10.0)
  79.   {
  80.     a /= 10.0;
  81.     b *= 10.0; ///////////////////////////////////
  82.   }

  83.   lf = 2.0;
  84.   gettimeofday(&start_time, NULL);
  85.   for (i = 0; i < 1000; i ++)
  86.    lf += stirling(lf + 100000000.0);
  87.   gettimeofday(&end_time, NULL);
  88.   used_time = ((end_time.tv_sec - start_time.tv_sec)*1000000.0 + (end_time.tv_usec - start_time.tv_usec)) / 1000.0;
  89.   printf("高精度stirling函数执行时间(毫秒): %8.6lf, 测试值:%.8lf\n", used_time, lf);

  90.   lf = stirling(start);
  91.   printf("起始对数: %.17f\n", lf);

  92.   gettimeofday(&start_time, NULL);
  93.   
  94.   while (1)
  95.   {
  96.     start += 1.0;
  97.     a = log2(start/b) * LT2;  ///////////////////////////
  98.    
  99.     if (a >= 1.0)
  100.     {
  101.       b *= 10.0; ////////////////////////////////
  102.       a -= 1.0;
  103.     }
  104.    
  105.     i ++;
  106.     lf += a;
  107.         
  108.     if (lf >= 1.0)
  109.         lf -= 1.0;

  110. //    printf("[%.0f, %.15f]  ", start, lf);
  111.    
  112.     if ((lf >= low) && (lf < hi))
  113.      {
  114.        find = 1;
  115.        break;
  116.      }
  117.    
  118.     if (i >= 1000)
  119.     {
  120. //      lf = stirling(start);
  121.       i = 0;
  122.    j ++;
  123.    if (j >= 1000)
  124.    {
  125. if (test1 == 0)
  126. {
  127.           gettimeofday(&end_time, NULL);
  128.           used_time = ((end_time.tv_sec - start_time.tv_sec)*1000000.0 + (end_time.tv_usec - start_time.tv_usec))/1000000.0;
  129.           printf("每10^6次查找执行时间(秒): %.6lf\n", used_time);
  130.    test1 = 1;
  131.         }
  132.    }
  133.     }
  134.   }
  135.   
  136.   if (find)
  137.   {
  138.     printf("\n当前界限下的阶乘%.0f!, 对应的对数%.15f", start, lf);
  139.   }
  140.   
  141.   mpfr_clear(ln10);
  142.   mpfr_clear(cA);
  143.   mpfr_clear(cT);
  144.   return 1;
  145. }
复制代码

编译命令
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.   
  11.   FILE * fp;
  12.   
  13. //=1/2ln(2π)+(n+1/2)ln(n)-n+1/12n
  14. //n > 10^8 误差<1/360Z^3 = 10^(-26)
  15. long double stirling(double n)
  16. {
  17.     mpfr_t a, b, c, s;
  18.     long double r;
  19.    
  20.     mpfr_init2(a, DefaultBitLength);
  21.     mpfr_init2(b, DefaultBitLength);
  22.     mpfr_set_d(a, n, GMP_RNDD); //a=n
  23.     mpfr_init2(c, DefaultBitLength);
  24.     mpfr_init2(s, DefaultBitLength);

  25.     mpfr_div(s, cT, a, GMP_RNDD); //s = 1/12a = 1/12n   
  26.     mpfr_sub(s, s, a, GMP_RNDD); //s = s - a = - n + 1/12n
  27.    
  28.     mpfr_log(c, a, GMP_RNDD); //c = ln(n)

  29.     mpfr_set_d(b, 0.5, GMP_RNDD);
  30.     mpfr_add(b, a, b, GMP_RNDD);
  31.     mpfr_mul(b, b, c, GMP_RNDD); //b = (n + 1/2)ln(n)
  32.    
  33.     mpfr_add(s, s, b, GMP_RNDD); //s = (n + 1/2)ln(n) - n + 1/12n

  34.     mpfr_add(s, s, cA, GMP_RNDD); //最终结果s=1/2ln(2*pi) + (n + 1/2)ln(n) - n + 1/12n

  35.     mpfr_mul(s, s, ln10, GMP_RNDD); //转换为log10
  36.     mpfr_floor(b, s);
  37.     mpfr_sub(s, s, b, GMP_RNDD); //只要小数
  38.    
  39.     r = mpfr_get_ld(s, GMP_RNDD);
  40.    
  41.     mpfr_clear(a);
  42.     mpfr_clear(b);
  43.     mpfr_clear(c);
  44.     mpfr_clear(s);
  45.     return r;
  46. }

  47. int main(void)
  48. {
  49.   double a, b, low, hi, lf, t;
  50.   double start, used_time;
  51.   int find = 0, test1 = 0;
  52.   long i, j = 0;
  53.   struct timeval start_time, end_time;

  54.   mpfr_init2(ln10, DefaultBitLength);
  55.   mpfr_init2(cA, DefaultBitLength);
  56.   mpfr_init2(cT, DefaultBitLength);
  57.   mpfr_set_ui(ln10, 10, GMP_RNDD);
  58.   mpfr_set_ui(cT, 1, GMP_RNDD);

  59.   mpfr_log(ln10, ln10, GMP_RNDD);
  60.   mpfr_div(ln10, cT, ln10, GMP_RNDD);
  61.   
  62.   mpfr_const_pi(cA, GMP_RNDD);
  63.   mpfr_mul_ui(cA, cA, 2, GMP_RNDD);
  64.   mpfr_log(cA, cA, GMP_RNDD);
  65.   mpfr_div_ui(cA, cA, 2, GMP_RNDD);
  66.   
  67.   mpfr_div_ui(cT, cT, 12, GMP_RNDD);
  68.   
  69.   printf("请输入起始搜索数字:");
  70.   scanf("%lf", &start);
  71.   printf("请输入上界: ");
  72.   scanf("%lf", &hi);
  73.   printf("请输入下界: ");
  74.   scanf("%lf", &low);
  75.   fp = fopen("factpi.log", "a+t");
  76.   printf("起始位置:%.0f\n当前界限[%.17f, %.17f]\n", start, low, hi);
  77.   fprintf(fp, "\n///////////////////////////////////////////////////////////////\n");
  78.   fprintf(fp, "起始位置:%.0f\n当前界限[%.17f, %.17f]\n", start, low, hi);

  79.   hi = log10(hi);
  80.   low = log10(low);
  81.   
  82.   printf("当前对数界限[%.17f, %.17f]\n", low, hi);
  83.   fprintf(fp, "当前对数界限[%.17f, %.17f]\n", low, hi);
  84.   a = start;
  85.   b = 1.0;
  86.   while (a >= 10.0)
  87.   {
  88.     a /= 10.0;
  89.     b *= 10.0;
  90.   }

  91.   lf = 2.0;
  92.   gettimeofday(&start_time, NULL);
  93.   for (i = 0; i < 1000; i ++)
  94.    lf += stirling(lf + 100000000.0);
  95.   gettimeofday(&end_time, NULL);
  96.   used_time = ((end_time.tv_sec - start_time.tv_sec)*1000000.0 + (end_time.tv_usec - start_time.tv_usec)) / 1000.0;
  97.   printf("高精度stirling函数执行时间(毫秒): %8.6lf, 测试值:%.8lf\n", used_time, lf);
  98.   fprintf(fp, "高精度stirling函数执行时间(毫秒): %8.6lf, 测试值:%.8lf\n", used_time, lf);

  99.   lf = stirling(start);
  100.   printf("起始对数: %.17f\n", lf);
  101.   fprintf(fp, "起始对数: %.17f\n", lf);
  102.   fflush(fp);
  103.   gettimeofday(&start_time, NULL);

  104.   i = 0;
  105.   while (1)
  106.   {   
  107.     if ((a = log10((start += 1.0)/b)) >= 1.0)
  108.     {
  109.       b *= 10.0;
  110.       a -= 1.0;
  111.     }
  112.         
  113.     if ((lf += a) >= 1.0)
  114.         lf -= 1.0;

  115. //    printf("[%.0f, %.15f]  ", start, lf);
  116.    
  117.     if ((lf >= low) && (lf < hi))
  118.      {
  119.        find = 1;
  120.        break;
  121.      }
  122.    
  123.     if (( ++i) >= 1000)
  124.     {
  125.       lf = stirling(start);
  126.       i = 0;
  127.    j ++;
  128. #ifdef TEST
  129.    if (j >= 1000)
  130.    {
  131. if (test1 == 0)
  132. {
  133.           gettimeofday(&end_time, NULL);
  134.           used_time = ((end_time.tv_sec - start_time.tv_sec)*1000000.0 + (end_time.tv_usec - start_time.tv_usec))/1000000.0;
  135.           printf("每10^6次查找执行时间(秒): %.6lf\n", used_time);
  136.    test1 = 1;
  137.         }
  138.    }
  139. #else
  140.    if (j >= 100000000)
  141.    {
  142. j = 0;
  143. fprintf(fp, "当前搜索数值: %.0f\n", start);
  144. fflush(fp);
  145.    }
  146. #endif  
  147.     }
  148.   }
  149.   
  150.   if (find)
  151.   {
  152.     printf("\n当前界限下的阶乘%.0f!, 对应的对数%.15f", start, lf);
  153.     fprintf(fp, "\n当前界限下的阶乘%.0f!, 对应的对数%.15f", start, lf);
  154.   }
  155.   
  156.   mpfr_clear(ln10);
  157.   mpfr_clear(cA);
  158.   mpfr_clear(cT);
  159.   fclose(fp);
  160.   return 1;
  161. }
复制代码

==========================================================
请输入起始搜索数字: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-4-26 12:05 , Processed in 0.058936 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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