今天和朋友聊面经,说到一道题目,如何不用库函数求根号,并且能用函数的输入参数来控制误差,他想了一个比较牛逼的方法,小数转整数,然后二分查找。在那么短的时间想到这个地步真是不错,但是我貌似记得科学计算的课上说过类似的东西,用泰勒计算:
其实想到了这个其他的都比较简单了,贴一下代码:
#include <iostream> #include <cmath> using namespace std; #define _DEBUG //(1+x)^t = 1+ t*x + t*(t-1)/(t!)8x^2+...+t*(t-1)(t-2)...(t-n+1)/(t!)*x^n const double alpha = 0.5; int main(int argc,char **argv) { if(argc!=2) return 0; double x,y, error = atof(argv[1]), n = 1.0,//int最大只能算12的阶乘! temp_a = alpha, temp = 100.0; cin>>y; x = y-1; y = 1.0; int i = 1; while(abs(temp) > error) { y+=temp_a/n*x; n*=(i+1); temp_a*=(alpha-i); x*=x; temp = temp_a/n*x; ++i; } cout<<"i= "<<i<<" Y = "<<y<<endl; return 0; }
但是可能是浮点数精度的问题,精度只能达到有限值,试一下long double结果一样,只能精确到10e-4的量级,这个如何解决?
是哪里计算的问题呢?再考虑一下!(当n过大时n!超出了double的表示范围!)。
《九章算术》中有云:
开方术曰:置积为实。借一算,步之,超一等。议所得,以一乘所借一算为法,而以除。除已,倍法为定法。其复除,折法而下。复置借算,步之如初,以复议一乘之,所得副以加定法,以除。以所得副从定法。复除,折下如前。
因此代码修正如下:
#include <iostream> #include <cmath> using namespace std; #define _DEBUG //(1+x)^t = 1+ t*x + t*(t-1)/(t!)8x^2+...+t*(t-1)(t-2)...(t-n+1)/(t!)*x^n const double alpha = 0.5; int main(int argc,char **argv) { if(argc!=2) return 0; double x,result, error = atof(argv[1]),//int最大只能算12的阶乘! temp; cin>>result; //设定循环的初始值 x = result-1; result = 1.0; temp = alpha*x; //设定循环次数统计变量 int i = 1; while(abs(temp) > error) { result+=temp; temp*=(alpha-i)/(i+1)*x;//霍纳法则更新temp的值 ++i; } cout<<"i= "<<i<<" Y = "<<result<<endl; return 0; }
整个问题的核心就在于如何使用泰勒级数,了解了公式,一切都比较好解决。
但是如果扩展这个问题,假定给定的是多项式,如何求根?使用泰勒级数逐项展开的前期工作量会变的很大,这时候需要考虑一下牛顿迭代了。
牛顿迭代的原理可以简单叙述如下:
1。 对原始方程求导F1(假定方程有解)
2。 任取一点X0,求出曲线上的点(X0,Y0),并求出斜率F1(X0),作直线
3。 求出该直线和X轴的交点,并把新求出的X1作为新的迭代值
4。 重复1-3直到达到允许误差
DONE~~