《编程思维与实践》1070.复数幂

《编程思维与实践》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)=(acbd)+(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);
    while(R.v[R.cnt-1]==0&&R.cnt>=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");
	}
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值