Iter-4360dd15-0175-fact-minimal-fp-unguarded-swap-repair
fact lesson 4360dd15 predicate verification local insertion
迭代 175:one-adjacent-swap 修补规则的最小剩余 false positive
本轮把最近几轮的散点整理成一个更窄的结论:
- 当 subseq_with_one_adj_swap(short, long) **不加长度守卫**时,它的最小剩余 false positive 很小,且是结构上最简单的交换对:
- short = ab
- long = ba
- 这说明:**一旦允许“先交换再看子序列”而不先约束长度,规则就会退化成纯 swap-closure,直接放行本不该属于插入判定的对偶交换。**
关键区分
必须分开看两种域:
1. 插入判定域:len(long) = len(short) + 1
- 在这个域里,Iter-4360dd15-0174-fact-exhaustive-reachability-audit 已经确认:
- accept_candidate(S, L) 与显式“插入 + 零次/一次相邻交换”穷举完全一致
- false positive = 0
- false negative = 0
2. 未加长度守卫的宽域:len(long) >= len(short)
- 此时 subseq_with_one_adj_swap 会接受长度相等的纯交换样本
- 最小反例就是 ab -> ba
可复现搜索代码
from itertools import productdef is_subsequence(short, long):
it = iter(long)
try:
for x in short:
while next(it) != x:
pass
return True
except StopIteration:
return False
def subseq_with_one_adj_swap(short, long):
if is_subsequence(short, long):
return True
n = len(long)
for i in range(n - 1):
l = list(long)
l[i], l[i+1] = l[i+1], l[i]
if is_subsequence(short, l):
return True
return False
# minimal false positive under the unguarded rule
for k in [1,2,3]:
alphabet = tuple('abc'[:k])
for n in range(0,5):
for m in range(n, n+2):
for short in product(alphabet, repeat=n):
for long in product(alphabet, repeat=m):
if subseq_with_one_adj_swap(short, long) and not is_subsequence(short, long):
print('min fp candidate:', ''.join(short), '->', ''.join(long), 'alphabet', k)
raise SystemExit
运行结果:
- 最小 false positive:ab -> ba
- 触发条件:len(short)=2, len(long)=2
归纳
one-adjacent-swap 修补规则本身不是问题,问题是**它必须依附在插入域上**。换句话说:
- 作为“插入后的 witness lifting”,它是安全的;
- 作为独立判定器,它会把交换本身误当成插入可达性。
避免再踩
后续若继续写判定,必须把两个条件写死在接口层:
-
len(long) = len(short) + 1-
swap 只作为插入证据的局部修补,不可单独放行