1. 程式人生 > >筆試題:求一個數的開方,如根號2

筆試題:求一個數的開方,如根號2

題目:

求一個數的開方,如根號2,要求保留到小數點位後10位。

解法一:

也就相當於求一個數n的開方,我們用二分法進行計算,不斷縮小範圍,但是double、float不能直接等,最後如果 mid*mid和n的相差不超過一個指定的最小值。那麼所求的mid就是我們得到的值。之後我們將它按照小數點之後10位打印出來就行。 程式碼如下:
#include<iostream>
#include<math.h>
#include<iomanip>
using namespace std;
void kaifang(double n,double accuracy);
void main()
{
	double n=2;
	double accuracy=1e-10;
	kaifang(n,accuracy);
}

void kaifang(double n,double accuracy)
{
	if(n<0) return;
	double low=0;
	double high=n;
	double kf=0;
	while(low<high)
	{
		double mid=(low+high)/2;
		double pf=mid*mid;
		double res=pf-n;
		if(abs(res)<accuracy)
		{
			kf=mid;
			break;
		}
		else if(pf>n)
			high=mid;
		else
		    low=mid;
			
	}
	printf("%.10f\n",kf);//利用原始的c打印出小數點後10位
	cout<<setiosflags(ios::fixed)<<setprecision(10);//利用C++的操作符打印出小數點後10位
	cout<<kf;
}
注意: setprecision 也是包含在名稱空間iomanip 中的C++ 操作符,該操作符的作用是設定浮點數小數點後的位數;
setprecision(2) 的意思就是小數點輸出的精度,即是小數點右面的數字的個數為2,C++預設的流輸出數值有效位是6。。 #include <iomanip> 
它是I/O流控制頭文 件,就像C裡面的格式化輸出一樣.以下是一些常見的控制函式的: 
dec 置基數為10 相當於"%d" 
hex 置基數為16 相當於"%X" 
oct 置基數為8 相當於"%o" 
setioflags(ios::fixed) 固定的浮點顯示 
setioflags(ios::scientific) 指數表示 

setiosflags(ios::left) 左對齊 
setiosflags(ios::right) 右對齊 
setiosflags(ios::skipws 忽略前導空白 
setiosflags(ios::uppercase) 16進位制數大寫輸出 
setiosflags(ios::lowercase) 16進位制小寫輸出 
setiosflags(ios::showpoint) 強制顯示小數點 
setiosflags(ios::showpos) 強制顯示符號 
舉例: 
#include <iostream> 
#include <iomanip> 
using namespace std; 

int main() 

cout<<12345.0<<endl;//輸出"12345" 
cout<<setiosflags(ios::fixed)<<setprecision(3)<<1.2345<<endl;輸出"1.235" 
cout<<setiosflags(ios::scientific)<<12345.0<<endl;//輸出"1.234500e+004 " 
cout<<setprecision(3)<<12345.0<<endl;//輸出"1.235e+004 " 
return 0; 
}

解法二:

利用牛頓迭代法,任意整數x,我任猜它的平方根為y,如果不對或精度不夠準確,那我令y = (x+x/y)/2。如此迴圈反覆下去,y就會無限逼近x的平方根。 例如,我想求根號2等於多少。假如我猜測的結果為4,雖然錯的離譜,但你可以看到使用牛頓迭代法後這個值很快就趨近於根號2了:

(       4  + 2/   4     ) / 2 = 2.25
(    2.25  + 2/   2.25  ) / 2 = 1.56944..
( 1.56944..+ 2/1.56944..) / 2 = 1.42189..
( 1.42189..+ 2/1.42189..) / 2 = 1.41423..

….


    這種演算法的原理很簡單,我們僅僅是不斷用(x,f(x))的切線來逼近方程x^2-a=0的根。根號a實際上就是x^2-a=0的一個正實根,這個函式的導數是2x。也就是說,函式上任一點(x,f(x))處的切線斜率是2x。那麼,x-f(x)/(2x)就是一個比x更接近的近似值。代入f(x)=x^2-a得到x-(x^2-a)/(2x),也就是(x+a/x)/2。
程式碼如下:
double NewtonIteration (double n,double accuracy)
{
	double y=1;//可任意取值(除零以外)
	long m=0;
	while(abs((y*y)-n)>accuracy)
	{
		y=(y+(n/y))/2;
		m++;
	}
	printf("%.10f\n",y);//利用原始的c打印出小數點後10位
	cout<<setiosflags(ios::fixed)<<setprecision(10)<<y<<endl;//利用C++的操作符打印出小數點後10位
	cout<<"while number: "<<m;
	return y;
}

最後我們比較兩個演算法執行while迴圈的次數,給出完整程式碼如下:
#include<iostream>
#include<math.h>
#include<iomanip>
using namespace std;
void kaifang(double n,double accuracy);
double NewtonIteration (double n,double accuracy);
void main()
{
	double n=2;
	double accuracy=1e-10;
	kaifang(n,accuracy);
	NewtonIteration (n,accuracy);
}

void kaifang(double n,double accuracy)
{
	if(n<0) return;
	double low=0;
	double high=n;
	double kf=0;
	long m=0;
	while(low<high)
	{
		double mid=(low+high)/2;
		double pf=mid*mid;
		double res=pf-n;
		if(abs(res)<accuracy)
		{
			kf=mid;
			break;
		}
		else if(pf>n)
			high=mid;
		else
		    low=mid;
		m++;
			
	}
	printf("%.10f\n",kf);//利用原始的c打印出小數點後10位
	cout<<setiosflags(ios::fixed)<<setprecision(10)<<kf<<endl;//利用C++的操作符打印出小數點後10位
	cout<<"while number: "<<m<<endl;
}

double NewtonIteration (double n,double accuracy)
{
	double y=1;//可任意取值(除零以外)
	long m=0;
	while(abs((y*y)-n)>accuracy)
	{
		y=(y+(n/y))/2;
		m++;
	}
	printf("%.10f\n",y);//利用原始的c打印出小數點後10位
	cout<<setiosflags(ios::fixed)<<setprecision(10)<<y<<endl;//利用C++的操作符打印出小數點後10位
	cout<<"while number: "<<m;
	return y;
}

執行結果為:
從結果中可以看出牛頓法的計算效率好一些,我還試了其他的y值,若y值為4或者3時,仍然while4次,就算是8,也只需while 7次,相較於29次的二分法,效率要高很多。