codevs 3119 高精度练习之大整数开根


利用牛顿迭代法,来算。但是需要大数运算支持。此外牛顿迭代法中初值选取时,精确求出前5位。求前5位通过手算平方根的办法来求。用int类型即可。

手算平方根 参考 http://wenku.baidu.com/link?url=5fD_xTiIXbtRXatkSV2xAj2Y4lOCbg7Mkx0EArO8Nq3jKyR8vly-y6FPGhaxnMBgFX-1BxHynACp0bcfls7xiLW2hwFnDDJumFz_KHqyMjG

#include <iostream>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <string>
#include <cmath>
using namespace std;

class BigInt{ 
	friend istream& operator>>(istream &is,BigInt &bi);
	friend ostream& operator<<(ostream &is,const BigInt &bi);
private:
	vector<int> num; // 存放数字
	char sig;
public:
	BigInt(){
		sig = '+';
	}
	BigInt operator-(BigInt& other);
	BigInt operator/(BigInt& other);  // 大数除法,仅求出算整数部分
	BigInt operator+(BigInt& other);  // 大数加,为大数乘法提供帮助
	BigInt operator*(BigInt& other);  // 大数乘,为大数乘法提供帮助
	BigInt biSqrt();     // 大整数开平方仅求出整数部分 // 二分法求开方
	void   divide(BigInt& other,BigInt& result);
	unsigned locate(unsigned);
	bool less(const BigInt& other);   // A 是否小于 B
	bool gt(const BigInt& other);
	void delTailZero();
	void delHeadZero();
	BigInt newtonSqrt();    // 牛顿迭代求开方
	void setSig(char s){
		sig = s;
	}
	char getSig() const{
		return sig;
	}
};


bool BigInt::less(const BigInt& other){
	if(num.size() != other.num.size()){
		return num.size() < other.num.size();
	}
	else{
		for(unsigned i = 0; i < num.size(); i++){
			if(num[i] < other.num[i])
				return true;
			else if(num[i] > other.num[i])
				return false;
		}
		return false;		
	}
}

bool BigInt::gt(const BigInt& other){  // 传去比较函数,然后在传入比较器。
	if(num.size() != other.num.size()){
		return num.size() > other.num.size();
	}
	else{
		for(unsigned i = 0; i < num.size(); i++){
			if(num[i] > other.num[i])
				return true;
			else if(num[i] < other.num[i])
				return false;
		}
		return false;		
	}
}

unsigned BigInt::locate(unsigned pos){     // 下标转换 o(1)
	return pos >= num.size() ? 0 : num[num.size() - pos - 1];
}

void BigInt::delTailZero(){
	while (0 == num.back() && num.size() > 1)
	{
		num.pop_back();
	}
}

void BigInt::delHeadZero(){  // 删除头0
	reverse(num.begin(),num.end());// n/2
	while (num.size() > 1 && 0 == num.back())
	{
		num.pop_back();
	}
	reverse(num.begin(),num.end()); // n/2
}

BigInt BigInt::operator+(BigInt& other){
	unsigned carry = 0,
		larger_size,
		partial_sum;

	BigInt sum;
	if (num.size() > other.num.size())
		larger_size = num.size();
	else
		larger_size = other.num.size();
	for (unsigned i = 0; i < larger_size; i++)
	{
		partial_sum = locate (i) + other.locate(i) +  carry;
		carry = partial_sum / 10;
		sum.num.push_back (partial_sum % 10);
	}
	if (carry == 1)
		sum.num.push_back (carry);
	reverse (sum.num.begin(), sum.num.end());
	return sum;

}


BigInt BigInt::operator-(BigInt& other)
{
	BigInt result,
		*bigger = this,
		*little = &other;

	unsigned borrow = 0;

	if((*this).less(other)){
		bigger = &other;
		little = this;
		result.setSig('-');
	}
	
	unsigned bi = 0,
		li = 0;
	
	for(unsigned i = 0; i < (*bigger).num.size(); i++){
		bi = (*bigger).locate(i);
		li = (*little).locate(i);
		if(bi < li + borrow){
			result.num.push_back(bi+10 - li - borrow);
			borrow = 1;
		}else{

			result.num.push_back(bi - li - borrow);
			borrow = 0;
		}
	}
	result.delTailZero();
	reverse(result.num.begin(),result.num.end());
	return result;
}

BigInt BigInt::operator*(BigInt& other){
	BigInt interResult,
			result;        //  12938 A
	unsigned carry = 0;    //*   123 B 
	for (unsigned i = 0 ; i < other.num.size(); i++)   // B
	{                              
		carry = 0;
		unsigned tmpi = i;
		interResult.num.clear();  // 计算前,将中间结果清除。
		while (tmpi > 0)
		{
			interResult.num.push_back(0);
			tmpi--;
		}
		for (unsigned j = 0; j < num.size(); j++)  // A
		{
			unsigned product = other.locate(i) * locate(j) + carry;
			carry = product / 10;
			interResult.num.push_back(product%10);   // 保存临时结果
		}
		if (carry > 0)
		{
			interResult.num.push_back(carry);
		}
		reverse(interResult.num.begin(),interResult.num.end());
		result = interResult + result;
	}
	return result;
}

BigInt BigInt::operator/(BigInt& other){
	// 仅仅保留整数部分 A > B , A == B A < B 。 所以有这三种情况。 A >= B 进行除法运算。
	BigInt result;  // 截取一段去除 other
	BigInt tmp;
	unsigned index = 0;  
	unsigned quotient = 0;  // 商
	bool contact = false;   
	if((*this).less(other)){
		result.num.push_back(0);
	}else{ // 进行除法运算
		index = other.num.size();  // 构造一个长度为 len的临时对象
		tmp.num.assign(num.begin(),num.begin()+index);
		if (index == num.size())                // 小于,大于,== 三个操作
		{
			while(!tmp.less(other)){
				quotient++;
				tmp = tmp - other; // = 9
			}
			result.num.push_back(quotient);
		}
		else
		{
			while(index < num.size()){
				quotient = 0;
				while(tmp.less(other)){    // 首先除数肯定比被除数大,若小整数部分肯定为了,所以追加数字,住

					if(index < num.size()){
						if(contact == true)            // 当一次拼接完成后,才判断是商0,还是商1.
							result.num.push_back(0);   // 小于某数补0;
						tmp.num.push_back(num.at(index));
						contact = true;
					}
					else{
						break;
					}
					index += 1;
				}
				contact = false;
				tmp.delHeadZero();
				while(!tmp.less(other)){
					quotient++;
					tmp = tmp - other; // = 9
				}
				tmp.delHeadZero();
				if(index <= num.size())  // 这里是 <=  而上面是小于 因为上面完成了index += 1 操作,所以这里少1。
					result.num.push_back(quotient);		 
			}
		}
	}
	return result;
}

istream& operator>>(istream &is,BigInt &bi)
{	
	const char lOWBOUND = '0';
	string num;
	is >> num;
	for(size_t i = 0; i < num.size(); i++)
		bi.num.push_back(num[i] - lOWBOUND);
	return is;
}

ostream& operator<<(ostream &os,const BigInt &bi)
{
	if(bi.getSig() == '-'){
		os <<"-";
	}
	for(size_t i = 0; i < bi.num.size(); i++){
		//os << bi.num[i];
		printf("%d",bi.num[i]);
	}
	return os;
}

BigInt BigInt::newtonSqrt() // 牛顿迭代法求 平方根,仅算整数部分
{
	BigInt result,
		biTwo,  // 2的大数表示 ,
		lastResult;
	biTwo.num.push_back(2);
	unsigned resultLen = (unsigned)((this->num.size())/2.0 + 0.5); // 开方后数字位数
	// 通过手算开放的办法先计算出结果的前5位
	int result1 = 0; // 存放计算的中间及结果
	int i1 = 0,
		n1 = 0,          // 截取的两位数和后面两位
		n2 = 0;          // 待减的两位数字

	if(this->num.size()>100)  // 大于100位数,预处理。精确算出前5位。
	{
    	    if (this->num.size()%2)  // 共偶数位,先取一位算
    	    {
    		i1 = 1; // 下一次计算从第i位开始
    		
    		int s = (int)(sqrt(this->num[0]*1.0));
    		result1 = result1*10 + s;
    		lastResult.num.push_back(s);
    		n1 = this->num[0] - s*s;
    
    	    }
    	    else
    	    {
    		i1 = 2;
    		int sum = this->num[0]*10+this->num[1];
    		int s = (int)(sqrt(sum*1.0));
    		lastResult.num.push_back(s);
    		n1 = sum - s*s;
    	    }
    	    for ( ; i1< 8; i1 += 2)  //  只预处理前8位
    	    {
    		n1 = n1*10 + this->num[i1];
    		n1 = n1*10 + this->num[i1+1];
    
    		n2 = result1*20;
    		if (n1 < n2 )
    		{
    			lastResult.num.push_back(0);
    			result1 = result1 * 10;  // 不够的话商0(左移一位)
    			continue;
    		}
    		int j = 1;
    		for (; j <= 9; j++)
    		{
    			if ((n2+1)*j > n1)
    			{
    				break;
    			}
    			else
    			{
    				n2++;
    			}
    		}
    		lastResult.num.push_back(n2%10);
    		result1 = result1*10 + n2%10;
    
    		n1 = n1 - n2*(j-1);
    	    }
        }
	for (unsigned i = (i1/2.0 + 0.5); i < resultLen; i++)  // 构建初始值,前5位由手算除法
	{
		lastResult.num.push_back(9);
	}	
	while (true)   // 牛顿迭代求 结果
	{
	    BigInt tmp = (*this)/lastResult;
		result = (lastResult + tmp)/biTwo;
		if(result.less(lastResult))
		{
			lastResult = result;
		}
		else
		{
			break;
		}
	}
	return lastResult;
}

int main(int argc, char** argv) {

	BigInt A; 
	cin >> A;
	cout << (A.newtonSqrt())<< endl;
	return 0;
}




你可能感兴趣的:(codevs 3119 高精度练习之大整数开根)