《编程思维与实践》1070.复数幂
题目
思路
思路比较简单,就是细节比较繁琐:
( a + b i ) ( c + d i ) = ( a c − b d ) + ( a d + b c ) i (a+bi)(c+di)=(ac-bd)+(ad+bc)i (a+bi)(c+di)=(ac−bd)+(ad+bc)i , 利用该公式分实部和虚部进行计算结果即可.
由于涉及加减和正负号,所以在大整数结构体中加入符号参数sign,
为了方便起见这里对加法和减法操作进行了一定的补充,
①针对加法,令sign一开始为0,a+b有以下四种可能,需要先进行判定:
1. a>0且b>0,则sign赋值为1;
2. a>0且b<0,则调用减法函数进行a-|b|,且将sign赋值为1;
3. a<0且b>0,则调用减法函数进行b-|a|,且将sign赋值为1;
4. a<0且b<0,则sign赋值为-1.
②针对减法,令sign一开始为0,a-b有以下四种可能,需要先进行判定:
1. a>0且b>0,则sign赋值为1;
2. a>0且b<0,则调用加法函数进行a+|b|,且将sign赋值为1;
3. a<0且b>0,则调用加法函数进行|a|+|b|,且将sign赋值为-1;
4. a<0且b<0,则调用减法函数进行|b|-|a|,且将sign赋值为-1.
在读取复数的实部和虚部时需要逆向遍历,因为大数据处理为了方便起见从个位开始存,
先判断是否存在虚部,之后判断是否存在实部,特别地,如果存在虚部但存的数位数为0时需要补一个1.
在输出复数的实部和虚部时同样也要判断是否存在实部和虚部,
特别地,如果实部前不输出加号或者只有虚部时前不输出加号;
同时,如果虚部存的数位数为1且值为1时直接输出i.
注意的点:
1. 0在之前题目的处理中均视为位数为1(因为要保留整数0),为了本题处理的方便,这里视为位数为0;
2. 注意到 ( 100 0 1000 ) 2 = 1 0 6000 (1000^{1000})^2=10^{6000} (10001000)2=106000 所以数组最大需要开6001;
3. 如果幂次为0则直接输出1;
4. 由于每次进行复数乘法时,实部和虚部的计算都需要用到上一步的实部和虚部,所以需要一个temp去存计算后的结果.
代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
#define N 6001
typedef struct{int cnt,v[N],sign;}BIGINT;
BIGINT carry(BIGINT S,int bin); //进位 bin表示进制 binary
BIGINT add(BIGINT S, BIGINT T,int sign); //两个大整数相加
BIGINT mul(BIGINT S, BIGINT T); //两个大整数相乘
BIGINT sub(BIGINT S, BIGINT T,int sign); //两个大整数相减 两个大整数均非负
void ReadBig(char *s,BIGINT* A,BIGINT* B); //读取实部虚部
void output(BIGINT R,BIGINT I); //输出复数
int main()
{
char s[1000];
int n;
scanf("%s%d",s,&n);
if(n==0)
{
printf("1");
}
else
{
BIGINT A={0,{0},1},B={0,{0},1};
ReadBig(s,&A,&B);
BIGINT R=A,I=B; //R实部 I虚部
for(int i=1;i<n;i++)
{
BIGINT temp=sub(mul(R,A),mul(I,B),0);
I=add(mul(R,B),mul(I,A),0);
R=temp;
}
output(R,I);
}
return 0;
}
BIGINT carry(BIGINT S,int bin) //进位 bin表示进制 binary
{
int flag=0;
for(int i=0;i<S.cnt;i++)
{
int temp=S.v[i]+flag;
S.v[i]=temp%bin;
flag=temp/bin;
}
if(flag)
{
S.v[S.cnt++]=flag;
}
return S;
}
BIGINT add(BIGINT S, BIGINT T,int sign) //两个大整数相加
{
if(sign==0)
{
if(S.sign==-1&&T.sign==-1)
{
sign=-1;
}
else if(S.sign==1&&T.sign==-1)
{
return sub(S,T,1);
}
else if(S.sign==-1&&T.sign==1)
{
return sub(T,S,1);
}
else
{
sign=1;
}
}
int max=S.cnt>T.cnt?S.cnt:T.cnt;
BIGINT R={max,{0},sign};
for(int i=0;i<max;i++)
{
R.v[i]=S.v[i]+T.v[i]; //依次进行普通乘法
}
R=carry(R,10);
for(int i=R.cnt-1;i>=0&&R.v[i]==0;i--) //去前置0(本题0的位数记为0)
{
R.cnt--;
}
return R;
}
BIGINT mul(BIGINT S, BIGINT T) //两个大整数相乘
{
BIGINT R={S.cnt+T.cnt,{0},S.sign*T.sign}; //位数最多为两者相加
for(int i=0;i<T.cnt;i++)
{
for (int j=0;j<S.cnt;j++)
{
R.v[i+j]+=S.v[j]*T.v[i]; //依此进行普通乘法
}
}
R=carry(R,10);
if(R.v[S.cnt+T.cnt-1]==0)
{
R.cnt--; //最高位0不统计在一个大整数的位数中
}
return R;
}
BIGINT sub(BIGINT S, BIGINT T,int sign) //两个大整数相减 两个大整数均非负
{
if(sign==0)
{
if(S.sign==-1&&T.sign==1)
{
return add(S,T,-1);
}
else if(S.sign==-1&&T.sign==-1)
{
return sub(T,S,1);
}
else if(S.sign==1&&T.sign==-1)
{
return add(S,T,1);
}
else
{
sign=1;
}
}
if(S.cnt<T.cnt)
{
return sub(T,S,-1);
}
else if(S.cnt==T.cnt&&sign!=-1)
{
for(int i=T.cnt-1;i>=0;i--) //注意是从最高位开始比
{
if(T.v[i]<S.v[i])
{
break;
}
else if(T.v[i]>S.v[i])
{
return sub(T,S,-1);
}
}
}
BIGINT R={S.cnt,{0},sign};
int flag=0; //借位
for(int i=0;i<R.cnt;i++)
{
if(S.v[i]-flag-T.v[i]>=0)
{
R.v[i]=S.v[i]-T.v[i]-flag;
flag=0;
}
else
{
R.v[i]=S.v[i]-T.v[i]-flag+10;
flag=1;
}
}
for(int i=R.cnt-1;i>=0&&R.v[i]==0;i--) //去前置0(本题0的位数记为0)
{
R.cnt--;
}
return R;
}
void ReadBig(char *s,BIGINT* A,BIGINT* B)
{
if(strchr(s,'i')!=NULL) //有虚部
{
int i;
for(i=strlen(s)-2;i>=0&&isdigit(s[i]);i--) //逆着遍历
{
B->v[B->cnt++]=s[i]-'0';
}
if(B->cnt==0) //补个1
{
B->v[B->cnt++]=1;
}
if(i>0) //有实部
{
B->sign=s[i]=='+'?1:-1;
i--;
while(i>=0&&isdigit(s[i]))
{
A->v[A->cnt++]=s[i--]-'0';
}
if(i==0)
{
A->sign=-1;
}
}
else if(i==0) //无实部且虚部前有符号
{
B->sign=-1;
}
}
else //无虚部
{
int i;
for(i=strlen(s)-1;i>=0&&isdigit(s[i]);i--)
{
A->v[A->cnt++]=s[i]-'0';
}
if(i==0)
{
A->sign=-1;
}
}
}
void output(BIGINT R,BIGINT I)
{
if(R.cnt!=0) //输出实部
{
if(R.sign==-1)
{
printf("-");
}
for(int i=R.cnt-1;i>=0;i--)
{
printf("%d",R.v[i]);
}
}
if(I.cnt!=0) //虚部不为0
{
if(R.cnt!=0) //存在实部的话需要输出正负号
{
if(I.sign==-1)
{
printf("-");
}
else if(I.sign==1)
{
printf("+");
}
}
else if(I.sign==-1) //不存在实部的话正号不需要输出
{
printf("-");
}
if(!(I.cnt==1&&I.v[0]==1)) // 1i直接输出i
{
for(int i=I.cnt-1;i>=0;i--)
{
printf("%d",I.v[i]);
}
}
printf("i");
}
}