Jelly , 309 байт в кодировке Jelly
“Æ÷“¥s“ɲ“¡µ’;“ịƊ⁴çNṂ‘_\
OḌ;¢*5$%¥/µ“+⁷ż!¤ña¡jIȧƁfvḶg/Ọ=^ƝĠ0Ẇƭ³½N~=.Ɗ°ɗẇ⁵\ɦ*ɠPf⁾?ṾHḣ 2=⁹ƒ!©ƊĠṣƥ®Ƙ0Yƙ>!ȧtƊN0w,$ɠẎ46fẋ⁷(ṣẆm⁾ŻƓṫµsçwṣḂḲd0Ruṛ’ḃ21+\iµØW“&;:' ”;“¡3ȧ%⁾xƑ?{Ñṃ;Ċ70|#%ṭdṃḃ÷ƑĠẏþḢ÷ݳȦṖcẇọqƁe ʠ°oḲVḲ²ụċmvP[ỴẊẋ€kṢ ȯḂ;jɓỴẏeṾ⁴ḳḢ7Ẓ9ġƤṙb€xÇ4ɗ⁻>Ẉm!Ƈ)%Ḃẇ$ġ£7ȧ`ỵẈƘɗ¡Ṃ&|ƙƥ³ẏrṛbḋƙċ⁻ṁƲRṀẹṾ<ñ⁻Ṅ7j^ɓĊ’b58¤ị;0ị@
ḲÇ€t0”@;Ṫ
Попробуйте онлайн!
Я решил, что пришло время попробовать свои силы. Использование Jelly (и его 8-битной кодовой страницы) дает мне преимущество в 12,5% по сравнению с языками только для ASCII, и Jelly удобен для этой задачи благодаря наличию встроенных базовых операторов преобразования с короткими именами, но большей части экономии из-за лучшего алгоритма сжатия (эта программа в среднем составляет менее одного байта на тип монстра).
Алгоритм и объяснение
Словесная классификация
Я решил, что для того, чтобы получить хороший результат, необходимо использовать преимущества структуры ввода больше, чем другие записи. Одна вещь, которая очень заметна, - то, что у многих монстров есть названия формы " прилагательное разновидности "; a red dragon
и a blue dragon
оба типа дракона, и поэтому выглядят как D
. У некоторых других монстров есть названия вида " работа вида ", такие как ; будучи типом орка, это выглядит как . Сложные вопросы - это нежить; a является и кобольдом, и зомби, и последнее состояние имеет приоритет в именовании монстров NetHack, поэтому мы хотели бы классифицировать это как .orc shaman
o
kobold zombie
Z
Таким образом, я классифицировал слова, которые появляются в именах монстров, следующим образом: индикатор - это слово, которое настоятельно рекомендует соответствующий класс монстров (например, sphere
настоятельно предполагает, что монстр находится в классе e
); неоднозначное слово это слово , которое делает гораздо меньше предложения ( lord
не сказать вам много), а все остальные слова псевдослов , что мы не заботимся о. Основная идея заключается в том, что мы смотрим на слова в названии монстра от конца к началу и выбираем первый индикатор, который мы видим. Таким образом, необходимо было обеспечить, чтобы каждое имя монстра содержало хотя бы один индикатор, за которым следовали двусмысленные слова. Как исключение, слова, которые появляются в именах монстров, которые выглядят как@
(самая большая группа) все классифицируются как неоднозначные. Все может появиться перед индикатором; например, имена цветов (такие как red
) всегда появляются в имени раньше, чем индикатор, и, таким образом, считаются не словами (так как они никогда не проверяются при определении личности монстра).
В конце концов, эта программа сводится к хеш-таблице, как и другие программы. Однако таблица не содержит записей для всех имен монстров или для всех слов, которые появляются в именах монстров; скорее он содержит только показатели. Хеши неоднозначных слов не отображаются в таблице, но должны быть назначены пустым слотам (попытка найти неоднозначное слово всегда будет пустой). Для не-слов не имеет значения, появляется ли слово в таблице или нет, или хэш сталкивается или нет, потому что мы никогда не используем значение поиска не-слова. (Таблица довольно разреженная, поэтому большинство слов не появляется в таблице, но некоторые из них, например flesh
, найдены в таблице в результате коллизий хешей.)
Вот несколько примеров того, как работает эта часть программы:
woodchuck
это длинное слово (таким образом, индикатор), и поиск в таблице woodchuck
дает нам ожидаемый ответ r
.
abbot
также одно слово долго, но выглядит как @
. Как таковое, abbot
считается неоднозначным словом; поиск по таблице пуст, и мы возвращаем ответ @
по умолчанию.
vampire lord
состоит из индикатора ( vampire
соответствует V
) и неоднозначного слова ( lord
которого нет в таблице). Это означает, что мы проверяем оба слова (в обратном порядке), а затем даем правильный ответ V
.
gelatinous cube
состоит из не-слова ( gelatinous
соответствует H
из-за коллизии хэшей) и индикатора ( cube
соответствует b
). Поскольку мы берем только последнее слово, найденное в таблице, это возвращает b
, как и ожидалось.
gnome mummy
состоит из двух показателей, gnome
соответствующих G
и mummy
соответствующих M
. Мы берем последний индикатор и получаем то M
, что мы хотим.
Код для обработки классификации на основе слов является последней строкой программы Jelly. Вот как это работает:
ḲÇ€t0”@;Ṫ
Ḳ Split on spaces
Ç€ Call function 2 (table lookup) on each entry
t0 Remove trailing zeroes (function 2 returns 0 to mean "not found")
”@; Prepend an @ character
Ṫ Take the last result
Есть два реальных случая; если входные данные полностью состоят из неоднозначных слов, t0
удаляются все выходные данные поиска в таблице, и мы получаем @
результат по умолчанию; если на входе есть индикаторы, t0
то удалит что-нибудь справа от крайнего правого индикатора и Ṫ
выдаст соответствующий результат для этого индикатора.
Сжатие таблицы
Конечно, разбиение ввода на слова само по себе не решает проблему; нам все еще нужно закодировать соответствие между индикаторами и соответствующими классами монстров (и отсутствие соответствия от неоднозначных слов). Чтобы сделать это, я построил разреженную таблицу с 181 использованными записями (что соответствует 181 индикатору; это большое улучшение по сравнению с 378 монстрами!) И всего 966 записей (что соответствует 966 выходным значениям хэш-функции). Таблица кодируется в программе с использованием двух строк: первая строка указывает размеры «пробелов» в таблице (которые не содержат записей); и вторая строка определяет класс монстров, который соответствует каждой записи. Они оба представлены в сжатой форме через базовое преобразование.
В программе Jelly код поиска по таблице вместе с самой программой представлен во второй строке, начиная с первой µ
. Вот как работает эта часть программы:
“…’ḃ21+\iµØW“&;:' ”;“…’b58¤ị;0ị@
“…’ Base 250 representation of the gap sizes
ḃ21 Convert to bijective base 21
+\ Cumulative sum (converts gaps to indexes)
i Find the input in this list
µ Set as the new default for missing arguments
ØW Uppercase + lowercase alphabets (+ junk we ignore)
“&;:' ”; Prepend "&;:' "
“…’ Base 250 representation of the table entries
b58 Convert to base 58
¤ Parse the preceding two lines as a unit
i Use the table to index into the alphabets
;0 Append a zero
i@ Use {the value as of µ} to index into the table
Биективное основание 21 похоже на основание 21, за исключением того, что 21 является допустимой цифрой, а 0 - нет. Для нас это более удобная кодировка, потому что мы считаем, что две соседние записи имеют пробел 1, поэтому мы можем найти действительные индексы с помощью накопленной суммы. Когда дело доходит до той части таблицы, которая содержит значения, у нас есть 58 уникальных значений, поэтому мы сначала декодируем в 58 последовательных целых чисел, а затем снова декодируем, используя таблицу поиска, которая сопоставляет их с реальными используемыми символами. (Большинство из них - буквы, поэтому мы начинаем эту вторичную таблицу поиска с &;:'
небуквенных записей, а затем просто добавляем константу Jelly, начинающуюся с прописных и строчных букв; у нее также есть какой-то другой мусор, но нам все равно об этом.)
Стражное значение Jelly «index not found», если вы используете его для индексации списка, возвращает последний элемент списка; таким образом, я добавил ноль (целое ноль, даже если таблица в основном состоит из символов) к таблице поиска, чтобы дать более подходящий сторож, чтобы указать отсутствующую запись.
Хэш-функция
Оставшаяся часть программы - хеш-функция. Это начинается достаточно просто, сOḌ
; это преобразует входную строку в ее ASCII-коды, а затем вычисляет последний код, плюс 10-кратный предпоследний код, плюс 100-кратный код до и т. д. (это очень короткое представление в Jelly, потому что оно чаще используется как строка → целочисленная функция преобразования). Однако, если бы мы просто сократили этот хэш напрямую с помощью операции модуля, нам бы понадобилась довольно большая таблица. Поэтому вместо этого я начинаю с цепочки операций по сокращению таблицы. Каждый из них работает следующим образом: мы берем пятую степень текущего хеш-значения; затем мы уменьшаем значение по модулю константы (эта константа зависит от того, какую операцию мы используем). Эта цепочка дает большую экономию (с точки зрения уменьшения размера результирующей таблицы), чем она стоит (с точки зрения необходимости кодирования самой цепочки операций), двумя способами: она может создать таблицугораздо меньше (966, а не 3529 записей), а использование нескольких этапов дает больше возможностей для создания полезных коллизий (такого не было много, но есть одно такое коллизирование: Death
и Yeenoghu
хеш, и 806, что позволяет нам удалить один запись из таблицы, так как они оба идут в&
). Здесь используются модули [3529, 2163, 1999, 1739, 1523, 1378, 1246, 1223, 1145, 966]. Между прочим, причина повышения до пятой степени заключается в том, что если вы просто берете значение непосредственно, промежутки, как правило, остаются одинаковыми, тогда как возведение в степень перемещает пробелы вокруг и может позволить распределению таблицы более равномерно распределяться после цепочка, а не застревание в локальном минимуме (более равномерно распределенные промежутки позволяют более сжатое кодирование размеров промежутков). Это должно быть нечетной степенью, чтобы предотвратить тот факт, что x ² = (- x ) ², приводящее к столкновениям, и 5 работал лучше, чем 3.
Первая строка программы кодирует последовательность модулей с использованием дельта-кодирования:
“…’;“…‘_\
“…’ Compressed integer list encoding, arbitrary sized integers
; Append
“…‘ Compressed integer list encoding, small integers (≤ 249)
_\ Take cumulative differences
Оставшаяся часть программы, начало второй строки, реализует хеш-функцию:
OḌ;¢*5$%¥/
O Take ASCII codepoints
Ḍ "Convert from decimal", generalized to values outside the range 0-9
;¢ Append the table of moduli from the previous line
/ Then reduce by:
*5$ raising to the power 5 (parsing this as a group)
%¥ and modulusing by the right argument (parsing this as a group, too).
верификация
Это скрипт Perl, который я использовал для проверки правильности работы программы:
use warnings;
use strict;
use utf8;
use IPC::Run qw/run/;
my %monsters = ("Aleax", "A", "Angel", "A", "Arch Priest", "@", "Archon", "A",
"Ashikaga Takauji", "@", "Asmodeus", "&", "Baalzebub", "&", "Chromatic Dragon",
"D", "Croesus", "@", "Cyclops", "H", "Dark One", "@", "Death", "&", "Demogorgon",
"&", "Dispater", "&", "Elvenking", "@", "Famine", "&", "Geryon", "&",
"Grand Master", "@", "Green-elf", "@", "Grey-elf", "@", "Hippocrates", "@",
"Ixoth", "D", "Juiblex", "&", "Keystone Kop", "K", "King Arthur", "@",
"Kop Kaptain", "K", "Kop Lieutenant", "K", "Kop Sergeant", "K", "Lord Carnarvon",
"@", "Lord Sato", "@", "Lord Surtur", "H", "Master Assassin", "@", "Master Kaen",
"@", "Master of Thieves", "@", "Medusa", "@", "Minion of Huhetotl", "&",
"Mordor orc", "o", "Nalzok", "&", "Nazgul", "W", "Neferet the Green", "@", "Norn",
"@", "Olog-hai", "T", "Oracle", "@", "Orcus", "&", "Orion", "@", "Pelias", "@",
"Pestilence", "&", "Scorpius", "s", "Shaman Karnov", "@", "Thoth Amon", "@",
"Twoflower", "@", "Uruk-hai", "o", "Vlad the Impaler", "V", "Wizard of Yendor",
"@", "Woodland-elf", "@", "Yeenoghu", "&", "abbot", "@", "acid blob", "b",
"acolyte", "@", "air elemental", "E", "aligned priest", "@", "ape", "Y",
"apprentice", "@", "arch-lich", "L", "archeologist", "@", "attendant", "@",
"baby black dragon", "D", "baby blue dragon", "D", "baby crocodile", ":",
"baby gray dragon", "D", "baby green dragon", "D", "baby long worm", "w",
"baby orange dragon", "D", "baby purple worm", "w", "baby red dragon", "D",
"baby silver dragon", "D", "baby white dragon", "D", "baby yellow dragon", "D",
"balrog", "&", "baluchitherium", "q", "barbarian", "@", "barbed devil", "&",
"barrow wight", "W", "bat", "B", "black dragon", "D", "black light", "y",
"black naga hatchling", "N", "black naga", "N", "black pudding", "P",
"black unicorn", "u", "blue dragon", "D", "blue jelly", "j", "bone devil", "&",
"brown mold", "F", "brown pudding", "P", "bugbear", "h", "captain", "@",
"carnivorous ape", "Y", "cave spider", "s", "caveman", "@", "cavewoman", "@",
"centipede", "s", "chameleon", ":", "chickatrice", "c", "chieftain", "@",
"clay golem", "'", "cobra", "S", "cockatrice", "c", "couatl", "A", "coyote", "d",
"crocodile", ":", "demilich", "L", "dingo", "d", "disenchanter", "R", "djinni",
"&", "dog", "d", "doppelganger", "@", "dust vortex", "v", "dwarf king", "h",
"dwarf lord", "h", "dwarf mummy", "M", "dwarf zombie", "Z", "dwarf", "h",
"earth elemental", "E", "electric eel", ";", "elf mummy", "M", "elf zombie", "Z",
"elf", "@", "elf-lord", "@", "energy vortex", "v", "erinys", "&", "ettin mummy",
"M", "ettin zombie", "Z", "ettin", "H", "fire ant", "a", "fire elemental", "E",
"fire giant", "H", "fire vortex", "v", "flaming sphere", "e", "flesh golem", "'",
"floating eye", "e", "fog cloud", "v", "forest centaur", "C", "fox", "d",
"freezing sphere", "e", "frost giant", "H", "gargoyle", "g", "garter snake", "S",
"gas spore", "e", "gecko", ":", "gelatinous cube", "b", "ghost", " ", "ghoul",
"Z", "giant ant", "a", "giant bat", "B", "giant beetle", "a", "giant eel", ";",
"giant mimic", "m", "giant mummy", "M", "giant rat", "r", "giant spider", "s",
"giant zombie", "Z", "giant", "H", "glass golem", "'", "glass piercer", "p",
"gnome king", "G", "gnome lord", "G", "gnome mummy", "M", "gnome zombie", "Z",
"gnome", "G", "gnomish wizard", "G", "goblin", "o", "gold golem", "'",
"golden naga hatchling", "N", "golden naga", "N", "gray dragon", "D", "gray ooze",
"P", "gray unicorn", "u", "green dragon", "D", "green mold", "F", "green slime",
"P", "gremlin", "g", "grid bug", "x", "guard", "@", "guardian naga hatchling",
"N", "guardian naga", "N", "guide", "@", "healer", "@", "hell hound pup", "d",
"hell hound", "d", "hezrou", "&", "high priest", "@", "hill giant", "H",
"hill orc", "o", "hobbit", "h", "hobgoblin", "o", "homunculus", "i",
"horned devil", "&", "horse", "u", "housecat", "f", "human mummy", "M",
"human zombie", "Z", "human", "@", "hunter", "@", "ice devil", "&", "ice troll",
"T", "ice vortex", "v", "iguana", ":", "imp", "i", "incubus", "&", "iron golem",
"'", "iron piercer", "p", "jabberwock", "J", "jackal", "d", "jaguar", "f",
"jellyfish", ";", "ki-rin", "A", "killer bee", "a", "kitten", "f", "knight", "@",
"kobold lord", "k", "kobold mummy", "M", "kobold shaman", "k", "kobold zombie",
"Z", "kobold", "k", "kraken", ";", "large cat", "f", "large dog", "d",
"large kobold", "k", "large mimic", "m", "leather golem", "'", "lemure", "i",
"leocrotta", "q", "leprechaun", "l", "lich", "L", "lichen", "F", "lieutenant",
"@", "little dog", "d", "lizard", ":", "long worm", "w", "lurker above", "t",
"lynx", "f", "mail daemon", "&", "manes", "i", "marilith", "&", "master lich",
"L", "master mind flayer", "h", "mastodon", "q", "mind flayer", "h", "minotaur",
"H", "monk", "@", "monkey", "Y", "mountain centaur", "C", "mountain nymph", "n",
"mumak", "q", "nalfeshnee", "&", "neanderthal", "@", "newt", ":", "ninja", "@",
"nurse", "@", "ochre jelly", "j", "ogre king", "O", "ogre lord", "O", "ogre", "O",
"orange dragon", "D", "orc mummy", "M", "orc shaman", "o", "orc zombie", "Z",
"orc", "o", "orc-captain", "o", "owlbear", "Y", "page", "@", "panther", "f",
"paper golem", "'", "piranha", ";", "pit fiend", "&", "pit viper", "S",
"plains centaur", "C", "pony", "u", "priest", "@", "priestess", "@", "prisoner",
"@", "purple worm", "w", "pyrolisk", "c", "python", "S", "quantum mechanic", "Q",
"quasit", "i", "queen bee", "a", "quivering blob", "b", "rabid rat", "r",
"ranger", "@", "raven", "B", "red dragon", "D", "red mold", "F",
"red naga hatchling", "N", "red naga", "N", "rock mole", "r", "rock piercer", "p",
"rock troll", "T", "rogue", "@", "rope golem", "'", "roshi", "@", "rothe", "q",
"rust monster", "R", "salamander", ":", "samurai", "@", "sandestin", "&",
"sasquatch", "Y", "scorpion", "s", "sergeant", "@", "sewer rat", "r", "shade", " ",
"shark", ";", "shocking sphere", "e", "shopkeeper", "@", "shrieker", "F",
"silver dragon", "D", "skeleton", "Z", "small mimic", "m", "snake", "S",
"soldier ant", "a", "soldier", "@", "spotted jelly", "j", "stalker", "E",
"steam vortex", "v", "stone giant", "H", "stone golem", "'", "storm giant", "H",
"straw golem", "'", "student", "@", "succubus", "&", "tengu", "i", "thug", "@",
"tiger", "f", "titan", "H", "titanothere", "q", "tourist", "@", "trapper", "t",
"troll", "T", "umber hulk", "U", "valkyrie", "@", "vampire bat", "B",
"vampire lord", "V", "vampire", "V", "violet fungus", "F", "vrock", "&", "warg",
"d", "warhorse", "u", "warrior", "@", "watch captain", "@", "watchman", "@",
"water demon", "&", "water elemental", "E", "water moccasin", "S", "water nymph",
"n", "water troll", "T", "werejackal", "d", "wererat", "r", "werewolf", "d",
"white dragon", "D", "white unicorn", "u", "winged gargoyle", "g",
"winter wolf cub", "d", "winter wolf", "d", "wizard", "@", "wolf", "d",
"wood golem", "'", "wood nymph", "n", "woodchuck", "r", "wraith", "W", "wumpus",
"q", "xan", "x", "xorn", "X", "yellow dragon", "D", "yellow light", "y",
"yellow mold", "F", "yeti", "Y", "zruty", "z");
for my $monster (sort keys %monsters) {
run ["./jelly", "fu", "monsters.j", $monster], \ "", \my $out;
print "$monster -> \"$out\" (",
($out ne $monsters{$monster} ? "in" : ""), "correct)\n";
}
mail daemon
> _ <