数学重学 - 10 布尔代数与逻辑

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

📌 已有相关笔记:布尔代数,本页从数学角度重新梳理,重点补充代码逻辑化简和实战应用

为什么要学布尔代数

写代码就是在写逻辑:if/else、while、权限判断、规则引擎…

复杂的条件判断 bug 频出?布尔代数能帮你化简

德摩根定律是代码简化的最强工具,用好了可以让嵌套 if 变成清爽代码

安全方向:WAF 规则组合、ACL 权限判断、SQL 注入检测

大数据方向:数据过滤条件优化、ETL 规则

后端方向:业务规则引擎、状态机、数据库查询优化

核心概念(直觉先行)

布尔值:世界上最简单的数据类型

只有两个值:True(1)False(0)

对应现实:开/关、是/否、有/无、通过/拒绝

乔治·布尔在 1854 年发明,100 年后被计算机发扬光大

为什么叫”代数”

因为它有自己的一套运算规则,和普通数字的加减乘除类似

普通代数:数字 + 加减乘除

布尔代数:True/False + AND/OR/NOT

基本运算与真值表

AND(与):两个都为真才为真

A B A AND B
0 0 0
0 1 0
1 0 0
1 1 1

直觉:串联电路——两个开关都闭合灯才亮

代码if user.is_active and user.has_permission:

OR(或):至少一个为真就为真

A B A OR B
0 0 0
0 1 1
1 0 1
1 1 1

直觉:并联电路——任一开关闭合灯就亮

代码if is_admin or is_owner:

NOT(非):取反

A NOT A
0 1
1 0

直觉:开关反转

代码if not is_blocked:

XOR(异或):不同才为真

A B A XOR B
0 0 0
0 1 1
1 0 1
1 1 0

直觉:二选一——只能选一个,不能都选或都不选

代码应用:校验位、错误检测

关于 XOR 的位运算细节 → 详见 08-进制与位运算

德摩根定律(最实用的化简法则)

两条核心法则

法则一NOT(A AND B) = (NOT A) OR (NOT B)

人话:”不是(两个都满足)” = “至少一个不满足”

法则二NOT(A OR B) = (NOT A) AND (NOT B)

人话:”不是(至少一个满足)” = “两个都不满足”

在代码中的应用(极其常用)

原始写法(难读):

if not (a and b): → 化简为 if (not a) or (not b):

原始写法(难读):

if not (a or b): → 化简为 if (not a) and (not b):

实际例子

判断不在范围内:

原始:if not (x > 5 and x < 10):

化简:if x <= 5 or x >= 10:

判断非法输入:

原始:if not (name and email):

化简:if (not name) or (not email):

排除条件:

原始:if not (is_admin or is_superuser):

化简:if (not is_admin) and (not is_superuser):

人话记忆法

NOT 穿过括号时,AND 和 OR 互换,每个条件都 取反

就像过安检:NOT(都通过) = 至少一个没通过

逻辑等价与化简

常用等价关系

A AND True = A(和 True 做 AND 不变)

A AND False = False(和 False 做 AND 一定是 False)

A OR True = True(和 True 做 OR 一定是 True)

A OR False = A(和 False 做 OR 不变)

A AND A = A(幂等)

A OR A = A(幂等)

A AND (NOT A) = False(矛盾)

A OR (NOT A) = True(排中)

分配律

A AND (B OR C) = (A AND B) OR (A AND C)

A OR (B AND C) = (A OR B) AND (A OR C)

和数字的乘法分配律结构一样

代码化简实战

复杂条件:

1
2
3
4
5
6
7
8
9
10
11
12
# 难读版本
if (status == 'active' and role == 'admin') or \
(status == 'active' and role == 'superadmin'):
grant_access()

# 用分配律化简
if status == 'active' and (role == 'admin' or role == 'superadmin'):
grant_access()

# 更 Pythonic
if status == 'active' and role in ('admin', 'superadmin'):
grant_access()

减少嵌套的技巧:提前 return(Guard Clause)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 嵌套地狱
def process(user, data):
if user:
if user.is_active:
if data:
if data.is_valid:
# 真正的逻辑
return do_work(user, data)
return None

# 用 Guard Clause 化简(提前排除不满足的情况)
def process(user, data):
if not user:
return None
if not user.is_active:
return None
if not data:
return None
if not data.is_valid:
return None
return do_work(user, data)

本质就是把 if A and B and C 拆成多个 if not X: return

短路求值(Short-Circuit Evaluation)

什么是短路求值

AND 短路A and B,如果 A 是 False,不会计算 B

OR 短路A or B,如果 A 是 True,不会计算 B

因为结果已经确定了,没必要算下去

代码中的实际应用

安全访问

1
2
3
4
# x 可能是 None,直接访问 x.value 会报错
if x and x.value > 10:
print("大于10")
# x 为 None/False/0 时,短路不会访问 x.value,不会报 AttributeError

默认值

1
2
3
4
5
6
# 左边有值就用左边,没有就用右边
config = user_config or default_config
name = get_name() or "Anonymous"

# 等价于
config = user_config if user_config else default_config

条件执行

1
2
3
4
# debug 模式才打印(短路 AND)
DEBUG and print("调试信息: x =", x)

# 不推荐这么写,但能看懂别人的代码

短路求值的陷阱

1
2
3
4
5
6
7
8
9
10
11
12
13
# 陷阱1:0 和空字符串是 falsy
count = 0
result = count or 100 # result = 100,不是 0!
# 因为 0 是 falsy,短路到了 100
# 正确做法:
result = count if count is not None else 100

# 陷阱2:副作用被跳过
def log_and_check():
print("检查中...") # 如果短路,这行不会执行
return True

False and log_and_check() # 不会打印!

Python 代码实战

逻辑化简示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 德摩根定律的代码验证
import itertools

def verify_demorgan():
"""穷举验证德摩根定律"""
for a, b in itertools.product([True, False], repeat=2):
# 法则一:NOT(A AND B) = (NOT A) OR (NOT B)
lhs1 = not (a and b)
rhs1 = (not a) or (not b)
assert lhs1 == rhs1, f"法则一失败: a={a}, b={b}"

# 法则二:NOT(A OR B) = (NOT A) AND (NOT B)
lhs2 = not (a or b)
rhs2 = (not a) and (not b)
assert lhs2 == rhs2, f"法则二失败: a={a}, b={b}"

print("德摩根定律验证通过!(穷举所有情况)")

verify_demorgan()

真值表生成器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def truth_table(expr_str, variables=['A', 'B']):
"""生成任意布尔表达式的真值表"""
header = ' | '.join(variables) + f' | {expr_str}'
separator = '-' * len(header)
print(header)
print(separator)

for values in itertools.product([False, True], repeat=len(variables)):
env = dict(zip(variables, values))
result = eval(expr_str, {"__builtins__": {}}, env)
row = ' | '.join(str(int(v)) for v in values)
print(f"{row} | {int(result)}")

print("=== AND ===")
truth_table("A and B")
print("\n=== XOR ===")
truth_table("A ^ B")
print("\n=== 德摩根 ===")
truth_table("not (A and B)")
truth_table("(not A) or (not B)")

WAF 规则模拟

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
class WAFRule:
"""简单的 WAF 规则引擎——布尔逻辑的实际应用"""
def __init__(self, name, condition_fn, action='block'):
self.name = name
self.condition = condition_fn
self.action = action

class SimpleWAF:
def __init__(self):
self.rules = []

def add_rule(self, rule):
self.rules.append(rule)

def check(self, request):
"""检查请求是否应该被拦截"""
triggered = []
for rule in self.rules:
if rule.condition(request):
triggered.append(rule)
return triggered

# 创建 WAF 实例
waf = SimpleWAF()

# 规则1:SQL 注入检测(OR 逻辑——任一特征命中)
sql_keywords = ["union", "select", "drop", "delete", "insert", "--", "/*"]
waf.add_rule(WAFRule(
"SQL注入",
lambda req: any(kw in req.get('url', '').lower() for kw in sql_keywords)
or any(kw in str(req.get('body', '')).lower() for kw in sql_keywords)
))

# 规则2:XSS 检测
xss_patterns = ["<script", "javascript:", "onerror=", "onload="]
waf.add_rule(WAFRule(
"XSS",
lambda req: any(p in req.get('url', '').lower() for p in xss_patterns)
or any(p in str(req.get('body', '')).lower() for p in xss_patterns)
))

# 规则3:频率限制(AND 逻辑——同时满足多个条件)
waf.add_rule(WAFRule(
"高频访问",
lambda req: req.get('request_count', 0) > 100
and req.get('time_window', 60) < 10
))

# 规则4:复合规则(非白名单 AND 可疑行为)
whitelist_ips = {'10.0.0.1', '192.168.1.1'}
waf.add_rule(WAFRule(
"非白名单的可疑请求",
lambda req: req.get('ip') not in whitelist_ips
and (req.get('user_agent', '') == ''
or 'bot' in req.get('user_agent', '').lower())
))

# 测试
test_requests = [
{'url': '/search?q=1 UNION SELECT * FROM users', 'ip': '1.2.3.4',
'user_agent': 'Mozilla/5.0'},
{'url': '/page?name=<script>alert(1)</script>', 'ip': '1.2.3.4',
'user_agent': 'Mozilla/5.0'},
{'url': '/api/data', 'ip': '5.6.7.8', 'user_agent': '',
'request_count': 200, 'time_window': 5},
{'url': '/index.html', 'ip': '10.0.0.1', 'user_agent': 'Mozilla/5.0'},
]

for req in test_requests:
triggered = waf.check(req)
if triggered:
rules_str = ', '.join(r.name for r in triggered)
print(f"🚫 拦截: {req['url'][:50]} → 触发规则: {rules_str}")
else:
print(f"✅ 放行: {req['url'][:50]}")

短路求值陷阱演示

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
# 演示短路求值的各种情况

print("=== AND 短路 ===")
def check(name, value):
print(f" 计算了 {name} = {value}")
return value

print("False and True:")
result = check("A", False) and check("B", True)
print(f" 结果: {result}")
# B 不会被计算

print("\nTrue and False:")
result = check("A", True) and check("B", False)
print(f" 结果: {result}")
# 两个都会被计算

print("\n=== OR 短路 ===")
print("True or False:")
result = check("A", True) or check("B", False)
print(f" 结果: {result}")
# B 不会被计算

print("\n=== 实际场景:安全访问 ===")
user = None
# 不会报 AttributeError,因为 user 是 None(falsy),短路了
if user and user.name:
print("有用户名")
else:
print("用户为空或无名字")

print("\n=== 陷阱:0 是 falsy ===")
score = 0
display = score or "无成绩"
print(f"score=0 时: display = '{display}'") # "无成绩" 而不是 0!
# 应该用:display = score if score is not None else "无成绩"

条件化简工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def simplify_range_check():
"""展示范围检查的化简过程"""
# 判断 x 不在 [5, 10) 范围内
test_values = [3, 5, 7, 10, 12]

for x in test_values:
# 原始写法
v1 = not (x >= 5 and x < 10)
# 德摩根化简
v2 = x < 5 or x >= 10

assert v1 == v2
print(f"x={x:2d}: not (x>=5 and x<10) = {v1}, x<5 or x>=10 = {v2}")

print("两种写法完全等价!但第二种更直观。")

simplify_range_check()

实际应用场景

安全方向

WAF 规则组合:多个检测条件用 AND/OR 组合(见上面代码)

SQL 注入检测has_keyword AND (not is_whitelisted) AND is_user_input

ACL 权限判断(is_owner OR is_admin) AND (NOT is_banned)

入侵检测:多个弱信号 AND 在一起形成强告警

后端方向

业务规则引擎:复杂的审批/风控规则本质上就是布尔表达式树

状态机条件current_state == 'pending' AND event == 'approve'

数据库索引优化WHERE a AND b vs WHERE a OR b 对索引使用的影响

配置开关feature_enabled AND (is_beta_user OR is_internal)

大数据方向

数据过滤:Spark/Pandas 的 filter 条件就是布尔表达式

ETL 数据清洗规则is_not_null AND is_valid_format AND in_range

A/B 测试分组hash(user_id) % 100 < 50 AND is_new_user

常见误区

❌ 误区1:not a == b 等于 (not a) == b

✅ Python 中 not a == b 实际是 not (a == b),因为 == 优先级高于 not

✅ 要明确写括号避免歧义

❌ 误区2:if x is not None and x > 0 可以简写成 if x > 0

✅ 如果 x 可能是非数字类型(如字符串),直接比较会出错

✅ 显式检查 None 更安全

❌ 误区3:if a or b == 'yes' 检查 a 或 b 等于 yes

✅ 实际是 if a or (b == 'yes'),a 只要 truthy 就通过

✅ 正确写法:if a == 'yes' or b == 'yes'

❌ 误区4:短路求值没有副作用问题

✅ 被短路的表达式中的函数调用不会执行,如果有日志/计数等副作用会出 bug

❌ 误区5:布尔代数太理论,写代码用不上

✅ 每次你化简 if 条件、用 Guard Clause、写规则引擎,都在用布尔代数

练习题

题1:德摩根化简

用德摩根定律化简以下表达式:

not (is_active and has_permission)

not (is_error or is_timeout)

答案:

(not is_active) or (not has_permission)

(not is_error) and (not is_timeout)

题2:条件化简

把下面代码化简成无嵌套的版本:

1
2
3
4
if user:
if user.age >= 18:
if user.is_verified:
allow()

答案:

1
2
3
4
5
6
7
if not user:
return
if user.age < 18:
return
if not user.is_verified:
return
allow()

或者一行版:if user and user.age >= 18 and user.is_verified: allow()

题3:真值表

画出 (A AND B) OR (NOT A AND NOT B) 的真值表

这个表达式有什么特殊含义?

答案:

| A | B | 结果 |
| 0 | 0 | 1 |
| 0 | 1 | 0 |
| 1 | 0 | 0 |
| 1 | 1 | 1 |

这就是 XNOR(同或):A 和 B 相同时为真

题4:短路求值

以下代码会输出什么?

1
2
3
4
x = 0
y = 5
result = x and y / x
print(result)

答案:

输出 0

x 是 0(falsy),AND 短路,不会计算 y / x,所以不会报 ZeroDivisionError

result = 0(x 的值)

题5:WAF 规则设计

设计一个拦截规则:当请求同时满足以下条件时拦截

来自非内网 IP(不是 10.x.x.x 或 192.168.x.x)

且(URL 包含 admin 或请求方法是 DELETE)

用布尔表达式写出来

答案:

NOT(is_internal_ip) AND (url_contains_admin OR method_is_delete)

1
2
block = (not ip.startswith(('10.', '192.168.'))) and \
('admin' in url or method == 'DELETE')

上一章 目录 下一章
09-模运算与整除 数学重学路线图 11-集合论与数据操作