How Siyuan Code works思源码是怎么工作的
The idea in four steps, with almost no formulas. For the full rigor, see the mathematics.用四步讲清思路,几乎不用公式。严谨版本见 数学原理。
Step 1 · The one simple idea第一步 · 那个简单的想法
Forget encoding rules for a moment. There is a finite set of DNA strands of a given length, and some of them are safe to store (low error) while others are error-prone. The simplest possible encoding is: write down the list of all safe strands, number them \(0, 1, 2, \dots\), and treat each number as data. To store a file, turn it into a big number and emit the strand with that number. To read it back, look up the strand's number.
先抛开编码规则。给定长度下,DNA 链的数量是有限的,其中一些 适合存储(低错误率),另一些则 易错。最简单的编码就是:把所有安全链列成一张表,编号 \(0,1,2,\dots\),把每个编号当成数据。存文件 = 把文件变成一个大数、输出对应编号的那条链;读回 = 查这条链的编号。
A tiny example一个小例子
Imagine a toy alphabet \(\{A,T\}\) and a rule “no AA”. The safe strands of length 2 are:
想象字母表 \(\{A,T\}\),规则是“不允许 AA”。长度为 2 的安全链有:
AT → 0 TA → 1 TT → 2
Three strands carry the numbers \(0,1,2\) — three states, \(\log_2 3 \approx 1.58\) bits per strand, and no strand is wasted. That is the whole trick: use every safe strand.
三条链承载 \(0,1,2\) ——三个状态、每条链约 \(\log_2 3 \approx 1.58\) 比特,而且 没有一条链被浪费。这就是全部诀窍:用尽每一条安全链。
Step 2 · Why the simple idea is hard第二步 · 为什么这个想法很难
The snag is the size of that list. The number of safe strands grows exponentially with length — around \(3.78^n\) under our strict in-vitro constraints, i.e. \(2.65\times10^{77}\) strands at just 134 nucleotides. You cannot write that table down, and you cannot look anything up in it. A hard disk could not even store the list, let alone search it.
难点在于这张表有多大。安全链的数量随长度指数增长——在湿实验的严格约束下约为 \(3.78^n\),仅 134 个核苷酸就有 \(2.65\times10^{77}\) 条。这张表你写不下,也查不了。别说检索了,连硬盘都存不下这张表。
Step 3 · Flip it: store the bad ones第三步 · 反过来:只存坏的
The insight is that the other side of the list is tiny. The unsafe strands are described by a handful of short “bad patterns”: a long run of the same base, a window with extreme GC content, a forbidden primer. Store those patterns in a compact trie — a tree that spells them out character by character — and a strand is safe exactly when a walk through this trie never lands on a forbidden pattern.
洞见在于:表的另一边很小。不安全 的链只由少数几个短“坏模式”描述:同一碱基的长连串、GC 含量极端的窗口、被禁的引物。把这些模式存进一棵紧凑的 trie——一棵逐字符拼出它们的树——那么一条链安全,当且仅当在这棵 trie 上行走时永远不会落到某个被禁模式上。
This flips the problem from “store an astronomically large set” to “store a small set of rules” — the same reason a dictionary stores rules of grammar, not every sentence.
这样就把问题从“存储一个天文数字大的集合”翻转成“存储一小套规则”——就像字典存的是语法规则,而不是每一个句子。
Step 4 · Count instead of list第四步 · 计数而非枚举
The last piece: if you cannot list the strands, count them. Dynamic programming computes, for every point in the trie, how many safe strands can follow from there. To find strand number \(i\), walk base by base: at each step, ask “does the branch starting with A hold at least \(i\) strands?” If yes, take A; if no, subtract that branch's count and try T, and so on. You descend to the \(i\)-th strand in a handful of arithmetic steps — without ever listing the others.
最后一块:既然列不出这些链,就去数它们。动态规划为 trie 里的每个位置算好 从那里出发还能接多少条安全链。要找第 \(i\) 条链,就一个碱基一个碱基地走:每一步问“以 A 开头的分支里是否至少有 \(i\) 条链?”有,就取 A;没有,就减去该分支的数量、再试 T,以此类推。只用几步加减法就走到第 \(i\) 条链——全程无需列出其它任何一条。
Why this wins为什么它能赢
- Optimal density. A bijection uses every safe strand, so nothing is wasted — the ceiling \(\log_2|S_k|/k\) is reached by construction.
- 密度最优。双射用尽了 每一条 安全链,没有浪费——构造上就达到了天花板 \(\log_2|S_k|/k\)。
- Deterministic. No randomness, no reject-and-retry, no infinite loops — unlike filtering methods, which can get stuck on strict rules.
- 确定性。没有随机、没有拒绝重试、没有死循环——不像筛法那样会在严格规则下卡住。
- Fast, regardless of strictness. The counting is one step per base, and the cost does not depend on how strict the constraints are — so Siyuan Code can afford the strictest rules, which lowers the error rate and the copy number.
- 快,且与严格程度无关。计数是每碱基一步,成本不随约束多严格而变——所以思源码用得起 最严 的规则,从而降低错误率与拷贝数。
Want the formal version?想看严谨版本? Read the mathematics →阅读数学原理 →