分数处理
我们为分数设置一个结构体,结构体内设置两个变量,一个分子,一个分分母
struct fraction{
long long up;//分子
long long down;//分母
};
接下来是分数的加减乘除运算,我们以加法为例,体会一下分为分子分母的好处
fraction frac_add(fraction a,fraction b)//分数相加,其余运算可以类比
{
fraction temp;
temp.down=a.down*b.down;
temp.up=a.up*b.down+b.up*a.down;
return reduction(temp);//返回规范化后的结果,防止越界
}
我们在上面的加法代码中看到了一个函数:reduction,这个是用来规范化分数的,什么是规范化,就是把每一个分数进行化简,让分子分母没有公因数,把负号统一转移给分子,这样其余函数处理分数时,比较方便
fraction reduction(fraction a)//分数规范化函数
{
if(a.down<0)//负号转移给分子
{
a.down=-a.down;
a.up=-a.up;
}
if(a.up==0)//分子为0是=时置分母为1
a.down=1;
else//约去分子分母的最大公因数
{
int d=gcd(abs(a.up),abs(a.down));
a.up/=d;
a.down/=d;
}
return a;
}
分数输出函数,以带分数形式输出
void frac_output(fraction a)//分数输出函数
{
a=reduction(a);
if(a.down==1)
printf("%lld",a.up);
else if(abs(a.up)>a.down)
printf("%lld %lld/%lld",a.up/a.down,abs(a.up)%a.down,a.down);
else
printf("%lld/%lld",a.up,a.down);
}
==========================================愉快的分割线============================================
大数处理
大数的数据结构:
struct bign{//大整数的数据定义
int d[1000];
int len;
bign()
{
memset(d,0,sizeof(d));
len=0;
}
};
我们以字符串形式读入大数,数字反向存储,方便从低位开始处理数据
bign change(string str)//以字符串形式读取数字并存储
{
//假设数字是123456789,在这里从d[0]~d[9]存储的是987654321
//这是为了数字可以方便的从低位开始处理
bign a;
a.len=str.length();
for(int i=0;i<a.len;i++)
a.d[i]=str[a.len-i-1]-'0';
}
大数比较
int compare(bign a,bign b)
{
if(a.len>b.len)
return 1;
else if(a.len<b.len)
return -1;
else
{
for(int i=a.len-1;i>=0;i--)//从高位到低位进行比较
{
if(a.d[i]>b.d[i])//a大
return 1;
else if(a.d[i]<b.d[i])//b大
return -1;
}
}
return 0;//两数相等
}
大数运算,加法
bign add(bign a,bign b)//大数相加,如果有负数要做相应调整
{
bign c;
int carrry=0;//进位
for(int i=0;i<a.len||i<b.len;i++)//取二者较长为界限
{//取较长为界限不用担心正确性,因为所有位都用0初始化了
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;
}
减法
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]+=10;
a.d[i+1]-=1;
}
c.d[c.len++]=a.d[i]-b.d[i];
}
while(c.len-1>=1&&c.d[c.len-1]==0)//截取尾端的0
c.len--;//去除高位的0,同时至少保留一位最低位
return c;
}
乘法
bign multi(bign a,int b)//大数乘小数,小数在后
{
bign c;
int carry=0;
for(int i=0;i<a.len;i++)
{
int temp=a[i]*b+carry;
c.d[c.len++]=temp%10;//个位作为结果
carry=temp/10;//高位部分作为新的进位
}
while(carry!=0)
{
c.d[c.len++]=carry%10;
caryy/=10;
}
return c;
}
除法
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)//不够除,置为0
c.d[i]=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;
}
注意,减法和除法需要截取高位0,因为算法一开始是把它们从低位开始对其的,高位补0
考虑到负数的问题,如果是a,b是负数,我们可以转为绝对值做相应运算,如果是a和b做减法可能会产生负数,那么我们可以先用compare函数判断大小,再转为绝对值进行运算,输出时加上负号即可