大数阶乘的一些经典问题解决

计算N!结果:首先实现利用字符串和vector<char>实现大数的加法:两个字符串从最低位开始相加,把相加的结果存到char类型容器中,注意在相加的过程中设置一个进位符,直到较短的字符串相加完毕;之后把进位符加到较长的字符串余位即可,在加的过程中同时注意的还是进位符的加法。最后如果进位符大于0则放入charvector中,这样生成的字符串是倒置的,需要reverse之后复制到字符串中返回即可。

下面利用字符串和容器实现大数乘法:中心思想就是乘法的本质就是加法,选择一个较短的数从个位开始和较长的数相乘,个位的时候就是把较长的数相加多次,若是十位便是相加多次后结果后面加一个0,百位加两个,一次类推,最后把结果相加就是最终结果。

 

求末尾0的个数:N!进行质因数分解: N!=2X*3Y*5Z…,因为10=2*5,所以M25的个数即XZ有关。每一对25都可以得到10,故M=min(X,Z)。因为能被2整除的数出现的频率要比能被5整除的数出现的频率高,所以M=ZZ =[N/5] + [N/52]+ [N/53] + …

[N/5] 表示不大于N的的数中5的倍数贡献一个5, [N/52]表示不大于N的数中52的倍数在贡献一个5……(这里用到的一个想法就是,如果看n可以贡献多少个m,则使用n/m计算即可

 

N!使用二进制表示的时候最低位1的位置:判断最后一个二进制是否为0:若为0将二进制数右移1位(移位和除法运算),即为商;若为1,则说明这个数是奇数,不能被2整除。所以判断N!的二进制表示中最低位为1的位置的问题可以转换为求N!中含有质因数2的个数的问题。即位置为N!含有质因数2的个数加1.N!中含有质因数2的个数等于:[N/2]+[N/4]+[N/8]+…

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

string bigSum(string str1, string str2){
	string result="";
	int len1=str1.length();
	int len2=str2.length();
	int i,j,AddOn=0,sum;
	vector<char> vSum;
	vector<char>::iterator it;
	//注意“,”和“&&”运算符的区别,前者只返回
	//最后一个表达式的结果(致命bug所在)
	for (i=len1-1,j=len2-1;i>=0 && j>=0;i--,j--){
		sum=str1.at(i)-'0'+str2.at(j)-'0'+AddOn;
		if(sum>=10)
			AddOn=1;
		else
			AddOn=0;
		vSum.push_back(sum%10+'0');
	}

	//分别处理两个字符串长度不相同时候的情况
	//注意相等的时候不用处理
	if(len1>len2)
	{
		for(;i>=0;i--){
			sum=str1.at(i)-'0'+AddOn;
			if(sum>=10)
				AddOn=1;
			else
				AddOn=0;
			vSum.push_back(sum%10+'0');
		}
	}
	else if(len2>len1)
	{
		for (;j>=0;j--){
			sum=str2.at(j)-'0'+AddOn;
			if(sum>=10)
				AddOn=1;
			else
				AddOn=0;
			vSum.push_back(sum%10+'0');
		}
	}
	if(AddOn>0)
		vSum.push_back(AddOn+'0');
	reverse(vSum.begin(),vSum.end());
	for (it=vSum.begin();it!=vSum.end();it++){
		result+=*it;
	}
	return result;
}

string bigMul(string str1, string str2){
	int len1=str1.length();
	int len2=str2.length();
	int i,j,k;
	string result="0";
	if (len1<len2)
	{
		for(i=len1-1;i>=0;i--)
		{
			string tmpSum="0";
			for (j=str1.at(i)-'0';j>0;j--)
				tmpSum=bigSum(str2,tmpSum);
			for(k=i;k<len1-1;k++)
				tmpSum.append("0");
			result=bigSum(result,tmpSum);
		}
	}
	else
	{
		for (i=len2-1;i>=0;i--)
		{
			string tmpSum="0";
			for(j=str2.at(i)-'0';j>0;j--)
				tmpSum=bigSum(str1,tmpSum);
			for(k=i;k<len2-1;k++)
				tmpSum.append("0");
			result=bigSum(result,tmpSum);
		}
		
	}
	return result;
}

string fac(int n){
	string res="1";
	char ch[100];
	for(int i=n;i>0;i--)
		res=bigMul(res,itoa(i,ch,10));
	return res;
}

int countZero(int n){
	int count=0;
	while (n/5!=0)
	{
		count+=n/5;
		n/=5;
	}
	return count;
}

int pos(int n){
	int pos=0;
	while(n/2!=0){
		pos+=n/2;
		n/=2;
	}
	return ++pos;
}

int main(){
	string str1="12";
	string str2="6";
	int n=4;
	//string res=bigSum(str1,str2);
	//string res=bigMul(str1,str2);
	cout << fac(n) << endl;
	cout << countZero(n) << endl;
	cout << pos(n) << endl;
	return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值