从零开始构建分数的四则运算体系——以【PAT B1034 有理数四则运算】为例

PAT 乙级练习 题解合集

本题链接

题目

本题要求编写程序,计算 2 个有理数的和、差、积、商。

输入格式:
输入在一行中按照 a1/b1 a2/b2 的格式给出两个分数形式的有理数,其中分子和分母全是整型范围内的整数,负号只可能出现在分子前,分母不为 0。

输出格式:
分别在 4 行中按照 有理数1 运算符 有理数2 = 结果 的格式顺序输出 2 个有理数的和、差、积、商。注意输出的每个有理数必须是该有理数的最简形式 k a/b,其中 k 是整数部分,a/b 是最简分数部分;若为负数,则须加括号;若除法分母为 0,则输出 Inf。题目保证正确的输出中没有超过整型范围的整数。

输入样例 1:

2/3 -4/2

输出样例 1:

2/3 + (-2) = (-1 1/3)
2/3 - (-2) = 2 2/3
2/3 * (-2) = (-1 1/3)
2/3 / (-2) = (-1/3)

输入样例 2:

5/3 0/6

输出样例 2:

1 2/3 + 0 = 1 2/3
1 2/3 - 0 = 1 2/3
1 2/3 * 0 = 0
1 2/3 / 0 = Inf

思路

写在前面的废话

我写这道题的思路是定义一个结构Fraction来表示任意分数,并以此为基础把分数的化简四则运算打印全部写成函数,属于过于一般化的做法(意思就是我写复杂了,光是为了做这个题完全没必要)。如果只追求这道题的 AC 的话可以把输入看成四个分离的数字来处理(没错我就是写完才发现的),如果你追求用简洁的方式把这道题做对,请参考柳神的代码。如果你对分数运算的一般化处理感兴趣的话,请继续阅读。完整的 AC 代码在最下面。


定义一个分数 Fraction

那么我们就从零开始了,首先我们得想个办法存储一个分数,结构体是个不错的选择(或者 C艹 选手愿意写成类也可以)。

虽然题目保证正确的输出中没有超过整型范围的整数,但是在运算中可能有溢出的情况出现,为保险起见,全部定义成long long类型。

typedef long long LL;
typedef struct {
	LL up, down;	// 分子和分母 
} Fraction;

如上所示,成员up表示分子,down表示分母。


化简 reduction

在分数的运算中,化简的重要性仅次于定义,因为化简使得我们体系中的分数都是标准化的。化简意味着三件事情:

  1. 如果分子是0的话,分母的大小是没有意义的,把分母变成1方便计算;
  2. 为了统一起见,我们希望负号只能出现在分子中。所以如果分母是负数,分子和分母都等于各自的相反数;
  3. 约分。约分实质上就是分子和分母同时除以他们的最大公约数。如果你不了解最大公约数的算法,可以搜索辗转相除法

以下就是化简相关的代码,注意在求最大公约数的时候要对分子取绝对值(为什么分母不用取绝对值?因为第 2 步保证了分母非负):

// 返回 a 的绝对值 
LL abs(LL a) {	
	return a < 0 ? -a : a;
} 
// 返回 a 和 b 的最大公约数 
LL gcd(LL a, LL b) {	
	return b ? gcd(b, a % b) : a; 
} 
// 对分数 a 进行约分,返回约分结果
Fraction reduction(Fraction a) {
	if (a.up == 0) {
		a.down = 1;
		return a;
	}
	if (a.down < 0) {
		a.up = -a.up;
		a.down = -a.down;
	} 
	LL g = gcd(abs(a.up), a.down);
	a.up /= g;
	a.down /= g;
	return a;
} 

加法 add

这里我们需要回忆一下小学的分数加法公式:
b a + d c = b × c + a × d a × c \frac{b}{a}+\frac{d}{c}=\frac{b\times c+a\times d}{a\times c} ab+cd=a×cb×c+a×d
接着不要忘记对结果进行化简。代码如下:

Fraction add(Fraction a, Fraction b) {
	Fraction ans;
	ans.up = a.up * b.down + a.down * b.up;
	ans.down = a.down * b.down;
	ans = reduction(ans);
	return ans;
}

减法 sub

b a − d c = b a + − d c \frac{b}{a}-\frac{d}{c}=\frac{b}{a}+\frac{-d}{c} abcd=ab+cd

Fraction sub(Fraction a, Fraction b) {
	Fraction ans;
	b.up = -b.up;
	ans = add(a, b);	// 加的同时已经化简过了
	return ans;
}

乘法 mul

b a × d c = b × d a × c \frac{b}{a}\times \frac{d}{c}=\frac{b\times d}{a\times c} ab×cd=a×cb×d

Fraction mul(Fraction a, Fraction b) {
	Fraction ans;
	ans.up = a.up * b.up;
	ans.down = a.down * b.down;
	ans = reduction(ans);
	return ans;
}

除法 div

b a ÷ d c = b × c a × d    ( d ≠ 0 ) \frac{b}{a}\div \frac{d}{c}=\frac{b\times c}{a\times d}\ \ (d\ne 0) ab÷cd=a×db×c  (d=0)
注意如果d == 0即我们代码中b.up == 0的话需要特殊处理,这里把结果的分母变成-1来表示无穷。

至此,四则运算已经结束,还剩输出。

Fraction div(Fraction a, Fraction b) {
	Fraction ans;
	if (b.up == 0) {		// 用分母出现 -1 来表示无穷 
		ans.down = -1;
		return ans;
	} 
	ans.up = a.up * b.down;
	ans.down = a.down * b.up;
	ans = reduction(ans);
	return ans;
}

打印单个分数 print

  1. 先检查分母是不是-1,如果是的话代表无穷,直接输出Inf
  2. 如果分子是0,直接输出0
  3. 根据题目要求,如果是负数,两边要带圆括号,圆括号里面-开头;
  4. 如果存在整数部分,输出;
  5. 如果整数和分数部分都存在,输出一个空格;
  6. 如果存在分数部分,输出。
void print(Fraction a) {
	if (a.down == -1) { 	// 用分母出现 -1 来表示无穷 
		printf("Inf");
		return;
	}
	if (a.up == 0) {
		printf("0");
		return;
	} 
	if (a.up < 0)
		printf("(-");
	if (a.up / a.down)
		printf("%lld", abs(a.up) / a.down);
	if (a.up / a.down && abs(a.up) % a.down)
		putchar(' '); 
	if (a.up % a.down) 
		printf("%lld/%lld", abs(a.up) % a.down, a.down);
	if (a.up < 0)
		putchar(')');
}

打印带过程的一行计算 printCalculation

这里参数opration是一个函数指针,可以是以上四则运算中的任何一个函数。

typedef Fraction(*func)(Fraction, Fraction);	// 定义 fun_t 为函数指针类型 
void printCalculation(Fraction a, Fraction b, char ch, func opration) {
	print(a);
	printf(" %c ", ch);
	print(b);
	printf(" = ");
	Fraction ans = opration(a, b);
	print(ans);
	putchar('\n'); 
} 

所有函数都写完了,完整的 AC 代码在下面。


代码

#include <stdio.h>

typedef long long LL;
typedef struct {
	LL up, down;	// 分子和分母 
} Fraction;

LL abs(LL a) {		// 绝对值
	return a < 0 ? -a : a;
} 

LL gcd(LL a, LL b) {	// 最大公约数 
	return b ? gcd(b, a % b) : a; 
} 

Fraction reduction(Fraction a) {	// 约分 
	if (a.up == 0) {
		a.down = 1;
		return a;
	}
	if (a.down < 0) {
		a.up = -a.up;
		a.down = -a.down;
	} 
	LL g = gcd(abs(a.up), a.down);
	a.up /= g;
	a.down /= g;
	return a;
} 

Fraction add(Fraction a, Fraction b) {	// 加法
	Fraction ans;
	ans.up = a.up * b.down + a.down * b.up;
	ans.down = a.down * b.down;
	ans = reduction(ans);
	return ans;
}

Fraction sub(Fraction a, Fraction b) {	// 减法
	Fraction ans;
	b.up = -b.up;
	ans = add(a, b);
	return ans;
}

Fraction mul(Fraction a, Fraction b) {	// 乘法
	Fraction ans;
	ans.up = a.up * b.up;
	ans.down = a.down * b.down;
	ans = reduction(ans);
	return ans;
}

Fraction div(Fraction a, Fraction b) {	// 除法
	Fraction ans;
	if (b.up == 0) {		// 用分母出现 -1 来表示无穷 
		ans.down = -1;
		return ans;
	} 
	ans.up = a.up * b.down;
	ans.down = a.down * b.up;
	ans = reduction(ans);
	return ans;
}

void print(Fraction a) {	// 按照题目要求打印分数 
	if (a.down == -1) { 	// 用分母出现 -1 来表示无穷 
		printf("Inf");
		return;
	}
	if (a.up == 0) {
		printf("0");
		return;
	} 
	if (a.up < 0)		printf("(-");
	if (a.up / a.down)	printf("%lld", abs(a.up) / a.down);
	if (a.up / a.down && abs(a.up) % a.down)	putchar(' '); 
	if (a.up % a.down)	printf("%lld/%lld", abs(a.up) % a.down, a.down);
	if (a.up < 0)		putchar(')');
}

// 根据指定的操作数,运算符,运算函数输出
typedef Fraction(*func)(Fraction, Fraction);	// 定义 fun_t 为函数指针类型 
void printCalculation(Fraction a, Fraction b, char ch, func opration) {
	print(a);
	printf(" %c ", ch);
	print(b);
	printf(" = ");
	Fraction ans = opration(a, b);
	print(ans);
	putchar('\n'); 
} 

int main() {
	Fraction a, b;
	scanf("%lld/%lld", &a.up, &a.down);
	scanf("%lld/%lld", &b.up, &b.down);
	a = reduction(a);
	b = reduction(b);
	printCalculation(a, b, '+', add);
	printCalculation(a, b, '-', sub);
	printCalculation(a, b, '*', mul);
	printCalculation(a, b, '/', div);
	return 0;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值