1. 程式人生 > >積跬步至千里——演算法強化訓練(7)大數問題

積跬步至千里——演算法強化訓練(7)大數問題

大數問題:當感覺用long或int已經不能滿足要求,需要考慮大數問題。大數問題將普通的數學運算,轉移到字串上的操作。大數操作有很多寫法,但是我習慣用以下這個模板,因為對加法 乘法 減法都是一個路子,便於記憶和理解。

大數乘法:

string Multi(string str1, string str2)
{
	int len1 = str1.length();
	int len2 = str2.length();
	reverse(str1.begin(),str1.end());
	reverse(str2.begin(),str2.end());
	
	vector<int> vec(len1+len2,0);
	int i,j,k,p;
	//計算每位的乘積
	for (i=0;i<len1;++i)
		for(j=0;j<len2;++j)
	       vec[i+j] += (str1[i]-'0')*(str2[j]-'0');
	//處理每一位和進位
	for (i=0;i<vec.size()-1;++i)
	{
		vec[i+1] +=vec[i]/10;
		vec[i] = vec[i]%10;
	}
	//儲存計算結果
	for (p=vec.size()-1;p!=-1;p--)//找到最高位
	   if(vec[p]!=0) break;
	if(p==-1) p=0;//只有一位零時候
	//構造結果字串
	string res(p+1,'0');
	for (k = p;k>=0;--k)
		res[p-k] = char(vec[k]+'0');
	
	return res;
}

大數加法:

string AddString(string str1 ,string str2)
{
	if(str1.length()<str2.length()) 
		swap(str1,str2);
	int len1 = str1.length();
	int len2 = str2.length();
	reverse(str1.begin(),str1.end());
	reverse(str2.begin(),str2.end());

    vector<int> vec(len1+1,0);
	int i,k,p;
	for (i=0;i<len1;++i)
	{
		if(i<len2) vec[i] = (str1[i]-'0')+(str2[i]-'0');
		else vec[i] = str1[i]-'0';
	}
	for (i=0;i<len1;++i)
	{
		vec[i+1] +=vec[i]/10;
		vec[i] = vec[i]%10;
	}
	for (p=vec.size()-1;p!=-1;p--)
		if(vec[p]!=0) break;
	if(p==-1) p=0;
	string res(p+1,'0');
	for (k = p;k>=0;--k)
		res[p-k] = char(vec[k]+'0');

	return res;
	
}

大數減法: 

string MinusString(string s1,string s2)
{
	bool minus = false;
	if(s1.length()<s2.length() || (s1.length()==s2.length() && s1<s2))
	{
		swap(s1,s2);
		minus = true;
	}
	int len1 = s1.length();
	int len2 = s2.length();
	reverse(s1.begin(),s1.end());
	reverse(s2.begin(),s2.end());

	vector<int> vec(len1,0);
	int i,p,k;
	int borrow=0;
	for (i=0;i<len1;++i)
	{
		if(i<len2) vec[i] = (s1[i]-'0')-(s2[i]-'0');
		else vec[i] = s1[i]-'0';
	}
	for (i=0;i<len1;++i)
	{
		if(vec[i]<0 || (vec[i]==0&&borrow==1))
		{
			vec[i] += 10-borrow;
			borrow =1;
		}
		else if(vec[i]>0)
		{
			vec[i] -= borrow;
			borrow = 0;
		}
	}
	//儲存計算結果
	for (p=vec.size()-1;p!=-1;p--)//找到最高位
		if(vec[p]!=0) break;
	if(p==-1) p=0;//只有一位零時候
	//構造結果字串
	string res(p+1,'0');
	for (k = p;k>=0;--k)
		res[p-k] = char(vec[k]+'0');

	if(minus) res = "-"+res;
	return res;
}

通過以上幾個程式碼,可以看到大數的操作應用了一個類似的模板

判斷基本條件——反轉兩個輸入串——構造vector,並進行運算處理——構造結果串

大數階乘:階乘在n為十幾的時候就會發生溢位,所以要用字串來處理

void Factorial(int n) //n>=1
{
	int a[100];
	a[0]=1;
	int j,p=1,carry;//p代表當前一共有多少位,carry用於儲存進位
	for (int i=2;i<=n;++i)
	{
		for (j=0,carry=0;j<p;++j)
		{
			a[j] = a[j]*i+carry;
			carry = a[j]/10;
			a[j] = a[j]%10;
		}
		while(carry!=0)
		{
			a[j++] = carry%10;
			carry = carry/10;
		}
		p = j;
	}
	for (int i = p-1;i>=0;--i)
		cout<<a[i];
}

求2的n次方:

string Power(int n) //n<1024
{
	string result;
	if(n==1) return "2";
	if(n & 0x1)//奇數
	{
		result = Multi(Power((n-1)/2),Power((n-1)/2));
		result = Multi(result,"2");
	}
	else
		result = Multi(Power(n/2),Power(n/2));
	return result;
}