Может случайные suitless


19

У меня есть реальные данные, которые я использую для симуляции карточной игры. Меня интересуют только ряды карт, а не масти. Однако это стандартная колода из карт, так что в колоде возможно только каждого ранга. Колода хорошо перетасовывается для каждой руки, и затем я вывожу всю колоду в файл. Таким образом , существует только возможных символов в выходном файле , которые являются . ( = десять ранга). Поэтому, конечно, мы можем распаковать их, используя бита на символ, но тогда мы тратим из возможных кодировок. Мы можем добиться большего успеха, если сгруппируем символа за раз, а затем сжимаем их, потому что524132,3,4,5,6,7,8,9,T,J,Q,K,AT43164134 = и это может вписаться довольно «уютно» в бит вместо . Теоретический предел упаковки битов составляет log ( ) / log ( ) = для данных с случайными символами для каждой возможной карты. Однако у нас не может быть королей, например, в этой колоде. Мы ДОЛЖНЫ иметь только каждого ранга в каждой колоде, поэтому энтропийное кодирование уменьшается примерно на половину бита на символ до .28,56115161323.70044135243.2

Итак, вот что я думаю. Эти данные не являются полностью случайными. Мы знаем, что есть каждого ранга, поэтому в каждом блоке из карт (назовем это перемешанной колодой), поэтому мы можем сделать несколько предположений и оптимизаций. Один из тех, кому мы не должны кодировать самую последнюю карту, потому что мы будем знать, какой она должна быть. Другая экономия была бы, если бы мы закончили на одном звании; например, если последние карты в колоде - , нам не нужно их кодировать, потому что декодер будет считать карты до этого момента и увидит, что все остальные ранги заполнены, и примет " пропущенные "карточки все с.452377737

Поэтому мой вопрос к этому сайту: какие другие оптимизации возможны, чтобы получить еще меньший выходной файл для данных этого типа, и если мы их используем, сможем ли мы когда-нибудь превзойти теоретическую (простую) энтропию упаковки в бита на символ, или даже приблизиться к предельному пределу энтропии в среднем около бит на символ? Если так, то как?3.700443.2

Когда я использую программу типа ZIP (например, WinZip), я вижу только сжатие , которое говорит мне, что это просто "ленивый" битпак до бит. Если я «предварительно сжимаю» данные, используя свою собственную битовую упаковку, мне кажется, что это лучше, потому что тогда, когда я запускаю это через zip-программу, я получаю сжатие чуть более . Я думаю, почему бы не сделать все сжатие самостоятельно (потому что я знаю больше данных, чем программа Zip). Мне интересно, смогу ли я энтропийный «предел» log ( ) / log ( ) =2:142:11323.70044, Я подозреваю, что смогу с несколькими упомянутыми мною «хитростями» и еще несколькими, возможно, узнаю. Разумеется, выходной файл не должен быть «читабельным». Пока кодировка без потерь, она действительна.

Вот ссылка на миллиона читаемых в случайном порядке колод (по на строку). Любой может «попрактиковаться» на небольшом подмножестве этих строк, а затем позволить ему копировать весь файл. Я буду обновлять мой лучший (самый маленький) размер файла на основе этих данных.31

https://drive.google.com/file/d/0BweDAVsuCEM1amhsNmFITnEwd2s/view

Кстати, в случае, если вас интересует, для какого типа карточных игр используются эти данные, вот ссылка на мой активный вопрос (с вознаграждением в баллов). Мне говорят, что это сложная проблема (точно), так как она потребует огромного количества места для хранения данных. Несколько моделей согласуются с приблизительными вероятностями. Никаких чисто математических решений предоставлено не было (пока). Наверное, это слишком сложно.300

/math/1882705/probability-2-player-card-game-with-multiple-patterns-to-win-who-has-the-advant

У меня есть хороший алгоритм, который показывает бит для кодирования первой колоды в моих данных образца. Эти данные были получены случайным образом с использованием алгоритма случайного перемешивания Фишера-Йейтса. Это реальные случайные данные, поэтому мой недавно созданный алгоритм, кажется, работает ОЧЕНЬ хорошо, что меня радует.168

Что касается «проблемы» сжатия, то в настоящее время у меня около 160 бит на колоду. Я думаю, что могу опуститься до 158. Да, я попробовал, и я получил 158.43 бит на колоду. Я думаю, что я приближаюсь к пределу моего алгоритма, поэтому мне удалось опуститься ниже 166 бит на колоду, но мне не удалось получить 156 бит, что будет 3 бита на карту, но это было забавное упражнение. Возможно, в будущем я подумаю о том, чтобы уменьшить каждую колоду в среднем на 2,43 бит или более.


8
Если вы сами генерируете эти перемешанные колоды (например, вместо того, чтобы описывать состояние физической колоды карт), вам вообще не нужно хранить колоду - просто храните семя ГСЧ, которое породило колоду.
Джейсон Харпер

3
Ваше описание и ответы очень похожи на концепцию, широко известную как кодировка диапазона ( en.wikipedia.org/wiki/Range_encoding ). Вы адаптируете возможности после каждой карты, чтобы она отражала оставшиеся возможные карты.
Х. Идден

Комментарии не для расширенного обсуждения; этот разговор был перемещен в чат .
Жиль "ТАК - перестань быть злым"

Ответы:


3

Еще одна вещь, которую стоит учесть: если вам нужно только сжать полный набор из нескольких миллионов колод, а также не заботиться о том, в каком порядке они находятся, вы можете получить дополнительную гибкость кодирования, отбрасывая информацию о порядке набора колод. , Это будет иметь место, например, если вам нужно загрузить набор, чтобы перечислить все колоды и обработать их, но не волнует, в каком порядке они обрабатываются.

Вы начинаете с кодирования каждой колоды индивидуально, как другие ответы описали, как это сделать. Затем отсортируйте эти закодированные значения. Сохраните серию различий между отсортированными кодированными значениями (где первое различие начинается с кодированной колоды '0'). Учитывая большое количество колод, различия, как правило, будут меньше, чем полный диапазон кодирования, поэтому вы можете использовать некоторую форму кодирования вариаций для обработки случайных больших различий, при этом сохраняя при этом небольшие различия. Соответствующая схема вартинта будет зависеть от того, сколько колод у вас в наборе (таким образом, определяя средний размер разницы).

К сожалению, я не знаю математики того, насколько это поможет вашему сжатию, но подумал, что эта идея может быть полезной для рассмотрения.


1
Грубо говоря, если у вас есть несколько миллионов случайных колод, то средние различия будут составлять одну (несколько миллионных) от полного диапазона, что означает, что вы ожидаете сэкономить около 20 с чем-то битов на значение. Вы теряете немного для своей кодировки.
Стив Джессоп

2
@DavidJames: если конкретный порядок колод не важен, просто в нем нет предвзятости, вы можете перетасовать 3 миллиона колод после распаковки (т.е. не меняйте ни одну из колод, просто измените порядок список из 3 миллионов колод).
Стив Джессоп

2
Это просто способ уменьшить информационное содержание немного дальше, если информация о заказе не важна; если это важно, это не применимо и может быть проигнорировано. Тем не менее, если единственное значение для упорядочения набора колод заключается в том, что он «случайный», вы можете просто рандомизировать порядок после распаковки, как указано в @SteveJessop.
Дэн Брайант

@DavidJames Видеть, что первые 173 вашей колоды начинаются с KKKK, а не смотреть на другие несколько миллионов, и сделать вывод, что все они начинаются с KKKK, - довольно глупая вещь. Особенно если они явно в отсортированном порядке.
user253751

3
@DavidJames: эти данные сжимаются, и процедура распаковки может при необходимости повторно их рандомизировать. «Какой-то наивный человек» вообще ничего не получит, они даже не поймут, как интерпретировать это как колоды карт. Это не ошибка в формате хранения данных (в данном случае формат с потерями), что кто - то использует его нужно RTFM , чтобы получить нужные данные из.
Стив Джессоп

34

Вот полный алгоритм, который достигает теоретического предела.

Пролог: Кодирование целочисленных последовательностей

13-целочисленная последовательность "целое число с верхним пределом , целое число с верхним пределом b - 1 ," целое число с верхним пределом c - 1 , целое число с верхним пределом d - 1 , ... целое число с верхним пределом m - 1 " всегда можно кодировать с идеальной эффективностью.a1b1c1d1m1

  1. Возьмите первое целое число, умножьте его на , добавьте второе, умножьте результат на c , добавьте третье, умножьте результат на d ,… умножьте результат на m , добавьте тринадцатое - и это даст уникальное число между 0 и б с д е е г ч я J к л м - 1 .bcdm0abcdefghijklm1
  2. Запишите это число в двоичном виде.

Обратное тоже легко. Разделите на а остаток - тринадцатое целое число. Разделите результат на l, а остаток - двенадцатое целое число. Продолжайте до тех пор, пока вы не разделите на b : остаток - это второе целое число, а частное - это первое целое число.mlb

Поэтому, чтобы кодировать ваши карты наилучшим образом, все, что нам нужно сделать, - это найти идеальное соответствие между 13-целочисленными последовательностями (с заданными верхними пределами) и расположением ваших перемешанных карт.

Вот как это сделать.

Соответствие между тасовками и целочисленными последовательностями

Начните с последовательности из 0 карт на столе перед вами.

Шаг 1

Возьмите четыре двойки в вашем рюкзаке и положите их на стол.

Какой у вас есть выбор? Карта или карты могут быть помещены либо в начале последовательности, уже находящейся на столе, либо после любой из карт в этой последовательности. В этом случае это означает, что есть возможных мест для размещения карт.1+0=1

Общее количество способов размещения 4-х карт в 1-х местах - . Кодируйте каждый из этих способов как число от 0 до 1 - 1 . Есть 1 такой номер.1011

Я получил 1, рассматривая способы записи 0 как сумму 5 целых чисел: это ,4×3×2×14!

Шаг 2

Возьмите четыре тройки в вашем рюкзаке и положите их на стол.

Какой у вас есть выбор? Карта или карты могут быть помещены либо в начале последовательности, уже находящейся на столе, либо после любой из карт в этой последовательности. В этом случае это означает, что есть возможных мест для размещения карт.1+4=5

Общее количество способов размещения 4 карт в 5 местах - . Кодируйте каждый из этих способов числом от 0 до 70 - 1 . Есть 70 таких номеров.700701

Я получил 70, рассматривая способы записи 4 как сумму 5 целых чисел: это ,8×7×6×54!

Шаг 3

Возьмите четыре четверки в вашем пакете и положите их на стол.

Какой у вас есть выбор? Карта или карты могут быть помещены либо в начале последовательности, уже находящейся на столе, либо после любой из карт в этой последовательности. В этом случае это означает, что есть возможных мест для размещения карт.1+8=9

Общее количество способов размещения 4 карт в 9 местах составляет . Закодируйте каждый из этих способов как число между 0 и 495 - 1 . Есть 495 таких номеров.49504951

Я получил 495, рассматривая способы записи 8 как сумму 5 целых чисел: это ,12×11×10×94!

И так до тех пор, пока ...

Шаг 13

Возьмите четыре туза из своей колоды и положите их на стол.

Какой у вас есть выбор? Карта или карты могут быть помещены либо в начале последовательности, уже находящейся на столе, либо после любой из карт в этой последовательности. В этом случае это означает, что есть возможных мест для размещения карт.1+48=49

Общее количество способов размещения 4 карточек в 49 местах составляет . Закодируйте каждый из этих способов числом от 0 до 270725 - 1 . Есть 270725 таких номеров.27072502707251

Я получил 270725, рассматривая способы записи 48 как сумму 5 целых чисел: это ,52×51×50×494!


Эта процедура дает соответствие 1: 1 между (а) перетасовкой карточек, где вас не интересует масть, и (б) последовательностями целых чисел, где первая находится в диапазоне от до 1 - 1 , вторая - между 0 и 70 - 1 , третий находится в пределах от 0 и 495 - 1 , и так далее до тринадцатого, которая находится между 0 и 270725 - 1 .01107010495102707251

Ссылаясь на «Кодирование целочисленных последовательностей», вы можете видеть, что такая последовательность целых чисел находится в 1-1 соответствии с числами от до ( 1 × 70 × 495 × × 270725 ) - 1 . Если вы посмотрите на выражение «произведение, деленное на факториал» каждого из целых чисел ( как описано курсивом в конце каждого шага ), то увидите, что это означает числа от 0 до 52 !0(1×70×495××270725)10который показал мой предыдущий ответ, был наилучшим из возможных.

52!(4!)131,

Таким образом, у нас есть идеальный способ сжатия ваших перемешанных карт.


Алгоритм

Предварительно вычислить список всех способов записать 0 как сумму 5 целых чисел, записать 4 как сумму 5 целых чисел, записать 8 как сумму 5 целых чисел,… записать 48 как сумму 5 целых чисел. Самый длинный список содержит 270725 элементов, поэтому он не особенно большой. (Предварительные вычисления не являются строго необходимыми, потому что вы можете легко синтезировать каждый список по мере необходимости: пробовать с Microsoft QuickBasic, даже прохождение списка из 270725 элементов было быстрее, чем мог видеть глаз)

Чтобы перейти от тасования к последовательности целых чисел:

2s ничего не дают, поэтому давайте их проигнорируем. Запишите число от 0 до 1-1.

3s: сколько 2s есть до первых 3? Сколько перед вторым? третий? 4-й? после 4го? Ответ - 5 целых чисел, которые, очевидно, составляют в целом 4. Итак, посмотрите на эту последовательность из 5 целых чисел в вашем списке «написание 4 как сумма 5 целых» и отметьте его положение в этом списке. Это будет число от 0 до 70-1. Запиши это.

4: сколько 2 или 3 есть до первых 4? Сколько перед вторым? третий? 4-й? после 4го? Ответ - 5 целых чисел, которые, очевидно, составляют в сумме 8. Итак, найдите эту последовательность из 5 целых чисел в вашем списке «написание 8 как сумма 5 целых чисел» и отметьте его положение в этом списке. Это будет число от 0 до 495-1. Запиши это.

И так до тех пор, пока ...

Тузы: Сколько карт не туза до первого туза? Сколько перед вторым? третий? 4-й? после 4го? Ответ - 5 целых чисел, которые, очевидно, составляют в сумме 48. Итак, посмотрите на эту последовательность из 5 целых чисел в вашем списке «запись 48 как сумма 5 целых чисел» и отметьте его положение в этом списке. Это будет число от 0 до 270725-1. Запиши это.

Вы сейчас записали 13 целых чисел. Закодируйте их (как описано выше) в одно число от до 52 !0 . Запишите это число в двоичном виде. Это займет чуть менее 166 бит.52!(4!)13

Это наилучшее возможное сжатие, поскольку оно достигает информационно-теоретического предела.

Декомпрессия проста: перейдите от большого числа к последовательности из 13 целых чисел, а затем используйте их для построения последовательности карт, как уже описано.


Комментарии не для расширенного обсуждения; этот разговор был перемещен в чат .
DW

Это решение мне непонятно и неполно. Он не показывает, как на самом деле получить 166-битное число и декодировать его обратно в колоду. Это не так просто для меня, поэтому я не знаю, как это реализовать. Ваша пошаговая формула в основном просто разбирает формула на 13 частей, которая мне не очень помогает. Я думаю, что это помогло бы, если бы вы создали диаграмму или диаграмму для шага 2 с 70 возможными способами расстановки карт. Ваше решение слишком абстрактно для моего мозга, чтобы принять и обработать. Я предпочитаю реальные примеры и иллюстрации. 52!/(4!13)13
Дэвид Джеймс

23

Вместо того, чтобы пытаться кодировать каждую карту отдельно в 3 или 4 бита, я предлагаю вам кодировать состояние всей колоды в 166 бит. Как объясняет Мартин Кочански , существует менее возможных расположений карт без учета мастей, что означает, что состояние всей колоды может быть сохранено в 166 битах.2166

Как вы делаете это сжатие и распаковку алгоритмически, эффективным способом? Я предлагаю использовать лексикографическое упорядочение и бинарный поиск. Это позволит вам эффективно выполнять сжатие и распаковку (как в пространстве, так и во времени), не требуя большой справочной таблицы или других нереалистичных предположений.

Более подробно: давайте упорядочим колоды, используя лексикографическое упорядочение несжатого представления колоды, т. Е. Колода представлена ​​в несжатом виде в виде строки, например 22223333444455556666777788889999TTTTJJJJQQQQKKKKAAAA; Вы можете заказать их в соответствии с лексикографическим порядком. Теперь предположим, что у вас есть процедура, которая дает колоду , подсчитывает количество колод, предшествующих ей (в лексикографическом порядке). Затем вы можете использовать эту процедуру для сжатия колоды: с учетом колоды D вы сжимаете до 166-битного числа, считая количество колод, предшествующих ей, и затем выводя это число. Это число является сжатым представлением колоды.DD

Чтобы распаковать, используйте бинарный поиск. Учитывая число , вы хотите найти n- ю колоду в лексикографическом порядке всех колод. Вы можете сделать это, используя процедуру, аналогичную бинарному поиску: выбрать колоду D 0 , сосчитать количество колод до D 0 и сравнить это с n . Это скажет вам, нужно ли корректировать D 0nnD0D0nD0прийти раньше или позже. Я предлагаю вам попытаться итеративно получить правильный символ: если вы хотите восстановить строку типа 22223333444455556666777788889999TTTTJJJJQQQQKKKKAAAA, сначала выполните поиск, чтобы найти то, что использовать в качестве первого символа в строке (просто попробуйте все 12 возможностей или используйте бинарный поиск по 12 возможностям ), затем, когда вы нашли правильное значение для первого символа, выполните поиск, чтобы найти второй символ, и так далее.

Все , что остается , чтобы придумать эффективную процедуру для подсчета количества колод , которые приходят лексикографический перед тем . Это выглядит как прямое, но утомительное комбинаторное упражнение. В частности, я предлагаю вам создать подпрограмму для следующей задачи: учитывая префикс (например, 222234), подсчитайте количество колод, которые начинаются с этого префикса. Ответ на эту проблему выглядит как довольно простое упражнение с биномиальными коэффициентами и факториалами. Затем, вы можете вызвать эту подпрограмму небольшое число раз , чтобы подсчитать число колод , которые приходят , прежде чем D .DD


Комментарии не для расширенного обсуждения; этот разговор был перемещен в чат .
DW

8

Количество возможных расстановок карт без учета мастей - чья логарифмическая база 2 равна 165,976 или 3,1919 бит на карту, что лучше, чем установленный вами предел.

52!(4!)13,

Любое фиксированное кодирование «битов на карту» не будет иметь смысла, поскольку, как вы заметили, последняя карта всегда может быть закодирована в битах, и во многих случаях также могут быть последние несколько карт. Это означает, что для довольно быстрого перехода к «хвосту» колоды количество битов, необходимых для каждой карты, будет намного меньше, чем вы думаете.0

Безусловно, наилучшим способом сжатия данных было бы найти 59 битов других данных, которые вы все равно хотите упаковать с данными вашей карты (на самом деле 59,6 бит), и записать эти 59 битов как 13-значное число по модулю 24 (= ), присвоить костюм каждой карте (одна цифра выбирает между 4 ! способов присвоения костюмов для асов, другой делает то же самое для царей, и так далее). Тогда у вас есть колода из 52 совершенно разных карт. 52 ! Возможности могут быть очень легко закодированы в 225,58 бит.4!4!52!

Но сделать это без возможности кодирования этих дополнительных битов также возможно в некоторой степени, и я подумаю об этом, как, я уверен, все остальные. Спасибо за действительно интересную проблему!


1
Можно ли использовать подход, подобный краже зашифрованного текста ? Как, например, данные, которые вы кодируете в этих дополнительных 59 битах, являются последними 59 битами кодированного представления?
Джон Дворак

@JanD Я думал о расследовании чего-то подобного. Но затем оказалось, что существует алгоритм, который достигает теоретического предела и является простым и надежным на 100%, поэтому не было смысла смотреть дальше.
Мартин Кочански

@MartinKochanski - я бы не назвал это «игнорированием костюмов», потому что мы все еще соблюдаем стандартные 4 масти на ранг. Лучшей формулировкой может быть «Количество возможных различных аранжировок колоды» ...
Дэвид Джеймс

3

Это давно решенная проблема.

Когда вы разыгрываете колоду из 52 карт, каждая раздача карт имеет один из 13 рядов с известной вероятностью. Вероятности меняются с каждой раздачей карт. Это оптимально обрабатывается с использованием древней методики, называемой адаптивным арифметическим кодированием, улучшением кодирования Хаффмана. Обычно это используется для известных, неизменных вероятностей, но также может использоваться для изменения вероятностей. Прочитайте статью в Википедии об арифметическом кодировании:

https://en.wikipedia.org/wiki/Arithmetic_coding


Хорошо, но это не отвечает на мой вопрос, может ли оно приблизиться, соответствовать или превзойти теоретический предел энтропийного кодирования. Кажется, что, поскольку существует n возможных колод, каждая из которых имеет вероятность 1 / n, энтропийное кодирование является пределом, и мы не можем добиться большего успеха (если не будем «обманывать» и сообщать декодеру что-то о входных данных кодеру заранее).
Дэвид Джеймс

3

Как DW, так и Martin Kochanski уже описали алгоритмы построения биекции между сделками и целыми числами в диапазоне , но, похоже, ни один из них не уменьшил проблему до самой простой формы. (Примечание 1)[0,52!(4!)13)

Предположим, у нас есть (частичная) колода, описываемая упорядоченным списком , где a i - количество карт типа i . В OP начальная колода описывается списком из 13 элементов, каждый из которых имеет значение 4. Число различных перемешиваний такой колоды равноaaii

c(a)=(ai)!ai!

что является простым обобщением биномиальных коэффициентов и действительно может быть доказано простым упорядочением объектов по одному типу за раз, как это было предложено Мартином Кочански. (См. Примечание 2 ниже)

Теперь для любой такой (частичной) колоды мы можем выбрать одну карту за раз, используя любую для которой a i > 0 . Количество уникальных тасов, начинающихся с i, составляетiai>0i

{0if ai=0c(a1,...,ai1,ai1,ai+1,...,an)if ai>0.

и по приведенной выше формуле мы имеем

c(a1,...,ai1,ai1,ai+1,...,an)=aic(a)ai

Затем мы можем повторять (или повторять) всю колоду до тех пор, пока перемешивание не будет завершено, наблюдая, что число перемешиваний, соответствующих префиксу, лексикографически меньшему, чем префикс до равноi

c(a)j=1iajj=1naj

Я написал это на Python, чтобы проиллюстрировать алгоритм; Python такой же разумный псевдокод, как и любой другой. Обратите внимание, что большая часть арифметики включает в себя расширенную точность; значения (представляющие порядковый номер тасования) и n (общее количество возможных тасов для оставшейся частичной колоды) являются 166-битовыми значениями. Чтобы перевести код на другой язык, необходимо будет использовать какую-то библиотеку bignum.kn

Кроме того, я просто использую список целых чисел, а не имен карт, и - в отличие от приведенной выше математики - целые числа основаны на 0.

Чтобы закодировать перемешивание, мы проходим по перемешиванию, накапливая в каждой точке количество перемешиваний, которые начинаются с меньшей карты, используя приведенную выше формулу:

from math import factorial
T = factorial(52) // factorial(4) ** 13

def encode(vec):
    a = [4] * 13
    cards = sum(a)
    n = T
    k = 0
    for idx in vec:
        k += sum(a[:idx]) * n // cards
        n = a[idx] * n // cards
        a[idx] -= 1
        cards -= 1
    return k

Декодирование 166-битного числа является простым обратным. На каждом шаге у нас есть описание частичной колоды и порядковый номер; нам нужно пропустить перемешивание, начиная с карточек меньшего размера, чем та, которая соответствует порядковому номеру, а затем мы вычисляем вывод выбранной карточки, удаляем ее из оставшейся колоды и корректируем количество возможных перемешиваний с выбранным префиксом:

def decode(k):
    vec = []
    a = [4] * 13
    cards = sum(a)
    n = T
    while cards > 0:
        i = cards * k // n
        accum = 0
        for idx in range(len(a)):
            if i < accum + a[idx]:
                k -= accum * n // cards
                n = a[idx] * n // cards
                a[idx] -= 1
                vec.append(idx)
                break
            accum += a[idx]
        cards -= 1
    return vec

Я не пытался оптимизировать приведенный выше код. Я запустил его для всего файла 3mil.TXT, проверив, что encode(decode(line))привело к оригинальной кодировке; это заняло чуть менее 300 секунд. (Семь строк можно увидеть в онлайн-тесте на ideone .) Переписывание на языке более низкого уровня и оптимизация деления (что возможно), вероятно, сократило бы это время до чего-то управляемого.

Поскольку закодированное значение является просто целым числом, оно может быть выведено в 166 битах. Нет смысла удалять начальные нули, так как тогда не было бы никакой возможности узнать, где завершилась кодировка, так что это действительно 166-битная кодировка.

Однако стоит отметить, что в практическом приложении, вероятно, никогда не нужно кодировать случайный случай; случайный случайный порядок может быть сгенерирован путем генерации случайного 166-битного числа и его декодирования. И совсем не обязательно, чтобы все 166 бит были случайными; было бы возможно, например, начать с 32-битного случайного целого числа, а затем заполнить 166 битов, используя любой стандартный RNG, засеянный 32-битным числом. Таким образом, если цель состоит в том, чтобы просто иметь возможность воспроизводить большое количество случайных тасов, вы можете более или менее произвольно уменьшить требования к хранилищу для каждой сделки.

Если вы хотите , чтобы кодировать большое число фактических сделок (генерируемых каким - либо другим способом) , но не заботиться о порядке сделок, вы можете дельта-кодирование отсортированный список номеров, сэкономив примерно войти 2 N битов для каждого число. (Экономия является результатом того факта, что отсортированная последовательность имеет меньшую энтропию, чем несортированная последовательность. Она не уменьшает энтропию одного значения в последовательности.)Nlog2N

Предполагая, что нам нужно закодировать отсортированный список из k- битных чисел, мы можем действовать следующим образом:N k

  1. Выберите в качестве целого числа, близкого к log 2 N (пол или потолок будут работать; обычно я иду к потолку).plog2N

  2. Мы неявно делим диапазон чисел на интервала двоичным префиксом. Каждый к номеру -битный делится на р -битных префикса и к - р -битный суффикс; выписываем только суффиксы (по порядку). Это требует N ( k - p ) битов.2pkpkpN(kp)

  3. Кроме того, мы создаем битовую последовательность: для каждого из префиксов (кроме префикса 0 ) мы записываем 0 для каждого числа с этим префиксом (если есть), за которым следует 1 . Эта последовательность, очевидно, имеет 2 p + N битов: 2 p 1 с и N 0 с.2p0012p+N2p 1N 0

Чтобы декодировать числа, мы запускаем счетчик префиксов с 0 и переходим к работе с битовой последовательностью. Когда мы видим , мы выводим текущий префикс и следующий суффикс из списка суффиксов; когда мы видим 1 , мы увеличиваем текущий префикс.01

Общая длина кодирования составляет что очень близко к N ( k - p ) + N + N или N ( k - p + 2 ) для среднего значения из к - р + 2 бита на значение.N(kp)+N+2pN(kp)+N+NN(kp+2)kp+2

Примечания

  1. -92024242230271040357108320801872044844750000000000иlog252!52!(4!)1392024242230271040357108320801872044844750000000000 составляет примерно165,9765. В тексте я иногда притворяюсь, что логарифм base-2 действительно166; в случае генерации случайных порядковых чисел в пределах диапазона может использоваться алгоритм отклонения, который очень редко отклоняет сгенерированное случайное число.log252!(4!)13165.9765166
  2. Для удобства я пишу для n i = k a i ; затем через 1 объекты типа 1 могут быть помещены в ( S 1SКΣязнак равноКNaяa11пути, а затем объекты типа2могут быть размещены в(S2(S1a1)2пути и тд. Так как ( Sя(S2a2), что приводит к общему количеству(Sяaя)знак равноSя!aя!(Sя-aя)!знак равноSя!aя!Sя+1!

Πязнак равно1NSя!Πязнак равно1Naя!Sя+1!

что упрощает формулу выше.


Комментарии не для расширенного обсуждения; этот разговор был перенесен в чат .
DW

@rici - я дал вам +100 награду, потому что вы объяснили свой ответ в том, что кажется лучшей презентацией, включая код, в то время как другие ответы более абстрактны / теоретичны, оставляя некоторые детали того, как на самом деле реализовать кодирование / декодирование. Как вы, наверное, знаете, при написании кода есть много деталей. Я признаю, что мой алгоритм не является самым простым, простым и легким для понимания, но я фактически заставил его работать без особых усилий, и со временем я могу заставить его работать быстрее с большим сжатием. Так что спасибо за ваш ответ и продолжайте в том же духе.
Дэвид Джеймс

2

В качестве альтернативного решения этой проблемы мой алгоритм использует составные дробные (нецелые) биты на карту для групп карт в колоде на основе количества оставшихся незаполненных рангов. Это довольно элегантный алгоритм. Я проверил мой алгоритм кодирования вручную, и он выглядит хорошо. Кодировщик выводит то, что представляется правильными цепочками битов (в байтовой форме для простоты).

Обзор моего алгоритма состоит в том, что он использует комбинацию групп карт и составное дробное кодирование битов. Например, в моем общем тестовом файле, состоящем из миллионов перемешанных колод, первая имеет первые 7 карт 54 A 236 Дж . Причина, по которой я выбрал 7- карточный размер блока, когда возможно 13 рядов карт, заключается в том, что 13 7 «рожков для обуви» (плотно прилегают) в 26 бит (так как 13 7 = 62 , 748 , 517 и 2 26 = 67 , 108 ,3754A236J7131372613762,748,517226 ). В идеале мы хотим, чтобы эти 2 числа были как можно ближе (но с силой 2 числа немного выше), чтобы не тратить впустую больше, чем очень маленькую долю в процессе упаковки битов. Обратите внимание, что я мог бы также выбрать размер группы 4 при кодировании 13 рангов, поскольку 13 4 = 28 , 561 и 2 15 = 32 , 768 . Это не так туго припадоктак как +15 / 4 = 3,75 , но +26 / +7 = 3,71467,108,864241313428,56121532,76815/4=3.7526/7=3.714, Таким образом, число битов на карте несколько ниже , на карте , если мы используем способ упаковки.26/7

54A236J23456789TJQKA547131015,565,9752600111011011000010010010111

2615,565,9751354A236J7

13,12,11...,2,1)13,12,11...21312122125248,832218262,14418/53.61326/73.71455553333

Вот мой полный список затрат (количество бит на карту) для всех возможных количеств рангов:

13    26/7=3.714=3  5/7
12    18/5=3.600=3  3/5
11      7/2=3.500=3  1/2
10    10/3=3.333=3  1/3
  9    16/5=3.200=3  1/5
  8      3/1=3.000=3
  7    17/6=2.833=2  5/6
  6    13/5=2.600=2  3/5
  5      7/3=2.333=2  1/3
  4      2/1=2.000=2
  3      5/3=1.667=1  2/3
  2      1/1=1.000=1
  1      0/1..4=0.0=0

75,6,7,7,7,7,KK1312713K21,2,3...3131720

16813,12,11

10777748747s. Если колода заканчивается парой (например, 77), тройной / сет (например, 777) или четверкой (например, 7777), мы получаем дополнительную экономию для этой колоды, используя мой алгоритм.

3222613163232

В первой колоде файла данных кодирование карточек выглядит следующим образом (схема будет представлена ​​позже). Формат (размер группы, биты, режим кодирования ранга):

7,26,1372613
7,26,13
7,26,13
5,18,12
5,18,12
3,10,10
3,  9,  8
6,17,  7
5,13,  6
3,  5,  3
1,  0,  1

521683.23

181/33.23,254545454722772277...322223333444455556666777788889999TTTTJJJJQQQQKKKKAAAA40

1103,7K8101карта осталась. Это важно, потому что это делает процесс кодирования более эффективным, когда декодер может делать правильные предположения без необходимости передавать ему дополнительные сообщения.

313121110

54 A 236 J 87726 Q 3 3969 A A A Q J K 7 T         26             26             26            18         18       10      9          17           13        5     0
    54A236J  87726Q3  3969AAA  QJK7T  9292Q  36K  J57   T8TKJ4  48Q8T  55K  4
13                                            12                    xy     98         7              6        543     2 1  0

2166175168биты. Обратите внимание, что мы получили только один 4 в конце колоды, но если бы вместо этого мы получили все четыре 4, это лучший случай, и нам потребовалось бы только 161 бит для кодирования этой колоды, случай, когда упаковка фактически побеждает энтропия прямого двоичного кода его порядкового положения.

Теперь у меня есть код, реализованный для расчета требований к битам, и он показывает мне в среднем около 175 бит на деку с минимумом 155 и максимумом 183 для тестового файла с 3 миллионами колод. Таким образом, мой алгоритм использует 9 дополнительных бит на деку по сравнению с прямым двоичным кодированием метода порядкового положения. Не так уж плохо, только на 5,5% требуется дополнительное место для хранения. 176 бит это ровно 22 байта, так что это немного лучше, чем 52 байта на деку. Колода в лучшем случае (не показанная в тестовом файле с 3 миллионами колод) упаковывается в 136 битов, а колода в худшем случае (показанная в тестовом файле 8206 раз) составляет 183 бита. Анализ показывает, что наихудший случай - когда мы не получаем первый четырехугольник до тех пор, пока он не приблизится к (или не достигнет) 40-й карты. Затем, когда режим кодирования хочет быстро упасть, мы «застряли», заполняя блоки (размером до 7 карт) в более высокий режим кодирования битов. Кто-то может подумать, что не получить никаких квадов до 40-й карты было бы довольно редко, если использовать хорошо перемешанную колоду, но моя программа сообщает мне, что это произошло 321 раз в тестовом файле из 3 миллионов колод, так что это примерно 1 из каждых 9346 колод. Это чаще, чем я ожидал. Я мог бы проверить этот случай и обработать его с меньшим количеством битов, но это настолько редко, что это не повлияет на средние биты достаточно.

Также здесь есть кое-что еще очень интересное. Если я сортирую колоду по необработанным данным колоды, длина префиксов, которые повторяются значительное количество раз, составляет всего около 6 (например, 222244). Однако с упакованными данными эта длина увеличивается примерно до 16. Это означает, что если я отсортирую упакованные данные, я смогу получить значительную экономию, просто указав декодеру 16-битный префикс, а затем просто выведу оставшуюся часть колод (минус повторяющийся префикс), которые имеют тот же префикс, затем перейдите к следующему префиксу и повторите. Предполагая, что я сохраню даже 10 бит на колоду, я должен побить 166 бит на колоду. С техникой перечисления, заявленной другими, я не уверен, будет ли префикс таким же длинным, как в моем алгоритме. Также скорость упаковки и распаковки по моему алгоритму удивительно хороша.

Что касается 2-го уровня сжатия, где я сортирую выходные битовые строки моего алгоритма, а затем использую «разностную» кодировку: очень простой метод заключается в кодировании 61 278 уникальных 16-битных префиксов, которые отображаются как минимум дважды в выходных данных (и максимум 89 раз) просто в качестве начального бита 0 в выходных данных, чтобы указать декомпрессору 2-го уровня, что мы кодируем префикс (например, 0000111100001111), и затем любые упакованные колоды с таким же префиксом будут следовать с 1 начальным битом указать не префиксную часть упакованной колоды. Среднее количество упакованных колод с одинаковым префиксом составляет около 49 для каждого префикса, не считая нескольких уникальных (только одна колода имеет этот конкретный префикс). Похоже, я могу сэкономить около 15 бит на колоду, используя эту простую стратегию (сохраняя общие префиксы один раз).

После 2-го уровня сжатия с использованием разностного (префиксного) кодирования отсортированного вывода цепочки битов первого кодера я получаю около 160 бит на деку. Я использую префикс длины 18 и просто храню его без изменений. Поскольку почти все (245013 из 262144 = 93,5%) из этих возможных 18-битных префиксов отображаются, было бы еще лучше кодировать префиксы. Возможно, я могу использовать 2 бита, чтобы закодировать, какой тип данных у меня есть. 00 = обычный префикс 18 длины сохранен, 01 = «1 префикс up» (такой же, как предыдущий префикс, кроме 1 добавленного), 11 = прямое кодирование из упаковки 1-го уровня (в среднем около 175 бит). 10 = будущее расширение, когда я думаю о чем-то еще, чтобы кодировать, чтобы сохранить биты.

Кто-нибудь еще побил 160 бит на колоду? Я думаю, что смогу немного снизить мой уровень, экспериментируя и используя 2-битные дескрипторы, которые я упоминал выше. Возможно, он достигнет дна на 158-м. Моя цель - получить 156 бит (или лучше), потому что это будет 3 бита на карту или меньше. Очень впечатляюще. Много экспериментов, чтобы довести его до этого уровня, потому что если я изменю кодировку первого уровня, то мне придется повторно протестировать, которая является лучшей кодировкой 2-го уровня, и есть много комбинаций, которые можно попробовать. Некоторые изменения, которые я делаю, могут быть полезны для других подобных случайных данных, но некоторые могут быть смещены в сторону этого набора данных. Не совсем уверен, но если я получу желание, я могу попробовать другой набор данных из трех миллионов колод, чтобы посмотреть, что произойдет, если я получу аналогичные результаты на нем.

1050

У кого-нибудь есть идеи о том, как сделать мой алгоритм лучше, например, какие другие случаи я должен кодировать, чтобы уменьшить объем памяти для каждой колоды в среднем? Кто-нибудь?

Еще 2 вещи: 1) Я несколько разочарован тем, что больше людей не одобрили мое решение, которое, хотя и не является оптимальным по пространству, все же является приличным и довольно простым для реализации (у меня все работает нормально). 2) Я выполнил анализ своего файла данных с 3 миллионами колод и заметил, что наиболее часто встречающаяся карта с заполнением 1-го ранга (например, 4444) находится на карте 26. Это происходит примерно в 6,711% времени (для 201322 из 3 миллионов колод) ). Я надеялся использовать эту информацию, чтобы сжать больше, например, начать в режиме 12-символьного кодирования, так как мы знаем, что в среднем мы не видим каждый ранг до середины колоды, но этот метод не сжимал ни одного, поскольку его издержки превышали экономию. Я ищу некоторые изменения в моем алгоритме, которые могут реально сохранить биты.

Так есть ли у кого-нибудь идеи, что мне следует попробовать, чтобы сэкономить несколько бит на колоду, используя мой алгоритм? Я ищу паттерн, который встречается достаточно часто, чтобы я мог уменьшить количество бит на деку даже после дополнительных затрат на указание декодеру, какой паттерн ожидать. Я думал что-то с ожидаемой вероятностью оставшихся невидимых карт и складывал все оставшиеся одиночные карты в одно ведро. Это позволит мне быстрее перейти в режим более низкого кодирования и, возможно, сохранить некоторые биты, но я сомневаюсь в этом.

Кроме того, к вашему сведению, я сгенерировал 10 миллионов случайных перемешиваний и сохранил их в базе данных для удобства анализа. Только 488 из них заканчиваются квадом (например, 5555). Если я упаковываю только те, которые используют мой алгоритм, я получаю в среднем 165.71712 бит с низким 157 битам и высоким 173 битам. Чуть ниже 166 бит, используя другой метод кодирования. Я несколько удивлен тем, насколько редок этот случай (примерно 1 из каждых 20 492 тасовок в среднем).


3
Я заметил, что вы сделали около 24 правок за 9 часов. Я ценю ваше желание улучшить ваш ответ. Тем не менее, каждый раз, когда вы редактируете ответ, он попадает в верхнюю часть первой страницы. По этой причине мы не рекомендуем чрезмерное редактирование. Если вы планируете вносить много правок, можно ли будет отредактировать ваши правки, поэтому вы делаете только одну правку каждые несколько часов? (Между прочим, обратите внимание, что вставка «РЕДАКТИРОВАТЬ:» и «ОБНОВЛЕНИЕ:» в вашем ответе обычно является плохим стилем. См. Meta.cs.stackexchange.com/q/657/755. )
DW

4
Это не место для размещения отчетов о проделанной работе, обновлений статуса или элементов блога. Нам нужны полностью сформированные ответы, а не «скоро» или «у меня есть решение, но я не буду описывать, что это такое».
DW

3
Если кому-то интересно, он найдет улучшенное решение. Лучший способ - дождаться полного ответа и опубликовать его. Если у вас есть некоторые обновления, блог будет делать. Я не поощряю это, но если вы действительно должны (я не вижу веской причины, почему), вы можете написать комментарий под своим постом и объединить позже. Я также призываю вас удалить все устаревшие комментарии и объединить их в один простой вопрос - становится трудно читать все. Я пытаюсь создать свой собственный алгоритм, отличный от любого из представленных, но я не доволен результатами - поэтому я не публикую партиалы для редактирования - поле ответа предназначено для полных.
Зло

3
@DavidJames, я понимаю. Однако это по-прежнему не меняет наших правил: пожалуйста, не вносите так много правок. (Если вы хотите предложить улучшения для веб-сайта, не стесняйтесь сделать сообщение на нашей Meta Computer Science Meta или meta.stackexchange.com с предложением об этом. Разработчики не читают эту ветку комментариев.) Но пока что мы работать с программным обеспечением, которое у нас есть, и делать много правок не рекомендуется, потому что это поднимает вопрос к вершине. На этом этапе, ограничиваясь одним редактированием в день, вы можете стать хорошим ориентиром. Не стесняйтесь использовать автономные редакторы или StackEdit, если это поможет!
DW

3
Я не одобряю ваш ответ по нескольким причинам. 1) это ненужно долго и FAR слишком многословно. Вы можете резко уменьшить его представление. 2) опубликованы лучшие ответы, которые вы предпочитаете игнорировать по незаметным для меня причинам. 3) спрос на отсутствие голосов обычно является для меня «красным флагом». 4) Это постоянно оставалось на первой странице из-за безумного количества правок.
Николас Манкузо
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.