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

[讨论] 阶乘和圆周率

  [复制链接]
发表于 2010-8-23 20:56:07 | 显示全部楼层


我重读这个帖子,期待对另外一个问题程序的改进
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2010-8-24 09:25:04 | 显示全部楼层
我期待这个题有一个新的突破
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2012-10-31 08:09:30 | 显示全部楼层
如果允许小数的话,问题就变得很简单了。
比如 n!=3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446 E1000000000000
那么n=94851898540.1396977006676482198750221184274755718919148371815155857816611145105279808242712122064124653944534478504904850688162913
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2012-12-1 20:22:20 | 显示全部楼层
1# medie2005

做新手任务,凑字数
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2012-12-3 18:16:10 | 显示全部楼层
这个题目真的很有意思!
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2012-12-4 16:16:05 | 显示全部楼层
1# medie2005


难度确实挺大
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2012-12-6 18:08:52 | 显示全部楼层
我曾发过帖子说2的幂能以任何数字开始
这个问题和那帖子上的问题类似
但更难做了
无心人 发表于 2008-12-4 11:38

Matlab?

评分

参与人数 1金币 +20 收起 理由
gxqcn + 20 首帖奖励,欢迎常来。

查看全部评分

毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2012-12-12 14:56:54 | 显示全部楼层
62# 无心人

今天仔细考虑了这个题目。我认为无需使用大数库。无心人的程序中调用了大数库,而且用到对数,故速度较慢。其实这个题目虽然需要做高精度计算,但只用到小整数乘以大数的运算,完全可以自己实现。我刚刚写了一个程序,未用到任何大数库。这个程序从1开始,一直查找到2^32-1,在我的电脑上只需要3分钟左右。

说明:
    1. 我在程序中设定的精度是4个DWORD,每个DWORD表示9位10进制数,故最大精度不超过36位,若需更高精度,则只需改动一处即可。
    2. 目前的代码最多可计算到2^32-1,若需要扩大搜索范围,需要实现一个64bit数和大数相乘的算法。
    3. 我的程序依次计算每个32位整数n,求n!的科学计数法表示和pi的差,若其差小于当前的最小值,则输出n和n!.

下面是我的程序的输出结果:

  1. 20!=2.432902008176640000*10^18
  2. 23!=2.5852016738884976640000*10^22
  3. 28!=3.04888344611713860501504000000*10^29
  4. 154!=3.0897696138473508879585646636*10^271
  5. 332!=3.1034430734498676144426322732*10^694
  6. 612!=3.1345284031431087019726722580*10^1441
  7. 2703!=3.1398094010536875630936179148846*10^8104
  8. 8945!=3.139993064736104560624185498*10^31464
  9. 17254!=3.14084642621024495446191444792*10^65612
  10. 28726!=3.14106463595708329690285209393642766*10^115595
  11. 50583!=3.1415379577347882738422653077918*10^215977
  12. 527600!=3.14155838836242816120923043886*10^2789957
  13. 780018!=3.1415890794236473559982729593497*10^4257193
  14. 1812538!=3.141589535267260763834817102767*10^10556211
  15. 5385973!=3.141591234607732809306850985*10^33915312
  16. 5573998!=3.14159238184880918674222498012222406*10^35182367
  17. 52604972!=3.1415925665166406146782547740*10^383318353
  18. 65630447!=3.141592602756036723755404377401952*10^484537182
  19. 187024036!=3.141592617297502030148476882134*10^1465820139
  20. 410390476!=3.141592622630656164649753699858562*10^3356543814
  21. 464736214!=3.14159264915921444226889681595*10^3826132373
  22. 688395641!=3.141592651962886290907283002629*10^5784962808
  23. It took 188 secs
复制代码

评分

参与人数 1贡献 +10 鲜花 +10 收起 理由
gxqcn + 10 + 10 很巧的方法,难得的结果!

查看全部评分

毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2012-12-12 14:59:02 | 显示全部楼层
  1. #include "stdlib.h"
  2. #include "stdio.h"
  3. #include "string.h"
  4. #include "windows.h"

  5. typedef __int64  INT64;

  6. typedef unsigned long DWORD;

  7. #define DEC_RADIX 1000000000

  8. #define PRECISION_DIV9 4                //10进制数表示的数字个数除以9


  9. const DWORD PI_VALUE[][2]={
  10.         {3,141592653},
  11.         {31,415926535},
  12.         {314,159265358},
  13.         {3141,592653589},
  14.         {31415,926535897},
  15.         {314159,265358979},
  16.         {3141592,653589793},
  17.         {31415926,535897932},
  18.         {314159265,358979323},
  19. };

  20. double scales[]=
  21. {
  22.         1.00,
  23.         10.00,
  24.         100.00,
  25.         1000.00,
  26.         10000.00,
  27.         100000.00,
  28.         1000000.00,
  29.         10000000.00,
  30.         100000000.00,
  31. };


  32. int Uint32BitCount(DWORD n)
  33. {
  34.         _asm
  35.         {
  36.                 bsr eax,n
  37.                 inc eax
  38.         }
  39. }

  40. //******************************************************
  41. DWORD DEC_Array_Mul_DWORD_ALU(DWORD *a, size_t len, DWORD num)
  42. //******************************************************
  43. {
  44.         static DWORD _s_TEN9=DEC_RADIX;
  45.         //---------------------------       
  46.         _asm
  47.         {
  48.                 push esi

  49.                 mov ecx,len
  50.                 mov esi,a
  51.                 lea esi,[esi+ecx*4-4]
  52.                 xor ecx,ecx                        //carry

  53.                 test esi,3                        //the a must be 4byte align
  54.                 jnz  thisExit

  55.                 jmp cmp000

  56. loop_start:
  57.                 mov eax,[esi]
  58.                 mul dword ptr num
  59.                 add eax,ecx
  60.                 adc edx,0
  61.                 div dword ptr _s_TEN9
  62.                 mov [esi],edx
  63.                 mov ecx,eax
  64.                 sub esi,4
  65. cmp000:
  66.                 cmp esi,a
  67.                 jae loop_start
  68. thisExit:
  69.                 mov eax,ecx
  70.                 pop esi
  71.         }
  72. }

  73. double check_diff(DWORD num[],int shift)
  74. {
  75.         int t,carry;
  76.         DWORD tArr[2];

  77.         tArr[0]=PI_VALUE[shift][0];
  78.         tArr[1]=PI_VALUE[shift][1];
  79.        
  80.         t=tArr[1]-num[1];
  81.         if (t<0)
  82.         {
  83.                 carry=1;
  84.                 t+=DEC_RADIX;
  85.         }
  86.         else
  87.                 carry=0;

  88.         tArr[1]=t;
  89.        
  90.         t=tArr[0]-num[0]-carry;
  91.         tArr[0]=t;

  92.         if (t<0)
  93.         {
  94.                 return 999;
  95.         }
  96.         else
  97.         {
  98.                 double diff;
  99.                 diff =tArr[0] + tArr[1] * 0.000000001;
  100.                 diff /= scales[shift];
  101.                 return diff;

  102.         }
  103. }

  104. void print_number(DWORD n,DWORD res[],int len,double exp)
  105. {
  106.         char buff[PRECISION_DIV9*9+9];
  107.         char *p;
  108.         int i,s;
  109.         INT64 e;

  110.         p=buff;
  111.         p+=sprintf(p,"%d",res[0]);
  112.         s=strlen(buff);

  113.         if (len>1)
  114.         {
  115.                 for (i=1;i<len;i++)
  116.                         p+=sprintf(p,"%09d",res[ i ]);
  117.         }
  118.        
  119.         printf("\n%u!=",n);
  120.         for (i=0;i<strlen(buff);i++)
  121.         {
  122.                 printf("%c",buff[ i ]);
  123.                 if (i==0)
  124.                         printf(".");
  125.         }

  126.         e=(INT64)(exp+(len-1)*9+s-1);
  127.         printf("*10^%I64d\n",e);
  128. }


  129. void search_n()
  130. {
  131.         DWORD i,j;
  132.         DWORD carry;

  133.         int shift;
  134.         int bc,fac_len;                // fac(i)的结果的数组的长度
  135.         double diff,min_diff;
  136.         double res_exp;
  137.        
  138.         DWORD fac[PRECISION_DIV9+1]; //fac(i)的结果的数组
  139.         DWORD time;

  140.        
  141.         time=GetTickCount();
  142.         min_diff=1;
  143.         res_exp=0;
  144.         fac[0]=1;
  145.         fac_len=1;
  146.        
  147.         i=1;
  148.         //while (i<DEC_RADIX)
  149.         while (i!=0)
  150.         {
  151.                 carry=DEC_Array_Mul_DWORD_ALU(fac,fac_len,i);
  152.                 if (carry>=DEC_RADIX)
  153.                 {
  154.                         for (j=PRECISION_DIV9-2;j>=2;j--)
  155.                                 fac[j]=fac[j-2];
  156.                         fac[0]=carry / DEC_RADIX;
  157.                         fac[1]=carry % DEC_RADIX;
  158.                        
  159.                         if ( fac_len+2 <= PRECISION_DIV9)
  160.                                 fac_len+=2;
  161.                         else if (fac_len+1 ==PRECISION_DIV9)
  162.                         {
  163.                                 fac_len=PRECISION_DIV9;
  164.                                 res_exp+=9;
  165.                         }
  166.                         else
  167.                                 res_exp+=18;
  168.                 }
  169.                 else if (carry>0)
  170.                 {
  171.                         for (j=PRECISION_DIV9-1;j>=1;j--)
  172.                                 fac[j]=fac[j-1];
  173.                         fac[0]=carry;
  174.                         if (fac_len<PRECISION_DIV9)
  175.                                 fac_len++;
  176.                         else
  177.                                 res_exp+=9;
  178.                 }
  179.                 bc=Uint32BitCount(fac[0]);
  180.                
  181.                 switch (bc)
  182.                 {
  183.                 case 2:
  184.                          shift=0;
  185.                          diff=check_diff(fac,shift);
  186.                          if (diff<min_diff)
  187.                          {
  188.                                  min_diff=diff;
  189.                                  if (fac_len>=2)
  190.                                          print_number(i,fac,fac_len,res_exp);
  191.                          }
  192.                          break;
  193.                 case 5:
  194.                         shift=1;
  195.                         diff=check_diff(fac,shift);
  196.                         if (diff<min_diff)
  197.                         {
  198.                                  min_diff=diff;
  199.                                  if (fac_len>=2)
  200.                                          print_number(i,fac,fac_len,res_exp);
  201.                          }
  202.                          break;
  203.                 case 9:
  204.                          shift=2;
  205.                         diff=check_diff(fac,shift);
  206.                          if (diff<min_diff)
  207.                          {
  208.                                  min_diff=diff;
  209.                                  if (fac_len>=2)
  210.                                          print_number(i,fac,fac_len,res_exp);
  211.                          }
  212.                          break;
  213.                 case 12:
  214.                          shift=3;
  215.                         diff=check_diff(fac,shift);
  216.                          if (diff<min_diff)
  217.                          {
  218.                                  min_diff=diff;
  219.                                  if (fac_len>=2)
  220.                                          print_number(i,fac,fac_len,res_exp);
  221.                          }
  222.                          break;
  223.                 case 15:
  224.                          shift=4;
  225.                         diff=check_diff(fac,shift);
  226.                          if (diff<min_diff)
  227.                          {
  228.                                  min_diff=diff;
  229.                                  if (fac_len>=2)
  230.                                          print_number(i,fac,fac_len,res_exp);
  231.                          }
  232.                          break;
  233.                 case 19:
  234.                          shift=5;
  235.                         diff=check_diff(fac,shift);
  236.                          if (diff<min_diff)
  237.                          {
  238.                                  min_diff=diff;
  239.                                  if (fac_len>=2)
  240.                                          print_number(i,fac,fac_len,res_exp);
  241.                          }
  242.                          break;
  243.                 case 22:
  244.                          shift=6;
  245.                         diff=check_diff(fac,shift);
  246.                          if (diff<min_diff)
  247.                          {
  248.                                  min_diff=diff;
  249.                                  if (fac_len>=2)
  250.                                          print_number(i,fac,fac_len,res_exp);
  251.                          }
  252.                          break;
  253.                 case 25:
  254.                          shift=7;
  255.                         diff=check_diff(fac,shift);
  256.                          if (diff<min_diff)
  257.                          {
  258.                                  min_diff=diff;
  259.                                  if (fac_len>=2)
  260.                                          print_number(i,fac,fac_len,res_exp);
  261.                          }
  262.                          break;
  263.                 case 29:
  264.                         shift=8;
  265.                         diff=check_diff(fac,shift);
  266.                          if (diff<min_diff)
  267.                          {
  268.                                  min_diff=diff;
  269.                                  if (fac_len>=2)
  270.                                          print_number(i,fac,fac_len,res_exp);
  271.                          }
  272.                          break;
  273.                 default:
  274.                         break;
  275.                 }
  276.                 i++;
  277.         }
  278.        
  279.         time=(GetTickCount()-time);
  280.         printf("It took %d secs\n",time/1000);

  281. }

  282. int main(int argc, char* argv[])
  283. {
  284.         search_n();
  285.         return 0;
  286. }
复制代码
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2012-12-12 21:34:01 | 显示全部楼层
刚才仔细看了一下无心人的代码,明白了其所用的算法。其主要算法如下,
从start开始,逐个计算每个整数n的阶乘的常用对数的小数部分,记作log(fac(n)), 和PI的常用对数的小数部分比较,符合条件则输出。
    当n是1000的整数倍时,计算base=n, lf0=log10(fac(base))的小数部分,
    若n不是base的整数倍时,则计算从base+1到n之间所有数的累乘积的自然对数的小数部分lf1, 则 n!的对数的小数部分为 lf= lf0+lf1。

无心人的算法主要受制于 c语言库中(或者CPU浮点指令)中的双精度数的对数函数精度的制约,很难提高精度。


受无心人的启发,我打算编写一个不使用求双精度对数和多重精度对数函数的程序。
  主要特点有:
  1. 可计算到10^15或者更高。
  2. 可分段计算。
  3. 当n小于10亿时,使用自己写的多重精度大数乘以32bit整数的子程序,计算n的阶乘,fac(n),以10^9进制表示.
     当n>=10亿且n是100万的倍数时,使用斯特林公式的普通形式(而不是指数形式)计算 fac(n),以10^9进制表示,记作fac_0
     当n>10亿且n不是100万的倍数时,令n=base+d(base是100万的整数倍,d>0 且 d<base),使用自己写的多重精度大数乘以64bit整数的子程序,计算base+1到n的累乘积,记作fac_1, 则n的阶乘fac(n)=fac0*fac1

  我的算法和无心人算法的差别,我的记作B,无心人的记作A。
    1. B使用斯特林公式的普通形式,而A使用斯特林公式的对数形式。
    2. B不计算n的阶乘的常用对数,而是直接计算n的阶乘的值,以10亿进制的表示形式。
    3. A的核心部分为计算64bit整数的的常用对数,使用C语言函数库,A受制于double型数的精度,无法提高精度。 B的和核心部分计算一个多精度数与64bit整数的乘积,可通过增加多精度数数的长度来提高精度。
    4. 性能的PK. 主要看 多精度数与64bit整数的乘积 VS log10(double),那个算得更快。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
您需要登录后才可以回帖 登录 | 欢迎注册

本版积分规则

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

GMT+8, 2024-4-20 08:55 , Processed in 0.044571 second(s), 16 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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