Created
May 14, 2022 21:55
-
-
Save AleksandrHovhannisyan/f23b89beada7884ea38c9cf37d74849a to your computer and use it in GitHub Desktop.
Frequency analysis of the ciphertext in Edgar Allen Poe's "The Gold Bug" short story
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://poestories.com/read/goldbug | |
const cipherText = `53‡‡†305))6*;4826)4‡.)4‡);806*;48†8¶60))85;1‡(;:‡*8†83(88)5*†;46(;88*96*?;8)*‡(;485);5*†2:*‡(;4956*2(5*-4)8¶8*;4069285);)6†8)4‡‡;1(‡9;48081;8:8‡1;48†85;4)485†528806*81(‡9;48;(88;4(‡?34;48)4‡;161;:188;‡?;`; | |
const getFrequencyTable = (list) => { | |
return list.reduce((table, element) => { | |
if (!table[element]) { | |
table[element] = { | |
count: 0, | |
percentage: 0, | |
}; | |
} | |
table[element].count++; | |
const percentage = Number( | |
((table[element].count / list.length) * 100).toFixed(2) | |
); | |
table[element].percentage = percentage; | |
return table; | |
}, {}); | |
}; | |
const cipherLetters = cipherText.split(""); | |
// Sort in descending order by letter count to get the most popular letters first | |
letterFrequencies = Object.entries(getFrequencyTable(cipherLetters)).sort( | |
(entry1, entry2) => { | |
const count1 = entry1[1].count; | |
const count2 = entry2[1].count; | |
return count2 - count1; | |
} | |
); | |
/* | |
[ | |
[ '8', { count: 33, percentage: 16.26 } ], | |
[ ';', { count: 26, percentage: 12.81 } ], | |
[ '4', { count: 19, percentage: 9.36 } ], | |
[ '‡', { count: 16, percentage: 7.88 } ], | |
[ ')', { count: 16, percentage: 7.88 } ], | |
[ '*', { count: 13, percentage: 6.4 } ], | |
[ '5', { count: 12, percentage: 5.91 } ], | |
[ '6', { count: 11, percentage: 5.42 } ], | |
[ '(', { count: 10, percentage: 4.93 } ], | |
[ '1', { count: 8, percentage: 3.94 } ], | |
[ '†', { count: 8, percentage: 3.94 } ], | |
[ '0', { count: 6, percentage: 2.96 } ], | |
[ '2', { count: 5, percentage: 2.46 } ], | |
[ '9', { count: 5, percentage: 2.46 } ], | |
[ '3', { count: 4, percentage: 1.97 } ], | |
[ ':', { count: 4, percentage: 1.97 } ], | |
[ '?', { count: 3, percentage: 1.48 } ], | |
[ '¶', { count: 2, percentage: 0.99 } ], | |
[ '.', { count: 1, percentage: 0.49 } ], | |
[ '-', { count: 1, percentage: 0.49 } ] | |
] | |
*/ | |
console.log(letterFrequencies); | |
const decipheredMessage = cipherText | |
// 8 is the most frequently occurring letter, so likely e | |
.replace(/8/g, "e") | |
// ;48 appears often, suggesting it is "the" | |
.replace(/;/g, "t") | |
.replace(/4/g, "h") | |
// with the above transformations, we have: | |
// 53‡‡†305))6*the26)h‡.)h‡)te06*the†e¶60))e5t1‡(t:‡*e†e3(ee)5*†th6(tee*96*?te)*‡(the5)t5*†2:*‡(th956*2(5*-h)e¶e*th0692e5)t)6†e)h‡‡t1(‡9the0e1te:e‡1the†e5th)he5†52ee06*e1(‡9thet(eeth(‡?3hthe)h‡t161t:1eet‡?t | |
// ‡ appears twice in a row and also after h, suggesting it's o | |
.replace(/‡/g, "o") | |
// thet(eeth => the tree th... | |
.replace(/\(/g, "r") | |
// 9thetreethro?3hthe => ? = u and 3 = g | |
.replace(/\?/g, "u") | |
.replace(/3/g, "g") | |
// 5goo†g05))6*the26)ho.)ho)te06*the†e¶60))e5t1ort:o*e†egree)5*†th6rtee*96*ute)*orthe5)t5*†2:*orth956*2r5*-h)e¶e*th0692e5)t)6†e)hoot1ro9the0e1te:eo1the†e5th)he5†52ee06*e1ro9thetreethroughthe)hot161t:1eetout | |
// Since ) appears twice in a row multiple times, safe to assume it's 's' since we already found O and E, the two other letters that frequently repeat themselves. | |
.replace(/\)/g, "s") | |
// *orth = north or forth, north seems likely given it's a map | |
.replace(/\*/g, "n") | |
// th6rteen => 6 = i | |
.replace(/6/g, "i") | |
// thirteen9inutesnorthe => 9 = m | |
.replace(/9/g, "m") | |
// se¶enth => ¶ = v | |
.replace(/¶/g, "v") | |
// one†egree => † = d | |
.replace(/†/g, "d") | |
// inthedevi0ss => 0 = l | |
.replace(/0/g, "l") | |
// onedegrees5ndthirteenminutesnorthe => 5 = a | |
.replace(/5/g, "a") | |
// 1rom => 1 = f | |
.replace(/1/g, "f") | |
// atfort:onedegrees => : = y | |
.replace(/\:/g, "y") | |
// and2ynorth => 2 = b | |
.replace(/2/g, "b") | |
// bynorthmainbran-hseventhlimbeast => - = c | |
.replace(/\-/g, "c") | |
// agoodglassinthebisho.shostel => . = p | |
.replace(/\./g, "p"); | |
// a good glass in the bishops hostel in the devils seat forty one degrees | |
// and thirteen minutes northeast and by north main branch seventh limb | |
// east side shoot from the left eye of the deaths head a beeline from | |
// the tree through the shot fifty feet out | |
console.log(decipheredMessage); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment