1034 有理数四则运算 (20 分)
题意描述:
本题要求编写程序,计算 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
输入样例3:
-5/-21 21/5
输出样例3:
5/21 + 4 1/5 = 4 46/105
5/21 - 4 1/5 = (-3 101/105)
5/21 * 4 1/5 = 1
5/21 / 4 1/5 = 25/441
解题思路:
Alice: 这道题本来小学生都能做!哼
Bob: 什么叫本来??小学生也能做,我咋有点。。。不会呢。。。
Alice: 你看题啊,明明是分数的加减乘除再加上约分和求假分数。出题人偏偏写成有理数的四则运算,嘁~
Bob: 说的也是啊,小学生也会分数的四则运算。可是他们不会写程序啊。。。也难说啊,毕竟95后现在都秃然了。
Alice: 高中学的求最大公约数还记得怎么写吗 ?
Bob: 记得记得,辗转相除直到两个数字中有一个变成零,另外一个就是了。
Alice: 还不赖嘛。最简形式呢?有没有什么好的办法 ?
Bob: 还能有啥办法,先求出来分子和分母,然后约分,求假分数,再格式化呗。
Alice: 嗯,很朴素的想法。不过好像不够优雅。你说你怎么不能把程序写的像我一样优雅呢 ?
Bob: (失笑喷饭满案),快拿点水来。
代码:
def main():
temp = input().split()
# 以字符串的形式接收输入的两个分数
fenzi1 = int(temp[0].split('/')[0])
# 第一个分数的分子
fenmu1 = int(temp[0].split('/')[1])
# 第一个分数的分母
fenzi2 = int(temp[1].split('/')[0])
# 第二个分数的分子
fenmu2 = int(temp[1].split('/')[1])
# 第二个分数的分母
a = fenzi1 * fenmu2 + fenzi2 * fenmu1
# 如何计算两个分数之和呢,先把两个分数变成同分母的。为了简化计算,在这
# 里我们没有将两个分母放大到其最小公倍数,而是直接放大到两个分母之积。
# 相应的两个分子分别扩大相应的倍数再相加
b = fenzi1 * fenmu2 - fenzi2 * fenmu1
# 与计算两个分数的减法类似,把分母放大到两个分母之积然后再把相应的两个分子
# 分别扩大相应的倍数再相减
c = fenzi1 * fenzi2
# 计算两个分数的乘法,分子相乘,分母相乘。
fenmu = fenmu1 * fenmu2
# 加法,减法,乘法的分母
d = fenzi1 * fenmu2
fenmu_ = fenmu1 * fenzi2
# 如何计算两个分数的商呢?除一个数等于乘这个数的倒数,那我们就计算第一个分数
# 和第二个分数倒数之积吧。
string1 = get_format(fenzi1, fenmu1)
# 第一个分数的 “最简形式”
string2 = get_format(fenzi2, fenmu2)
# 第二个分数的 “最简形式”,下面的四则运算都需要这两个分数的最简形式,将其提前
# 计算出来能减少计算量。
print("{} + {} = {}".format(string1, string2, get_format(a, fenmu)))
# 求加法,并输出最简形式
print("{} - {} = {}".format(string1, string2, get_format(b, fenmu)))
# 求减法,并输出最简形式
print("{} * {} = {}".format(string1, string2, get_format(c, fenmu)))
# 求乘法,并输出最简形式
# 求除法,并输出最简形式
if fenmu_ == 0:
# 如果除法中分母是零,其实就是fenzi2是0
print("{} / {} = {}".format(string1, string2, 'Inf'))
else:
# 正常的除法
print("{} / {} = {}".format(string1, string2, get_format(d, fenmu_)))
def get_format(fenzi, fenmu):
# 给出一个分数的分子和分母,返回题目中要求的最简形式
if fenzi * fenmu < 0:
# 判断这个分数是整数还是负数,由于不知道负号是在分子上还是在分母上或者分子分母上都有
# 干脆乘一下,一锤定音
prefix = -1
elif fenzi * fenmu > 0:
prefix = 1
else:
# 如果分子是0,最简形式如下,直接返回。而且必须返回,因为下面求最大公约数的操作对于0
# 会出现错误。
return '0'
fenzi = abs(fenzi)
fenmu = abs(fenmu)
# 既然我们已经知道了这个分数的正负了,就把分子和分母都变成正的,以便于下面的计算。.
mcd = get_maximum_common_divisor(fenzi, fenmu)
# 求最大公约数
if mcd > 1:
fenzi //= mcd
fenmu //= mcd
# 约分
if fenzi >= fenmu:
fore = fenzi // fenmu
fenzi = fenzi % fenmu
# 求假分数的整数部分,fore表示整数部分,fenzi表示最简形式中的分子。注意有的分数
# 的最简形式可能是没有分子和分母的如 2/1,此时fore = 2, fenzi = 0, fenmu = 1
if prefix == 1 and fenzi != 0:
# 如果是正数而且 最简形式中分子不为零
return "{} {}/{}".format(fore, fenzi, fenmu)
elif prefix == 1 and fenzi == 0:
# 如果是正数而且 最简形式中分子是零
return "{}".format(fore)
elif prefix == -1 and fenzi != 0:
# 如果是负数而且 最简形式中分子不为零
return "(-{} {}/{})".format(fore, fenzi, fenmu)
elif prefix == -1 and fenzi == 0:
# 如果是负数而且 最简形式中分子是零
return "(-{})".format(fore)
else:
# fenzi < fenmu, 这时没有整数部分,只有分数。而且此时分子一定不为零,因为前面只有约分
# 操作,而约分之前,分子是零的情况已经return了。
if prefix == 1 and fenzi != 0:
# 正分数
return "{}/{}".format(fenzi, fenmu)
elif prefix == -1 and fenzi != 0:
# 负分数
return "(-{}/{})".format(fenzi, fenmu)
def get_maximum_common_divisor(a, b):
# 辗转相除法,求两个正整数的最大公约数。
# 既可以 a > b, 亦可以 a <= b 。 a <= b时,经过第一次递归就变成了 a > b 的情况。
if b == 0:
# 递归停止条件
return a
else:
return get_maximum_common_divisor(b, a % b)
# 辗转相除,不断取余
if __name__ == '__main__':
main()
易错点:
- 分数要化简成 最简形式,需要先约分, 再写成假分数的形式,还有考虑负数要加上括号的情况。
- 分子和分母的约分需要求二者的最大公约数,我们在求解最大公约数的时候一般都是将两个数字都视作正整数的而分子和分母有可能是负数。
总结: