中文譯本 · 原作 Daniel Bourdeau《Unsolved Historical Ciphers》 以 CC BY 4.0 釋出,本頁由 JIC 翻譯,非原作者官方版本,譯文如有出入以英文原頁為準 · 檢視英文原頁 ↗

哥本哈根 · 筆畫、圓點和數字,體系未定 · 大約發現於1950年代末

哥本哈根密碼

三行由筆畫、圓點、尖角符號和數字組成的字跡,發現於哥本哈根一座軍事博物館中一幅1835年丹麥將軍肖像的背後,在 Klaus Schmeh 的 Top 50 中排第23位。2026年9月,它被當作簡單替換,在兩種轉錄、六種約定下用十種語言加以攻擊,並配有匹配的對照。沒有復原出金鑰,也從未有人發表過解讀。

Daniel Bourdeau · 釋出於 · 更新於 · 資料來源:Klaus Schmeh 在 Cipherbrain 上2015年1月、2017年8月和2021年10月的帖子及其評論;614 × 147 畫素的掃描件 Danish-General-Cryptogram.jpg

方法:未破解 · 程度:未獲得文字

不是所測任何語言的簡單替換 就現有掃描件而言已結案

用一個以5-gram語言模型打分的求解器(模擬退火),在十種語言下攻擊了流傳的兩種轉錄。各語言最好也只達到每字母−2.69 nats,300次重啟也沒有改變這一結果。同一求解器能復原用同樣方式加密的15段隨機103字母文字中的12段,而這一長度的真實文字得分在−1.4到−2.1之間。要麼這張紙條不是這些語言中某段文字的簡單替換,要麼兩種讀法都帶有同一個轉錄錯誤。

01 實物及其來歷

紙條上有三行短字。它大約在1950年代末發現於哥本哈根一座軍事博物館中一幅1835年丹麥將軍肖像的背後,並被寄給了美國密碼協會(American Cryptogram Association)。ACA 沒有記錄任何解答,也從未撰文介紹。博物館、將軍和發現者都沒有記錄。

Klaus Schmeh 從 ACA 會員 Kent Ramliden 那裡得到一份掃描件;Ramliden 是旅居佛羅里達的瑞典人,2016年去世。Schmeh 三次發表過它:2015年1月用德語,2017年8月作為他 Top 50 的第23號,2021年10月又以德語和英語的“懸案”帖子發表。流傳的唯一影象就是那份掃描件,614 × 147 畫素。2021年評論中有兩條讀者貢獻與此相關:“ShadowWolf”的25符號轉錄,以及 Matthew Brown 用丹麥語四元組做的爬山法,後者一無所獲。

02 轉錄

使用了兩種轉錄。 cipher.txt 是本專案的轉錄,根據放大四倍和六倍的掃描件,每次半行做成。 cipher_sw.txt 是 ShadowWolf 在2021年10月7日那篇德語帖子評論中給出的25符號讀法。以下是合併後的讀法,每個符號一個記號,以空格分隔:

7 N F n F B 3 A D B n o 3 D B P 6 3 B 3 P 8 D F 2 n 3 | 2 3 A W R F 0 3
A n 2 0 B n o 9 P n P 9 2 B 3 v v 3 D B | 0 A B v 3 D B P n o F 3 8 0 D B
P X 0 3 B n o n F 9 n D B v n | 1 3 | n o n 3 F D B 3 F B A 1 2 3 A

共107個記號:20種共103個符號,外加4條長豎線,寫作 | ,當作句子分隔符處理。數字4和5從未出現。

記號紙條上的標記次數
3數字316
n普通的 n13
B後面帶點的反斜線(\.)14
F後面帶點的斜線(/.)8
P加號6
A尖角或 lambda(^)6
D位於筆畫之前的獨立圓點8
o小圓字母 o5
2數字25
0高的零5
v照寫4
9數字93
1數字12
8數字82
7 6 X照寫各1次
N上方帶兩點的 n1
R第1行末尾一個形似“or”的小字形1
W上方帶雙重音符的尖角1
|長豎線,句子分隔符,不計入103個之內4

兩種讀法的分歧

兩種讀法逐個符號一致,只是 ShadowWolf 把這裡合併的東西拆開了。他把後面帶點的反斜線與兩側都帶點的反斜線區分開來,後者在這裡是一個獨立圓點 D 後接 B。他把後面帶點的斜線與前面帶點的斜線區分開來,後者在這裡是 D 後接 F。他把第一對尖角之後那個小小的上標雙撇單列為一個符號。他區分了四種尖角形狀:一種高的、像 lambda 的,即第1行的第一個尖角和第3行的最後一個符號;一種小的;一種後面帶點、位於第2行開頭的;還有一種在第2行的零之後。他還把第3行 1 3 附近的第二條高豎線讀作數字1,而不是分隔符。這樣拆分後符號數為25,與 Schmeh 的數字一致;合併後為20。兩種讀法都做了攻擊。

兩條內部規律

信件 o 出現五次,每次都緊跟在 n: no3, no9, +no, non 和 non3之後,其中 non 在第3行出現兩次。成對的 vv 出現一次,在第2行。第06節說明了它們能走多遠。

03 攻擊

solve.py 用 Project Gutenberg 中十種語言的文字構建字元5-gram語言模型:丹麥語、瑞典語、挪威語、德語、荷蘭語、法語、英語、拉丁語、冰島語和芬蘭語,每種0.6至240萬字母。語料不在倉庫中,而是透過 Gutendex API 按會話日誌中的 id 獲取。求解器是在單射的符號到字母對映上做模擬退火,每次執行重啟60到80次,長豎線視為句子邊界。求解器只最佳化5-gram得分;得分最高的候選隨後再按詞典覆蓋率重新排序,而詞典覆蓋率並不在最佳化目標之內。

在合併讀法上測試了六種記號約定:獨立圓點作為字母、刪去、作為邊界,或與後面的筆畫合併;長豎線作為字母;帶點的 n 與普通 n 合併。25符號讀法則按原樣執行。另有一個指令碼 solve_sp.py檢驗“兩種帶點筆畫是單詞分隔符”這一假設——那樣的話,文字就是22個詞,平均每詞3.7個字母——它使用一個考慮空格的模型,並以整詞命中來計分。

在任何語言、任何約定下都沒有得出可讀的東西。各語言的最佳每字母得分,以及最佳金鑰得出的結果:

語言最佳每字母得分(任意約定)結果大致如何
拉丁語−2.69fxsisted tine togeteom scie ...
丹麥語−2.77junindel dige dskedesf ... / ... udstødes ... velsigne ...
挪威語−2.81bærersag sena stjasatm ...
瑞典語−2.81honensam sera stfasatv ...
中文釋義−2.88cydidsea sine stbesetf ...
德語−2.93uchthien itze irbeierm ...
法語−2.97chiailes lape ltbeletr ...
荷蘭語、冰島語、芬蘭語−3.07 至 −3.30

單詞分隔符假設在帶空格的模型下每字母得分−4.6,而真實文字約為−2.3。它只得出一些孤立的丹麥語單詞:er、elle、unge、død、døde。最後兩個就是第06節討論的片段。

04 匹配對照

一次失敗的搜尋本身證明不了什麼,所以先用求解器本應能解開的文字對它進行校準。 control.py 從同樣的語料中隨機抽取103字母、含三個句子斷點的段落,每段用約20個符號的隨機簡單替換加密,再用完全相同的求解器攻擊。每種語言試驗三次,每次重啟40次;而真正的密碼用了60到80次,後來又用了300次。準確率是復原出的段落所佔比例。

語言試驗金鑰中的字母數準確率找到的得分真實文字的得分結果
丹麥語1190.99−1.99−1.85已復原
丹麥語2190.99−1.93−1.96已復原
丹麥語3180.99−1.85−1.76已復原
德語1220.96−1.88−1.78已復原
德語2190.79−2.64−1.75部分
德語3220.14−3.42−1.81搜尋失敗
中文釋義1201.00−1.44−1.44已復原
中文釋義2210.99−1.99−1.85已復原
中文釋義3201.00−1.67−1.67已復原
拉丁語1181.00−2.09−2.09已復原
拉丁語2181.00−1.74−1.74已復原
拉丁語3190.99−1.87−1.72已復原
瑞典語1221.00−1.95−1.95已復原
瑞典語2220.05−3.49−1.59搜尋失敗
瑞典語3221.00−1.99−1.99已復原

15段中有12段基本完全復原,真實文字的每字母得分總在−1.4到−2.1之間。兩次試驗徹底失敗,一次部分失敗。每一例中,真正的金鑰得分都會遠好於求解器找到的結果:−1.81對−3.42,−1.59對−3.49,−1.75對−2.64。這些是40次重啟下的搜尋失敗,很可能與22字母的金鑰有關;它們意味著,對真實文字的單次失敗執行證明不了什麼。正因如此,最有希望的幾種候選語言在兩種讀法上都用300次重啟重跑了一遍。

對照這一校準,該密碼在任何語言、任何讀法下的得分都從未好於−2.7。所以,要麼它不是這十種語言中某段文字的簡單替換,要麼轉錄以兩種獨立讀法共有的方式合併或拆分了字母。丹麥語自2015年以來一直是預設假設,但它的契合度並不比拉丁語或挪威語好。Matthew Brown 在2021年評論中做的丹麥語四元組爬山法,也同樣一無所獲。

05 300次重啟的確認

以種子5重啟300次的最佳每字母得分,物件為合併讀法的 full 和 nodot 約定以及25符號讀法。 dotattach 的執行在寫這些筆記時仍在進行,尚未超過這些分數。

語言合併,完整合併,去掉圓點25符號讀法
拉丁語−2.90−2.82−3.16
丹麥語−3.04−2.90−3.16
瑞典語−3.17−2.99−3.20
挪威語−3.12−3.01−3.17
中文釋義−3.20−3.13−3.28
德語−3.29−3.32−3.46

重啟次數增至五倍,結果紋絲不動;第03節取的是六種約定中的最佳值,這些執行固定了約定和種子,沒有一格超過它。上限依舊,在每種語言中都比這一長度的真實文字得分低0.7到1.4 nats每字母。搜尋失敗這一解釋可以排除。

06 唯一引人遐想的片段

non 讀作 død

在 n = d, o = ø, 3 = e 的情況下,n-o-n 模式給出丹麥語 død(死的,死亡), non3 給出 døde(死了,死者),而 no3 給出 døe,即 dø(死)在1900年以前的拼法。這是整個搜尋中唯一引人遐想的片段。

它出現在單詞分隔符那次執行中,那次執行的其他整詞命中只有 er、elle 和 unge。涉及的三個符號佔103個記號中的34個,所以這一指派固定了三分之一的文字。但它無法延伸。那次解密的其餘部分都是噪聲,整次執行每字母得分−4.6,而真實文字在同一模型下約為−2.3。

07 現狀及重啟的條件

未破解 就現有掃描件而言已結案。有三條途徑可以重啟。

08 檔案

所有內容都在專案倉庫的 targets/copenhagen/ 中。 cipher.txt 是合併後的20符號讀法,檔案頭中註明了記號約定; cipher_sw.txt 是與之對齊的 ShadowWolf 25符號讀法。 solve.py 是帶詞典重排序的5-gram求解器(模擬退火), solve_sp.py 是單詞分隔符測試, control.py 是匹配對照的測試框架。 NOTES.md 儲存了這次嘗試的完整記錄。Gutenberg 語料不在倉庫中;Gutendex id 記在會話日誌裡。