数学重学 - 01 分数与小数

这是 数学重学路线图 阶段〇的子页面

分数与小数

分数是除法的另一种写法,小数是分数的十进制展开

上一节:00-数与运算


为什么要学这个

分数和小数是日常生活和编程中无处不在的概念

打折、税率、比例、概率——全是分数的应用

后端开发中的分页、比率计算、精度控制都离不开分数思维

安全场景:错误率、误报率、漏报率都是分数

大数据场景:采样率、压缩比、数据完整率


核心概念:分数的本质

分数就是除法

1/4 = 1 ÷ 4 = 0.25

3/5 = 3 ÷ 5 = 0.6

分数线就是除号,分子是被除数,分母是除数

这不是两个不同的东西,是同一个东西的两种写法

分数的组成

1
2
3
分子 (numerator)
──────
分母 (denominator)

分子:表示”取了几份”

分母:表示”一共分成几份”

1/4 的直觉:把一个披萨切成 4 块,取其中 1 块

分母不能为 0

5/0 = ? → 意味着 ? × 0 = 5

但任何数 × 0 = 0,不可能等于 5

所以除以 0 没有意义,这不是”无穷大”,是”未定义”

在编程中:ZeroDivisionError / ArithmeticException

安全隐患:除零错误可以被攻击者利用来触发服务崩溃(DoS)


真分数、假分数、带分数

真分数

分子 < 分母,值在 0 和 1 之间

1/4, 3/5, 7/8 都是真分数

假分数

分子 ≥ 分母,值 ≥ 1

5/3, 8/4, 7/7 都是假分数

带分数

整数部分 + 真分数部分

5/3 = 1 又 2/3(1 个整的 + 2/3)

转换方法:5 ÷ 3 = 商1余2 → 1 又 2/3

编程中很少用带分数,一般直接用小数或假分数


约分:化简分数

什么是约分

把分子分母同时除以它们的公因数

6/8 → 分子分母都除以 2 → 3/4

约到不能再约为止 = 最简分数

最大公约数 GCD(Greatest Common Divisor)

约分的关键是找到分子分母的最大公约数

12/18:GCD(12, 18) = 6,所以 12/18 = 2/3

辗转相除法(欧几里得算法)

求 GCD 的经典算法,思想简单但极其优雅

GCD(a, b) = GCD(b, a % b),直到余数为 0

例:GCD(48, 18)

48 % 18 = 12 → GCD(18, 12)

18 % 12 = 6 → GCD(12, 6)

12 % 6 = 0 → GCD = 6

人话翻译:反复用大的除以小的取余数,直到整除

这个算法在密码学中也会用到(求模逆元)

辗转相除法的 Python 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def gcd(a, b):
"""辗转相除法求最大公约数"""
while b:
a, b = b, a % b
return a

# 测试
print(gcd(48, 18)) # 6
print(gcd(12, 8)) # 4
print(gcd(100, 75)) # 25

# Python 内置也有
import math
print(math.gcd(48, 18)) # 6

# 约分函数
def simplify(numerator, denominator):
"""约分"""
g = gcd(abs(numerator), abs(denominator))
return numerator // g, denominator // g

print(simplify(12, 18)) # (2, 3)
print(simplify(48, 64)) # (3, 4)

通分:找公分母

什么是通分

把不同分母的分数变成相同分母,方便比较和加减

1/3 和 1/4 → 变成 4/12 和 3/12

最小公倍数 LCM(Least Common Multiple)

通分的关键是找分母的最小公倍数

LCM(a, b) = a × b ÷ GCD(a, b)

例:LCM(3, 4) = 3 × 4 ÷ 1 = 12

例:LCM(6, 8) = 6 × 8 ÷ 2 = 24

LCM 的 Python 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def lcm(a, b):
"""利用 GCD 求 LCM"""
return abs(a * b) // math.gcd(a, b)

print(lcm(3, 4)) # 12
print(lcm(6, 8)) # 24
print(lcm(12, 18)) # 36

# Python 3.9+ 内置 math.lcm
import math
print(math.lcm(3, 4)) # 12

# 多个数的 LCM
from functools import reduce
def lcm_multiple(*args):
return reduce(lcm, args)

print(lcm_multiple(2, 3, 4, 5)) # 60

分数的四则运算

加法和减法:先通分

1/3 + 1/4

通分:4/12 + 3/12 = 7/12

口诀:通分之后加分子,分母不变

1
2
3
1     1     4     3     7
─── + ─── = ─── + ─── = ───
3 4 12 12 12

乘法:分子乘分子,分母乘分母

2/3 × 4/5 = (2×4)/(3×5) = 8/15

直觉:蛋糕的 2/3 再取 4/5,就是 8/15

技巧:先约分再乘,数字更小不容易算错

2/3 × 3/4 = (2×3)/(3×4) → 先约分 → 2/4 = 1/2

除法:乘以倒数

2/3 ÷ 4/5 = 2/3 × 5/4 = 10/12 = 5/6

为什么除以一个分数等于乘以它的倒数?

2/3 ÷ 4/5 就是问:2/3 里面有多少个 4/5

除以 4/5 = 除以 4 再乘以 5 = 乘以 5/4

常见错误:忘了取倒数就直接乘了

运算规则汇总

1
2
3
4
5
6
7
8
9
加减法:先通分,分母相同后 加/减 分子
a/b + c/d = (ad + bc) / bd
a/b - c/d = (ad - bc) / bd

乘法:分子×分子,分母×分母
a/b × c/d = ac / bd

除法:乘以倒数
a/b ÷ c/d = a/b × d/c = ad / bc

分数、小数、百分比互转

分数 → 小数

直接做除法:1/4 = 1 ÷ 4 = 0.25

1/3 = 0.333…(无限循环小数,记作 0.3̄)

小数 → 分数

0.75 = 75/100 = 3/4

0.333… = 1/3

技巧:数小数位数,分母就是对应的 10 的幂次

百分比 → 小数/分数

25% = 25/100 = 1/4 = 0.25

直接除以 100 即可

常用互转速查表

1
2
3
4
5
6
7
8
9
10
11
分数    小数     百分比
─────────────────────────
1/2 0.5 50%
1/3 0.333.. 33.3%
1/4 0.25 25%
1/5 0.2 20%
1/8 0.125 12.5%
1/10 0.1 10%
2/3 0.666.. 66.7%
3/4 0.75 75%
3/8 0.375 37.5%

生活应用

做饭调整用量

食谱说 4 人份用 200g 面粉

只做 2 人份:200 × 2/4 = 200 × 1/2 = 100g

做 6 人份:200 × 6/4 = 200 × 3/2 = 300g

本质就是比例缩放

打折计算

8 折 = 原价 × 0.8 = 原价 × 4/5

打 65 折 = 原价 × 0.65 = 原价 × 13/20

满 300 减 50 相当于打几折? (300-50)/300 = 250/300 ≈ 0.833 → 约 8.3 折

注意:满减不等于全场打折,只有刚好花 300 时折扣最大

税率

增值税 13%:含税价 = 不含税价 × 1.13

不含税价 = 含税价 / 1.13(不是 × 0.87!)

这是一个常见的数学错误:加 13% 的逆运算不是减 13%

加 13%:× 1.13 → 逆运算:÷ 1.13


开发应用

分页计算

总 100 条数据,每页 15 条,需要几页?

100 ÷ 15 = 6.666… → 需要 7 页(向上取整)

公式:total_pages = ceil(total_items / page_size)

或者整数方法:total_pages = (total_items + page_size - 1) // page_size

比率计算

错误率 = 错误数 / 总请求数

100 万请求中有 50 个错误 → 50/1000000 = 0.005% = 5 × 10^(-5)

可用性 = 1 - 错误率 = 99.995%

比例分配

按权重分配流量:A 服务权重 3,B 服务权重 2

A 的流量占比 = 3/(3+2) = 3/5 = 60%

B 的流量占比 = 2/(3+2) = 2/5 = 40%


Python 代码实战

fractions 模块:精确分数运算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from fractions import Fraction

# 创建分数
a = Fraction(1, 3) # 1/3
b = Fraction(1, 4) # 1/4

# 四则运算 —— 自动约分!
print(a + b) # 7/12
print(a - b) # 1/12
print(a * b) # 1/12
print(a / b) # 4/3

# 从字符串创建
c = Fraction('0.75') # 3/4
print(c) # 3/4

# 从小数创建(注意精度问题)
d = Fraction(0.1) # 不精确!
print(d) # 3602879701896397/36028797018963968
d = Fraction('0.1') # 用字符串才精确
print(d) # 1/10

# 分数的属性
f = Fraction(7, 12)
print(f.numerator) # 7(分子)
print(f.denominator) # 12(分母)
print(float(f)) # 0.5833333333333334

# 比较大小
print(Fraction(1, 3) > Fraction(1, 4)) # True

GCD 和 LCM 完整实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import math

# GCD: 最大公约数
def gcd(a, b):
"""欧几里得算法"""
while b:
a, b = b, a % b
return a

# LCM: 最小公倍数
def lcm(a, b):
return abs(a * b) // gcd(a, b)

# 约分
def simplify_fraction(num, den):
g = gcd(abs(num), abs(den))
# 确保分母为正
if den < 0:
num, den = -num, -den
return num // g, den // g

# 通分
def common_denominator(num1, den1, num2, den2):
l = lcm(den1, den2)
new_num1 = num1 * (l // den1)
new_num2 = num2 * (l // den2)
return new_num1, new_num2, l

# 测试
print(simplify_fraction(12, 18)) # (2, 3)
print(simplify_fraction(-6, 8)) # (-3, 4)
print(common_denominator(1, 3, 1, 4)) # (4, 3, 12)

分页计算器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import math

def paginate(total_items, page_size, current_page=1):
"""
分页计算
返回:总页数、当前页起始索引、当前页结束索引
"""
if page_size <= 0:
raise ValueError("每页条数必须大于0")

total_pages = math.ceil(total_items / page_size)

# 整数方法(不需要 math.ceil)
# total_pages = (total_items + page_size - 1) // page_size

# 边界检查
current_page = max(1, min(current_page, total_pages))

start = (current_page - 1) * page_size
end = min(start + page_size, total_items)

return {
'total_items': total_items,
'page_size': page_size,
'total_pages': total_pages,
'current_page': current_page,
'start_index': start,
'end_index': end,
'has_prev': current_page > 1,
'has_next': current_page < total_pages
}

# 测试
result = paginate(100, 15, 1)
print(f"总共 {result['total_items']} 条")
print(f"每页 {result['page_size']} 条")
print(f"共 {result['total_pages']} 页") # 7 页
print(f"第 {result['current_page']} 页")
print(f"显示第 {result['start_index']+1} ~ {result['end_index']} 条")

# SQL 对应
page = 3
size = 15
offset = (page - 1) * size # 30
print(f"SELECT * FROM table LIMIT {size} OFFSET {offset}")

比率和百分比工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
def error_rate(errors, total):
"""计算错误率"""
if total == 0:
return 0.0 # 避免除零
rate = errors / total
return rate

def format_percentage(value, decimals=2):
"""格式化为百分比字符串"""
return f"{value * 100:.{decimals}f}%"

def availability(errors, total):
"""计算可用性(SLA)"""
return 1 - error_rate(errors, total)

# 模拟场景:100万请求中有50个错误
total_requests = 1_000_000
error_count = 50

rate = error_rate(error_count, total_requests)
avail = availability(error_count, total_requests)

print(f"错误率: {format_percentage(rate, 4)}") # 0.0050%
print(f"可用性: {format_percentage(avail, 4)}") # 99.9950%

# SLA 等级
sla_levels = {
'99.9%': 0.001, # 三个9 → 每天 86.4 秒不可用
'99.99%': 0.0001, # 四个9 → 每天 8.64 秒不可用
'99.999%': 0.00001, # 五个9 → 每天 0.86 秒不可用
}
print("\nSLA 等级与每年允许停机时间:")
for level, max_error in sla_levels.items():
downtime_sec = max_error * 365 * 24 * 3600
if downtime_sec > 3600:
print(f" {level}: {downtime_sec/3600:.1f} 小时/年")
elif downtime_sec > 60:
print(f" {level}: {downtime_sec/60:.1f} 分钟/年")
else:
print(f" {level}: {downtime_sec:.1f} 秒/年")

常见误区

误区1:分数除法忘记取倒数

错:2/3 ÷ 4/5 = 2/3 × 4/5 = 8/15

对:2/3 ÷ 4/5 = 2/3 × 5/4 = 10/12 = 5/6

误区2:加减法直接加分子和分母

错:1/3 + 1/4 = 2/7

对:1/3 + 1/4 = 4/12 + 3/12 = 7/12

误区3:以为加 13% 的逆运算是减 13%

不含税 100 → 含税 113 → 113 × 0.87 = 98.31 ≠ 100

正确逆运算:113 / 1.13 = 100

误区4:Fraction(0.1) 和 Fraction(‘0.1’) 一样

Fraction(0.1) 接收的是浮点数 0.1(已经不精确了)

Fraction(‘0.1’) 接收的是字符串,精确表示 1/10

误区5:分页计算用 int() 而不是 ceil()

int(100/15) = 6,丢了最后那 10 条数据

math.ceil(100/15) = 7,才是正确的页数


练习题

题目1:约分

将 84/126 化为最简分数

提示:先求 GCD(84, 126)

答案:GCD = 42,84/126 = 2/3

题目2:分数运算

计算 2/5 + 3/8 - 1/4

答案:LCM(5,8,4) = 40 → 16/40 + 15/40 - 10/40 = 21/40

题目3:实际应用

某商品原价 299 元,先涨价 10% 再打 9 折,最终价格是多少?

和直接 99 折(×0.99)比哪个便宜?

答案:299 × 1.1 × 0.9 = 299 × 0.99 = 296.01,两者相同!因为 1.1 × 0.9 = 0.99

题目4:编程题

用 Python 的 Fraction 类实现一个函数,输入一个有限小数字符串,输出最简分数

例:输入 “0.375” 输出 “3/8”

题目5:分页

数据库有 10237 条记录,每页显示 50 条

计算:总页数、第 100 页显示的是第几条到第几条

答案:ceil(10237/50) = 205 页;第 100 页显示第 4951~5000 条


小结

分数 = 除法的另一种写法

约分靠 GCD(辗转相除法),通分靠 LCM

加减先通分,乘法交叉乘,除法取倒数

分数/小数/百分比是三种写法,本质同一个数

编程中用 fractions.Fraction 做精确分数运算,用 math.ceil 做分页

下一节:02-幂根号与指数


上一章 目录 下一章
00-数与运算 数学重学路线图 02-幂根号与指数