第五章 数学问题:
5.1 简单数学问题:
PAT-B 1003. 我要通过!(20)PAT-B 1019. 数字黑洞 (20)
PAT-B 1049. 数列的片段和(20)
5.2 最大公约数与最小公倍数:
最大公约数的两种写法,运用的原来是欧几里得算法(即辗转相除法)
int gcd(int a,int b){
return !b?a:gcd(b,a%b);
}
int gcd(int a,int b){
if(b==0) return a;
else return gcd(b,a%b);
}
最小公倍数一般用 lcm(a,b) 表示,如果d为a和b的最大公约数,则最小公倍数为ab/d,
为了避免越界,最小公倍数最恰当的写法是a/d*b。由于d是a和b的最大公约数,因此a/d一定可以整除
5.3 分数的四则运算:详细代码
5.4 素数:
Eratosthenes筛法求素数(埃氏筛法)
5.5 质因子分解:
对于一个正整数n来说,如果它存在1和它本身之外的银子,那么一定是在sqrt(n)的左右成对出现。如果它存在[2,n]范围内的质因子,要么这些质因子全部小于等于sqrt(n),要么只存在一个大于sqrt(n)的质因子,而其它质因子全部小于等于sqrt(n)
思路:
step1:枚举1~sqrt(n)范围内的所有质因子p,判断p是否为n的因子,如果是,就给fac数组中增加质因子p,并初始化个数为0,然后只要p还是n的因子,就让n不断除以p,每次操作令p的个数加1,直到p不再是n的因子为止
struct factor{
int x,cnt; //x为质因子,cnt为个数
}fac[10];
if(n%prime[i]==0){ //prime[i]是质数表数组
fac[num].x = prime[i]; //如果prime[i]是质因子,记录它
fac[num].cnt = 0;
while(n%prime[i]==0){ //计算出质因子prime[i]的个数
fac[num].cnt++;
n/=prime[i];
}
num++;
} //如果p不是n的因子,直接跳过
step2:如果上面步骤结束后n仍然大于1,说明n有且仅有一个大于sqrt(n)的质因子(也有可能是n本身),这时需要把这个质因子加入fac数组,并令其个数为1
if(n!=1){
fac[num].x = n;
fac[num++].cnt = 1;
}
5.6 大整数运算
5.6.1 大整数的储存
使用数组,数组中的每一位就代表了存放的每一位,而为了方便随时获取大整数的长度,一般都会定义一个int型变量len来记录其长度,并和d数组成结构体,并在结构体内部加上构造函数
struct bign{
int d[1000];
int len;
bign(){
memset(d,0,sizeof(d));
len=0;
}
};
输入大整数时,一般都是先用字符串读入,然后把字符串另存为至bign结构体。由于使用char数组进行读入时,整数的高位会变成数组的低位,而整数的低位会变成数组的高位,因此为了让整数在bign中顺位存储,需让字符串倒着赋给d[]数组
bign change(char str[]){
bign a;
a.len = strlen(str);
for(int i=0;i<a.len;i++){
a.d[i] = str[a.len-i-1] - '0';
}
return a;
}
如果要比较两个bign变量的大小:先判断长度,不相等则长的大;若相等,则从高位到低位进行比较,知道出现某一位不等
5.6.2 大整数的高精度加法
这样写法的条件是两个对象都是非负整数,如果有一方是负的,可以去掉负号,做高精度减法
bign add(bign a,bign b){
bign c;
int carry=0; //carry是进位
for(int i=0;i<a.len || i<b.len;i++){
int temp = a.d[i]+b.d[i]+carry; //两个对应位与进位相加
c.d[c.len++] = temp%10; //个位数为该位的结果
carry = temp/10;
}
if(carry!=0){ //如果最后进位不为0,则直接赋值给结果的最高位
c.d[c.len++] = carry;
}
return c;
}
5.6.3 大整数的高精度减法
//默认大减小
bign sub(bign a,bign b){
bign c;
for(int i=0;i<a.len || i<b.len;i++){
if(a.d[i]<b.d[i]){
a.d[i+1]--;
a.d[i]+=10;
}
c.d[c.len++] = a.d[i] - b.d[i];
}
while(c.len-1>=1 && c.d[c.len-1]==0){
c.len--; //去除高位的0,同时至少保留一位最低位
}
return c;
}
5.6.4 高精度与低精度的除法
bign divide(bign a,int b,int& r){ //高精度除法,r为余数
bign c;
c.len = a.len;//被除数的每一位和商的每一位是一一对应的
for(int i=a.len-1;i>=0;i--){
r = r*10 + a.d[i];
if(r<b) c.d[i]=0; //不够除,商0
else{ //够除
c.d[i]=r/b; //商
r = r%b; //新余数
}
}
while(c.len-1>=1 && c.d[c.len-1]==0){
c.len--; //去除高位0
}
return c;
}
5.6.5 高精度与低精度的乘法
bign multi(bign a,bign b){
bign c;
int carry = 0;
for(int i=0;i<a.len;i++){
int temp = a.d[i]*b+carry;
c.d[i]=a.d[i]*b+carry;
carry=temp/10;
}
while(carry!=0){
//和加法不一样,乘法的进位可能不止一位,因此用while
c.d[c.len++] = carry%10;
carry/=10;
}
return c;
}