找回密码
 欢迎注册
查看: 17361|回复: 8

[讨论] 在高精度计算中,连分式展开相对泰勒展开式究竟有多少优点?

[复制链接]
发表于 2016-4-30 13:38:23 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?欢迎注册

×
对连分式感兴趣1是数值计算原理教科书上的栗子:Ln2的连分式R44比泰勒展开式8阶精确10万倍;2是Mathematica的实现说明也写很多函数是使用连分式计算的;3是“连分式可以减少乘除法计算量。”

然而动手编码却发现没那么简单:
先说减少乘除法计算量,恐怕是针对浮点机器数的,一次除法大概等于乘法。但高精度除法一次大概等于10次乘法。避免每次做除法就使用递推法分别计算分子分母,最后再除法,那么也是每阶要4次乘法。还是不如秦九韶法计算泰勒展开式。

然后就是“同等阶数”下连分式和泰勒展开式的精度问题。原来函数做连分式展开式没有唯一方式。
使用欧拉公式把泰勒展开式转化为连分式?精度同等的,如前所说,没有计算量优势。
原始方式,一次次计算一次泰勒展开?除非找到系数规律,不然对于高精度计算需要上百阶的情形没意义。
还有就是高斯的椭圆函数连分式了,估计Mathematica的“很多函数”就是用这个了。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2016-5-2 16:30:50 | 显示全部楼层
高精度除法一次大概等于10次乘法


我不这么认为,我认为一次高精度除法的运行时间大概等于1.5到2次高精度乘法。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2016-5-2 17:18:09 | 显示全部楼层
liangbch 发表于 2016-5-2 16:30
我不这么认为,我认为一次高精度除法的运行时间大概等于1.5到2次高精度乘法。

a/b,前面逐次倍增精度是n*log依位数不同有差别。单算最后倒数 x*b=1,接着 a*x已经是2次。


推一步讲即使除法优化到1.5到2次高精度乘法时间,一般情况下依然是不如泰勒展开。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2016-5-2 19:42:40 | 显示全部楼层
使用牛顿迭代法计算b的倒数并精确到p位数字,当p比较小时(在这种情况下,最快的大数乘法依然是硬乘法),计算r=1/b的复杂度约等于0.5次p位乘以p位的大数乘法,故总的复杂度是1.5次。
“前面逐次倍增精度是n*log依位数不同有差别”,
我曾经做过这样的事情,我记得,
若需精确都p位数,则 精度逐次倍增时, 每次迭代的运算量是上一次迭代的2倍,最后一次运算量是 (0.5 * p)^2, 这是一个公比为0.5的等比数列,故总的迭代过程的运算量位 0.25*p*p /(1-0.5)=0.5*p *p.  故有这个结论,你可重新检查一下,看看是否有误。

by the way, 我并没有否定你的结论,即,一般情况下,泰勒展开式更快。
不过对于2次方根,其连分数形式一定是一个循环简单连分数,求其连分数形式也不复杂。若求得其“循环节”,其连分数法不失为一种重要的方法。

初等数论是这样说的。
实2次无理数的无限简单连分数表示式一定是一个循环连分数。
对于整系数方程 a(x^2) + bx+c=0,  若 d=b^2-4ac>0, 则其根x0一定是 r+s*sqrt(d)  的形式,这样形式的无理数称之为实2次无理数。

Pell 方程的解法就是用到这个性质。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2016-5-2 19:53:05 | 显示全部楼层
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2016-5-2 20:15:48 | 显示全部楼层
本站帖子 http://bbs.emath.ac.cn/thread-143-1-10.html 讨论了使用牛顿迭代法求高精度平方根的方法,可惜在论坛转移的过程中,代码无法下载。我在我的电脑上搜索了下,找到一份代码,不知道有没有问题,见下。


  1. // sqrt2.cpp : Defines the entry point for the console application.
  2. //
  3. #include <stdlib.h>
  4. #include <stdio.h>
  5. #include <math.h>
  6. #include <iostream.h>

  7. #include "../../../HugeCalc/HugeCalc_API/CppAPI/Include/HugeCalc.h"        // 公共接口
  8. #include "../../../HugeCalc/HugeCalc_API/CppAPI/Include/HugeInt.h"        // 10进制系统
  9. #include "../../../HugeCalc/HugeCalc_API/CppAPI/Include/HugeIntX.h"        // 16进制系统

  10. #pragma message( "automatic link to ../../HugeCalc/HugeCalc_API/CppAPI/Lib/HugeCalc.lib" )
  11. #pragma comment( lib, "../../../HugeCalc/HugeCalc_API/CppAPI/Lib/HugeCalc.lib" )


  12. #define integer CHugeInt

  13. #define TEN9 1000000000
  14. /*
  15.   sqrt(a) 算法:
  16.   
  17.         step1: 得到一个 1/sqrt(a) 近似值 x0
  18.         step2: x[n+1]= 3/2*x[n] + a/2 x[n]^3
  19.                另一个形式: ( x[n+1]= x[n] + x[n] ( 1/2- a/2 *x[n]^2)
  20.         不断计算step2,x[n+1]精度逐步提高,每次迭代精度提高一倍。           
  21.         最后:计算 x[n+1]* a 得到 sqrt(a)
  22. */
  23. double int_sqrt_demo(int n)
  24. {
  25.         double x0;
  26.         double x1;
  27.         double c0,c1;
  28.        
  29.     c0=0.5;
  30.         c1=double(n)/2;

  31.         x0= 1 /sqrt( n );
  32.         x0= floor(x0 * 10000.0) /10000; //取前4位有效数字
  33.        

  34.         while (true)
  35.         {
  36.                 x1= x0 + x0 * (c0- c1 * x0 * x0);
  37.                 if ( (x1- x0)/x0 <1e-15)
  38.                 {
  39.                         break;
  40.                 }
  41.                 x0=x1;
  42.         }
  43.         return double(n) * x1;
  44. }

  45. int dig2Unit(int n)
  46. {
  47.         return (n+8)/9;
  48. }

  49. void sqrt_proc( double n, integer *x,int lmt_pre[],double limitLen)
  50. {
  51.         int i=0;
  52.         char *pBuff=NULL;
  53.         const char *pTmp;
  54.         double esp=1e-15;
  55.         UINT32 digLen;  //数字个数

  56.         integer x0;
  57.         integer x1;
  58.         integer t1;
  59.         integer t2;
  60.        
  61.         integer c0;
  62.         integer c1;
  63.        
  64.         {
  65.                 unsigned int n_01,n_02;
  66.                 n/=2;
  67.                 c0= (TEN9/2);   // c0=0.5;
  68.                 n_01= UINT32(n);
  69.                 n_02= UINT32((n-n_01+esp)*TEN9);

  70.                 c1 = n_01;
  71.                 c1 *= TEN9;
  72.                 c1 += n_02;     // c1=double(n)/2;
  73. #ifdef _DEBUG               
  74.                 if ( pBuff!=NULL)
  75.                 {        delete[] pBuff;        }

  76.                 pBuff=new char[c1.GetDigits()+2];
  77.                 pTmp= c1.GetStr(FS_NORMAL);
  78.                 strcpy(pBuff,pTmp);
  79.                 printf("c1=%s\n",pBuff);
  80.                
  81. #endif

  82.         }
  83.        
  84.         x0= *x;
  85.         i=0;
  86.         digLen=x0.GetDigits();

  87.         while (true)
  88.         {

  89. #ifdef _DEBUG
  90.                 printf("第%d次迭代\n",i+1);
  91. #endif
  92.                
  93.                 if (i>0 && digLen -lmt_pre[i]>=9)
  94.                 {
  95.                         int shift= (digLen-lmt_pre[i])/9*9;
  96.                         x0.DecRShift( shift ); //丢弃多于的数字
  97.                         digLen=x0.GetDigits();
  98.                 }

  99. #ifdef _DEBUG               
  100.                 if ( pBuff!=NULL)
  101.                 {        delete[] pBuff;        }

  102.                 pBuff=new char[x0.GetDigits()+2];
  103.                 pTmp= x0.GetStr(FS_NORMAL);
  104.                 strcpy(pBuff,pTmp);
  105.                 printf("x0=%s\n",pBuff);
  106. #endif
  107.        
  108.                 t1.Pow( x0,2);                //t1= x0 * x0

  109. #ifdef _DEBUG               
  110.                 if ( pBuff!=NULL)
  111.                 {        delete[] pBuff;        }
  112.                 pBuff=new char[t1.GetDigits()+2];
  113.                 pTmp= t1.GetStr(FS_NORMAL);
  114.                 strcpy(pBuff,pTmp);
  115.                 printf("t1=%s\n",pBuff);
  116. #endif
  117.                
  118.                 // x1= x0 + x0 * (c0- c1 * x0 * x0);
  119.                 // c0=0.5;
  120.                 // c1=double(n)/2;

  121.                 t1 *= c1;    // t1= c1* x0 * x0
  122.                 t1.DecRShift(9);
  123. #ifdef _DEBUG               
  124.                 if ( pBuff!=NULL)
  125.                 {        delete[] pBuff;        }

  126.                 pBuff=new char[t1.GetDigits()+2];
  127.                 pTmp= t1.GetStr(FS_NORMAL);
  128.                 strcpy(pBuff,pTmp);
  129.                 printf("t1=%s\n",pBuff);
  130. #endif               

  131.                 t2=c0;
  132.                 t2.DecLShift( dig2Unit(digLen)*2*9-9);  //扩展数位

  133. #ifdef _DEBUG               
  134.                 if ( pBuff!=NULL)
  135.                 {        delete[] pBuff;        }

  136.                 pBuff=new char[t2.GetDigits()+2];
  137.                 pTmp= t2.GetStr(FS_NORMAL);
  138.                 strcpy(pBuff,pTmp);
  139.                 printf("t2=%s\n",pBuff);
  140. #endif               

  141.                 t2-=t1;                // t2=  (c0- c1* x0 * x0);
  142.                 t2 *= x0;   // t2=  x0 * (c0- c1* x0 * x0);
  143.                 t2.DecRShift(dig2Unit(digLen)*9);

  144. #ifdef _DEBUG               
  145.                 if ( pBuff!=NULL)
  146.                 {        delete[] pBuff;        }
  147.                 pBuff=new char[t2.GetDigits()+2];
  148.                 pTmp= t2.GetStr(FS_NORMAL);
  149.                 strcpy(pBuff,pTmp);
  150.                 printf("t2=%s\n",pBuff);
  151.                
  152. #endif               
  153.                 x0.DecLShift( dig2Unit(digLen)*9 );  //扩展x0长度

  154. #ifdef _DEBUG               
  155.                 if ( pBuff!=NULL)
  156.                 {        delete[] pBuff;        }
  157.                 pBuff=new char[x0.GetDigits()+2];
  158.                 pTmp= x0.GetStr(FS_NORMAL);
  159.                 strcpy(pBuff,pTmp);
  160.                 printf("x0=%s\n",pBuff);
  161. #endif
  162.                 x1= x0 + t2; // x1= x0 + x0 * (c0- c1 * x0 * x0);
  163.                
  164. #ifdef _DEBUG               
  165.                 if ( pBuff!=NULL)
  166.                 {        delete[] pBuff;        }
  167.                 pBuff=new char[x1.GetDigits()+2];
  168.                
  169.                 pTmp= x1.GetStr(FS_NORMAL);
  170.                 strcpy(pBuff,pTmp);
  171.                 printf("x1=%s\n",pBuff);
  172. #endif

  173.                 x0=x1;
  174.                 digLen= x0.GetDigits();
  175.                 if ( digLen >= limitLen)
  176.                 {
  177.                         break;
  178.                 }
  179.                 i++;
  180.         }
  181.        
  182.         *x= x0;
  183.         if (pBuff!=NULL)
  184.         {
  185.                 delete[] pBuff;
  186.         }
  187. }

  188. //高精度整数平方根
  189. void int_sqrt(int num, int dig,char *fileName)
  190. {
  191.         double fNum;
  192.         double fLen;
  193.         int e;    //100^e 表示需要把被开方数缩小 100^e,最终结果需要扩大10^e倍
  194.         int i,k;
  195.         int tmp;
  196.        
  197.         int lmt_pre[32];  //第i次迭代最低要求精度,单位10进制位
  198.        
  199.         UINT32 i0;
  200.         char *pBuff=NULL;
  201.         const char *pTmp;

  202.         HugeCalc::EnableTimer( TRUE );
  203.         HugeCalc::ResetTimer();
  204.        
  205.         integer x;
  206.        
  207.         tmp=num;
  208.         e=0;
  209.         while (tmp>=100)
  210.         {
  211.                 tmp /= 100;
  212.                 e++;
  213.         }

  214.         fNum= (double)(num) / pow(100.00,e);

  215.         k=0;
  216.         fLen=dig+18;
  217.         while (fLen >=9)
  218.         {
  219.                 fLen /= 2.0;
  220.                 k++;
  221.         }
  222.         for (i=0;i<=k;i++)
  223.         {
  224.                 lmt_pre[i]=(int)(ceil(fLen));
  225.                 fLen *=2;
  226.         }

  227.         i0= (UINT32)floor(1.0/sqrt(fNum) * TEN9+0.5);
  228.        
  229.         if (i0>=TEN9)
  230.                 i0=TEN9-1;

  231.         x=i0;
  232.         sqrt_proc(fNum, &x,lmt_pre,dig+18);
  233.         x *= integer(num);

  234.         if ( pBuff!=NULL)
  235.         {        delete[] pBuff;        }

  236.         pBuff=new char[x.GetDigits()+3];
  237.                
  238.         pTmp= x.GetStr(FS_NORMAL);
  239.         strcpy(pBuff+1,pTmp);
  240.        
  241.         int intLen= (int)ceil(log10(sqrt(num))); //计算整数部分的数字个数
  242.        
  243.         memcpy(pBuff,pBuff+1,intLen);
  244.         pBuff[intLen]='.';                                        //插入小数点

  245.         pBuff[dig+1]=0;        //去掉多余的数字

  246.         // 记录计算耗时
  247.         UINT32 u32Timer_Calc = HugeCalc::GetTimer( TIMER_UNIT_us );
  248.         double ftime= u32Timer_Calc;
  249.         printf("计算用时 %.6f 秒\n", ftime/1000000.0);

  250.         HugeCalc::EnableTimer( TRUE );
  251.         HugeCalc::ResetTimer();

  252.         FILE *fp=fopen(fileName,"wb");
  253.         fwrite(pBuff,dig+1,1,fp);
  254.         fclose(fp);

  255.         u32Timer_Calc = HugeCalc::GetTimer( TIMER_UNIT_us );
  256.         ftime= u32Timer_Calc;
  257.         printf("输出到文件用时 %.6f 秒\n", ftime/1000000.0);

  258.         delete[] pBuff;
  259.         pBuff=NULL;
  260. }

  261. void test_demo()
  262. {
  263.         double f1=int_sqrt_demo(2);
  264.         double f2=int_sqrt_demo(3);
  265.         double f3=int_sqrt_demo(10);
  266.         double f4=int_sqrt_demo(1000);
  267. }

  268. void nums_sqrt()
  269. {
  270.         int n;
  271.         int dig;
  272.         char fileName[100];


  273.         printf("calc sqrt(n)\n");
  274.        
  275.         while (true)
  276.         {
  277.                 printf("n=?(0 exit)");
  278.                 scanf("%d",&n);
  279.                
  280.                 if (n==0 )
  281.                         break;
  282.                 printf("how many digital is needed?");
  283.                 scanf("%d",&dig);
  284.                 if (dig<=0)
  285.                         break;
  286.                 sprintf(fileName,"sqrt_%d_%08d.txt",n,dig);
  287.                 int_sqrt(n,dig,fileName);
  288.         }

  289. }

  290. int main(int argc, char* argv[])
  291. {
  292.         //test_demo();
  293.         nums_sqrt();
  294.         return 0;
  295. }

复制代码

点评

复制下来学习  发表于 2016-5-4 18:05
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2016-5-2 23:42:16 | 显示全部楼层
liangbch 发表于 2016-5-2 19:42
使用牛顿迭代法计算b的倒数并精确到p位数字,当p比较小时(在这种情况下,最快的大数乘法依然是硬乘法), ...

我不是想辩论什么,只是想对前段时间对连分式的学习作个小结,虽然结论并不那么美好。

对于2次方根或者若干次方根的连分式有这几个维基百科/(上面再推荐)的地址
https://en.wikipedia.org/wiki/So ... continued_fractions
https://en.wikipedia.org/wiki/Generalized_continued_fraction
http://myreckonings.com/Dead_Rec ... racting%20Roots.pdf

如你下面说的,对于2次方根我也倾向用牛顿法。
我对高精度计算分了以下层次。是以我觉得实现的难度,而不是按数学上算术、初等函数等区分。请给些意见。

1 加减法 ,基本上就是延长数位和进位了,线性复杂度。

2 乘法,直乘或Toom-Took,FFT,数论等优化,要依据不同情况采用不同方法。   /*都试写过代码,而不同方法的选择条件反而是我最困惑。*/

3 能够表示成加减乘法方程的函数,除法开方和解多项式方程。      /*看了你对除法的分析,似乎是理论上达到精度就直接算下一步了。而我是代入方程确认达到精度,这里就多做了一次计算。*/

4 就是更复杂的其他函数。/*现在看来依然是使用泰勒展开合适*/

5 个别采用特殊方法的函数:log(/ *算术几何平均, 但我对初期开方优化不好*/) ,高斯连分式还有挖掘的地方。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2016-5-3 00:12:51 | 显示全部楼层
我的除法现在也是直接估出迭代次数,可以减少一次迭代。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
您需要登录后才可以回帖 登录 | 欢迎注册

本版积分规则

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

GMT+8, 2024-5-21 22:47 , Processed in 0.060619 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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