ランダムなスーツレストランプデータを圧縮して、エントロピーエンコーディングストレージにアプローチ、マッチング、またはビートすることさえできますか?もしそうなら、どのように?


19

シミュレートされたカードゲームに使用している実際のデータがあります。スーツではなく、カードのランクにのみ興味があります。ただし、標準のカードデッキであるため、このデッキでは各ランクのうちしか使用できません。デッキは各ハンドでうまくシャッフルされ、その後、デッキ全体をファイルに出力します。そのため、出力ファイルには、シンボルが個しかありません。( = 10ランク)。したがって、もちろん、シンボルごとにビットを使用してこれらをビットパックできますが、可能なエンコードのうちを無駄にしています。一度にシンボルをグループ化してから圧縮すると、より良い結果が得られます。4 13 2 3 4 5 6 7 8 9 T J Q K A T 4524132,3,4,5,6,7,8,9,T,J,Q,K,AT416 4 13 43164134 =あり、代わりにビットにかなり「」収まることができます。理論的なビットパッキングの制限は、可能なカードごとにランダムシンボルを持つデータの場合、log()/ log()=です。しかし、このデッキには、たとえば王を置くことはできません。各デッキには各ランクがしかないため、エントロピーエンコーディングはシンボルごとに約0.5ビット、約低下します。28,56115161323.70044135243.2

わかりましたので、ここに私が考えていることです。このデータは完全にランダムではありません。枚のカード(シャッフルデッキと呼ばれる)の各ブロックには各ランクがあることがわかっているため、いくつかの仮定と最適化を行うことができます。そのうちの1つは、最後のカードをエンコードする必要はありません。何をすべきかがわかるからです。別の節約は、単一のランクで終了する場合です。たとえば、デッキの最後の枚のカードが場合、デコーダーはその時点までカードをカウントし、他のすべてのランクが満たされていることを確認し、を想定するため、それらをエンコードする必要はありません。行方不明」カードはすべてです。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

サンプルデータの最初のデッキをエンコードするためにビットを示す優れたアルゴリズムがあります。このデータは、Fisher-Yatesシャッフルアルゴリズムを使用してランダムに生成されました。それは本当のランダムデータなので、新しく作成されたアルゴリズムは非常にうまく機能しているようで、私は幸せです。168

圧縮の「チャレンジ」については、現在、デッキあたり約160ビットです。おそらく158まで下げることができると思います。はい、試しましたが、デッキあたり158.43ビットを得ました。アルゴリズムの限界に近づいていると思うので、デッキあたり166ビットを下回ることに成功しましたが、カードあたり3ビットである156ビットを取得できませんでしたが、楽しい練習でした。おそらく将来的には、各デッキを平均で2.43ビット以上削減する何かを考えます。


8
これらのシャッフルデッキを自分で生成する場合(たとえば、物理的なカードデッキの状態を記述するのではなく)、デッキをまったく保存する必要はありません。デッキを生成したRNGシードを保存するだけです。
ジェイソンハーパー

3
あなたの説明と回答の説明は、一般に範囲エンコード(en.wikipedia.org/wiki/Range_encoding)として知られている概念に非常に似ています。残りの可能なカードを反映するように、各カードの後に​​確率を適応させます。
H. Idden

コメントは詳細なディスカッション用ではありません。この会話はチャットに移動さました
ジル 'SO-悪であるのをやめる'

回答:


3

考慮すべきもう1つのこと:数百万デッキの完全なセットを圧縮するだけで、それらの順序を気にしない場合、デッキのセットに関する情報を破棄することで、エンコードの柔軟性をさらに高めることができます。これは、たとえば、すべてのデッキを列挙して処理するためにセットをロードする必要があるが、それらが処理される順序を気にしない場合に当てはまります。

他の回答で方法を説明しているように、各デッキを個別にエンコードすることから始めます。次に、エンコードされた値を並べ替えます。ソートされたエンコード値間の一連の差分を保存します(最初の差分はエンコードされたデッキ「0」から始まります)。デッキの数が多いと、差分はエンコード範囲全体よりも小さくなる傾向があるため、何らかの形式のvarintエンコードを使用して、小さな違いを効率的に保存しながら、時々大きな違いを処理できます。適切なvarintスキームは、セットにあるデッキの数によって異なります(したがって、平均差のサイズが決まります)。

残念ながら、これがあなたの圧縮にどれだけ役立つかについての数学は知りませんが、このアイデアは検討するのに役立つかもしれないと思いました。


1
非常に大雑把に言えば、数百万のランダムデッキがある場合、平均差は全範囲の1(数百万)になります。つまり、値ごとに約20ビットを節約することを意味します。varintエンコーディングで少し失われます。
スティーブジェソップ

2
@DavidJames:デッキの特定の順序が重要でない場合、バイアスが存在しないというだけで、解凍後に300万デッキをシャッフルできます(つまり、どのデッキも変更せず、単に順序を変更するだけです) 300万デッキのリスト)。
スティーブジェソップ

2
これは、注文情報が重要でない場合に、情報の内容をもう少し減らす方法です。重要な場合、これは適用されず、無視できます。とはいえ、デッキの順序の唯一の重要性が「ランダム」である場合、@ SteveJessopが述べたように、解凍後に順序をランダム化することができます。
ダンブライアント

@DavidJamesデッキの最初の173がKKKKで始まり、他の数百万は見えないこと、そしてすべてがKKKKで始まると結論付けるのは、かなり愚かなことです。特に、それらが明らかに並べ替えられている場合。
user253751

3
@DavidJames:このデータは圧縮されており、必要に応じて圧縮解除ルーチンで再ランダム化できます。「素朴な人」は何も得られません。彼らはそれをカードのデッキとして解釈する方法を理解することすらありません。データストレージフォーマット(この場合は損失の多いフォーマット)の欠陥ではありません。それを使用する誰かが正しいデータを取り出すためにRTFMを必要とするのです。
スティーブジェソップ

34

理論的な限界に達する完全なアルゴリズムを次に示します。

プロローグ:整数シーケンスのエンコード

13整数配列「上限値と整数上限と、整数B - 1、 『上限付き整数C - 1、上限付き整数D - 1、...上限値を持つ整数M - 1』常に完璧な効率でコーディングできます。a1b1c1d1m1

  1. 最初の整数を取得し、乗算し、2番目を加算し、結果をcで乗算し、3番目を加算し、dで乗算し、... mで結果を乗算し、13番目を加算します。およびa b c d e f g h i j k l m 1bcdm0abcdefghijklm1
  2. その番号をバイナリで書き留めます。

逆も簡単です。で除算し、残りは13番目の整数です。結果をlで割り、残りは12番目の整数です。bで割るまで続けます。余りは2番目の整数で、商は最初の整数です。mlb

したがって、可能な限り最良の方法でカードをコーディングするために必要なことは、13整数のシーケンス(上限を指定)とシャッフルされたカードの配置との間の完全な対応を見つけることです。

方法は次のとおりです。

シャッフルと整数シーケンスの対​​応

目の前のテーブルにある一連の0枚のカードから始めます。

ステップ1

パックの4つの2を取り出し、テーブルに置きます。

どのような選択肢がありますか?1つまたは複数のカードは、すでにテーブルにあるシーケンスの先頭、またはそのシーケンスのいずれかのカードの後に​​配置できます。その場合、これは場所にカードを置くことができることを意味します。1+0=1

4枚のカードを1箇所に配置する方法の総数はです。間の数として、これらの方法の各々をコード0および1 - 1。そのような番号が1つあります。1011

0の書き方を5つの整数の合計と考えると1になります4×3×2×14!

ステップ2

パックの4つの3を取り出し、テーブルに置きます。

どのような選択肢がありますか?1つまたは複数のカードは、すでにテーブルにあるシーケンスの先頭、またはそのシーケンスのいずれかのカードの後に​​配置できます。その場合、これは、カードを置く場所があることを意味します。1+4=5

5箇所に4枚のカードを配置する方法の総数はです。間の数として、これらの方法の各々をコード070 - 1。このような番号は70個あります。700701

4の書き方を5つの整数の合計と考えると、70になります8×7×6×54!

ステップ3

パック内の4つの4を取り出し、テーブルの上に置きます。

どのような選択肢がありますか?1つまたは複数のカードは、すでにテーブルにあるシーケンスの先頭、またはそのシーケンスのいずれかのカードの後に​​配置できます。その場合、これは場所にカードを置くことができることを意味します。1+8=9

4枚のカードを9か所に配置する方法の総数はです。間の数として、これらの方法の各々をコード0495 - 1。そのような番号は495個あります。49504951

8の書き方を5つの整数の合計と考えると495になります。これは12×11×10×94!

など、...まで

手順13

パックにある4つのエースをテーブルに置きます。

どのような選択肢がありますか?1つまたは複数のカードは、すでにテーブルにあるシーケンスの先頭、またはそのシーケンスのいずれかのカードの後に​​配置できます。その場合、これは場所にカードを置くことができることを意味します。1+48=49

49か所に4枚のカードを配置する方法の総数はです。間の数として、これらの方法の各々をコード0270725 - 1。そのような番号は270725個あります。27072502707251

私は5つの整数の和として48を書く方法を考慮して270725を得た:それは52×51×50×494!


この手順により、(a)スーツを気にしないカードのシャッフルと(b)最初がから1 1の間、2番目が0から70 - 図1は、第三の間で0495 - 1などの間で第十三までに0270725 - 101107010495102707251

「整数シーケンスのエンコード」を参照すると、このような整数のシーケンスは、から1 × 70 × 495 × × 270725 1までの数字と1-1で対応していることがわかります。(あなたが整数のそれぞれの表現「階乗で割った製品」を見れば、各ステップの終わりにイタリック体で説明したように)あなたが表示されます。この手段それとの間の数字052 0(1×70×495××270725)10以前の回答が示したものが最高でした。

52!(4!)131,

したがって、シャッフルしたカードを圧縮するための完璧な方法があります。


アルゴリズム

0を5つの整数の合計として、4を5つの整数の合計として、8を5つの整数の合計として、... 48を5つの整数の合計として書くすべての方法のリストを事前計算します。最も長いリストには270725個の要素があるため、特に大きくはありません。(必要に応じて各リストを簡単に合成できるため、事前計算は厳密には必要ありません:Microsoft QuickBasicを試してみると、270725要素のリストを通過することも目で見るよりも速くなりました)

整数のシーケンスへのシャッフルから取得するには:

2は何も寄与しないため、無視してみましょう。0〜1-1の数字を書き留めます。

3s:最初の3の前に2がありますか?2番目の前にいくつですか?第3?4日?4日後?答えは明らかに4になる5つの整数です。「5つの整数の合計として4を書く」リストで5つの整数のシーケンスを調べ、そのリスト内の位置をメモします。これは、0〜70-1の数値になります。それを書き留め。

4s:最初の4個の前に2個または3個ありますか?2番目の前にいくつですか?第3?4日?4日後?答えは明らかに8になる合計5つの整数です。したがって、「5つの整数の合計として8を書き込む」リストで5つの整数のシーケンスを調べ、そのリスト内での位置に注意してください。0〜495-1の数値になります。それを書き留め。

など、…まで

エース:最初のエースの前に何枚の非エースカードがありますか?2番目の前にいくつですか?第3?4日?4日後?答えは、明らかに合計48個になる5個の整数です。「5個の整数の合計として48を書き込む」リストで5個の整数のシーケンスを検索し、そのリスト内の位置をメモします。これは、0〜270725-1の数値になります。それを書き留め。

これで13個の整数を書き留めました。(前述のとおり)それらをから52の間の単一の数値にエンコードします0。その番号をバイナリで書きます。166ビット弱かかります。52!(4!)13

これは、情報理論的な限界に達するため、可能な限り最高の圧縮です。

解凍は簡単です。大きな数から13個の整数のシーケンスに進み、それらを使用して、既に説明したようにカードのシーケンスを構築します。


コメントは詳細なディスカッション用ではありません。この会話はチャットに移動さました
DW

この解決策は私には不明確で不完全です。実際に166ビットの数値を取得して、それをデコードしてデッキに戻す方法は示していません。想像するのはまったく簡単ではないので、それを実装する方法を知りません。基本的に、ステップ式はを分解するだけです!/4 1313個の式に分割しますが、実際にはあまり役に立ちません。カードを配置するための70の可能な方法を使用して、おそらくステップ2の図またはチャートを作成すると役立つと思います。あなたのソリューションは、私の脳が受け入れて処理するには抽象的すぎる。実際の例とイラストが好きです。52!/(4!13)13
デビッドジェームズ

23

各カードを個別に3ビットまたは4ビットにエンコードするのではなく、デッキ全体の状態を166ビットにエンコードすることをお勧めします。Martin Kochanskiが説明するように、スーツを無視したカードの可能な配置は個より少ないため、デッキ全体の状態は166ビットで保存できます。2166

効率的な方法で、この圧縮と解凍をアルゴリズム的にどのように行いますか?辞書編集順序とバイナリ検索を使用することをお勧めします。これにより、大きなルックアップテーブルやその他の非現実的な仮定を必要とせずに、圧縮と解凍を(空間と時間の両方で)効率的に行うことができます。

より詳細に:デッキの非圧縮表現で辞書式順序を使用してデッキを注文しましょう。つまり、デッキは22223333444455556666777788889999TTTTJJJJQQQQKKKKAAAAのような文字列として非圧縮形式で表されます。辞書順に並べ替えることができます。ここで、デッキを与えて、その前にあるデッキの数を(辞書式順序で)カウントするプロシージャがあるとします。次に、この手順を使用してデッキを圧縮できます。デッキDを指定すると、その前にあるデッキの数をカウントし、その数を出力することにより、166ビット数に圧縮します。その番号は、デッキの圧縮表現です。DD

解凍するには、バイナリ検索を使用します。番号を指定すると、すべてのデッキの辞書式順序でn番目のデッキを見つけたいと思います。あなたがバイナリサーチの線に沿って手順を使用してこれを行うことができます:デッキ選ぶD 0を、前にデッキの数を数えるD 0、およびにそれを比較するn個D 0を調整するかどうかがわかりますnnD0D0nD0早くまたは遅く来ます。22223333444455556666777788889999TTTTJJJJQQQQKKKKAAAAのような文字列を復元する場合は、文字列の最初の記号として使用するものを見つけるために最初に検索します(12の可能性すべてを試すか、12の可能性に対してバイナリ検索を使用します) )、最初のシンボルに適切な値が見つかったら、検索して2番目のシンボルを見つけます。

残るのは、前に辞書式に来るデッキの数を数える効率的な手順を考え出すことです。これは簡単ですが退屈な組み合わせの練習のように見えます。特に、次の問題のサブルーチンを作成することをお勧めします。プレフィックス(222234など)を指定して、そのプレフィックスで始まるデッキの数をカウントします。この問題への答えは、二項係数と階乗のかなり簡単な練習のように見えます。次に、このサブルーチンを数回呼び出して、Dの前に来るデッキの数をカウントできます。DD


コメントは詳細なディスカッション用ではありません。この会話はチャットに移動さました
DW

8

スーツを無視したカードの可能な配置の数はその対数の底2は165.976、またはカードあたり3.1919ビットであり、指定した制限よりも優れています。

52!(4!)13,

ご指摘のとおり、最後のカードは常にビットでエンコードでき、多くの場合、最後のいくつかのカードもエンコードできるため、固定の「ビット/カード」エンコードは意味がありません。これは、パックの「テール」に向かってかなりの方法で、各カードに必要なビット数があなたが考えるよりもかなり少ないことを意味します。0

データを圧縮する最善の方法は、とにかくカードデータでパックしたい他のデータの59ビット(実際には59.6ビット)を見つけ、それらの59ビットを24を法とする13桁の数値として書き込むことです(= )の間の各カード(一桁の選択にスーツ割り当て4 エースにスーツを割り当てる方法を、別のは、等)に王のために同じこと。次に、52枚のまったく異なるカードのパックがあります。52 実際、可能性は225.58ビットで非常に簡単にエンコードできます。4!4!52!

しかし、これらの余分なビットをエンコードする機会を利用せずにそれを行うこともある程度可能であり、他の誰もが確信しているように私はそれについて考えます。本当に興味深い問題をありがとう!


1
暗号文の盗難に似たアプローチをここで使用できますか?同様に、これらの追加の59ビットでエンコードするデータは、エンコードされた表現の最後の59ビットですか?
ジョンドヴォルザーク

@JanD私はこのようなことを調査することを考えていました。しかし、その後、理論的な限界に達し、簡単で100%信頼できるアルゴリズムが存在することが判明したため、これ以上調べる必要はありませんでした。
マーティンコチャンスキー

@MartinKochanski-ランクごとに標準の4つのスーツを引き続き尊重しているため、「スーツを無視する」とは言いません。より良い文言は「デッキの可能性の異なる配置の数がある」かもしれない...
デイビッド・ジェームズ

3

これは長い間解決された問題です。

52枚のカードのデッキを配るとき、配るカードはすべて、既知の確率を持つ最大13ランクのうちの1つを持ちます。確率は配られるカードごとに変わります。これは、ハフマンコーディングの改良である適応算術コーディングと呼ばれる古代の手法を使用して最適に処理されます。通常、これは既知の不変の確率に使用されますが、確率の変更にも使用できます。算術コーディングに関するウィキペディアの記事を読んでください。

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


わかりましたが、理論上のエントロピーエンコーディングの制限に近づき、一致し、それを超えることができるかどうか、これは私の質問に答えません。それぞれ1 / nの確率でn個のデッキが存在するため、エントロピーエンコーディングが限界であり、より良いことはできません(事前にエンコーダーへの入力データについてデコーダーに何かを「チート」して伝えない限り)。
デビッドジェームズ

3

DWとマーティンKochanskiの両方がすでに範囲でお得な情報や整数の間の全単射を構築するためのアルゴリズムを説明している、どちらも問題を最も単純な形に減らしていないようです。(注1)[0,52!(4!)13)

順序付きリストで記述される(部分的な)デッキがあるとします。ここでaiはタイプiのカードの数です。OPでは、初期デッキは13個の要素のリストで記述され、各要素には値4があります。そのようなデッキの個別のシャッフルの数はaaii

c(a)=(ai)!ai!

これは二項係数の単純な一般化であり、実際には、マーティンコチャンスキーが示唆するように、オブジェクトを一度に1種類ずつ配置するだけで証明できます。(下記の注2を参照)

現在、このような(部分的な)デッキについて、a i > 0のを使用して、一度に1枚のカードをシャッフルすることができます。iで始まる一意のシャッフルの数はiai>0i

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

上記の式により、

ca1a1a1a+1an=acaa

シャッフルは、最大プレフィックスよりプレフィックスに対応するシャッフルの数辞書順少ないことを観察することによって完了するまで、私たちはその後、デッキを再帰(または反復)することができあります

caj=1ajj=1naj

アルゴリズムを説明するためにPythonでこれを書きました。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秒弱かかりました。(7行はideoneのオンラインテストで見ることができます。)下位レベルの言語で書き直し、除算を最適化する(可能な場合)と、おそらく管理可能な時間になります。

エンコードされた値は単なる整数であるため、166ビットで出力できます。先頭のゼロを削除しても値はありません。エンコードが終了した場所を知る方法がないため、実際には166ビットのエンコードです。

ただし、実際のアプリケーションでは、シャッフルをエンコードする必要はおそらくないことに注意してください。ランダムシャッフルは、ランダムな166ビット数を生成してデコードすることで生成できます。また、166ビットすべてがランダムである必要はありません。たとえば、32ビットのランダムな整数で開始し、32ビットの数値でシードされた標準のRNGを使用して166ビットを埋めることができます。したがって、目標が単純に多数のランダムシャッフルを再現可能に保存できるようにすることである場合、取引ごとのストレージ要件を多かれ少なかれ任意に減らすことができます。

あなたは多数のエンコードしたい場合は(他のいくつかの方法で生成された)実際の取引のをしかし、取引の順番を気にしない、あなたは、デルタエンコード番号のソートされたリストをすることができ、約節約ログイン2つの Nそれぞれのビットを数。(節約された結果は、ソートされたシーケンスのエントロピーがソートされていないシーケンスよりも小さいという事実に起因します。シーケンス内の単一の値のエントロピーは減少しません。)Nログ2N

kビット数のソート済みリストをエンコードする必要があると仮定すると、次のように進めることができます。N k

  1. log 2 Nに近い整数としてを選択します(床または天井のいずれかが機能します。通常は天井に向かいます)。pログ2N

  2. 数値の範囲を進数の接頭辞で暗黙的に2 p間隔に分割します。各kビット数は、pビットのプレフィックスとk pビットのサフィックスに分割されます。接尾辞のみを順番に書きます。これには、N k p ビットが必要です。2pkpkpNkp

  3. また、我々は、ビットシーケンスを作成する:の各々について(接頭辞を除いプレフィックス0我々は書き出す)0を、続いて、その接頭辞(もしあれば)との各数に対して1。この配列は、明らかに有する2 P + N:ビット2 P 1つの SおよびN 0秒。2p0012p+N2p 1N 0

数値をデコードするには、0でプレフィックスカウンターを開始し、ビットシーケンスの処理に進みます。表示されたら、現在のプレフィックスと次のサフィックスをサフィックスリストから出力します。1が表示され たら、現在のプレフィックスをインクリメントします。01

エンコードの全長はこれはN k p + N + NまたはN k p + 2 に非常に近いK - P + 2つの値当たりのビット。Nkp+N+2pNkp+N+NNkp+2kp+2

ノート

  1. 92024242230271040357108320801872044844750000000000であり、log252です!5241392024242230271040357108320801872044844750000000000は約165.9765です。本文では、ときどき2を底とする対数が実際に166であるふりをしています。範囲内でランダムな序数を生成する場合、生成された乱数を非常にまれにしか拒否しない拒否アルゴリズムを使用できます。ログ252413165.9765166
  2. 便宜のために、私は、書き込み用のN Σ iは= k個のI。次いで、1種類のオブジェクト1内に配置することができる S 1Sk=knaa11方法、及びその後のタイプのオブジェクト2に配置することができるS2S1a12方法など。以来 SiS2a2、それは合計数につながりますSa=SaSa=SaS+1

=1nS=1naS+1

上記の式を単純化します。


コメントは詳細なディスカッション用ではありません。この会話はチャットに移動さました
DW

@rici-あなたがあなたの答えを説明した+100バウンティcuzを与えました。コードを含むより良いプレゼンテーションのように見えるもので、他の答えはより抽象的な/理論的で、実際にエンコード/デコードを実装する方法の詳細は省きます。ご存知かもしれませんが、コードを記述する際には多くの詳細があります。私のアルゴリズムは最も単純で、単純で、理解しやすいものでもないことを認めていますが、実際には多くの労力をかけずに機能し、時間の経過とともに圧縮率を上げることでより高速に実行できます。あなたの答えに感謝し、良い仕事を続けてください。
デビッドジェームズ

2

この問題の別の解決策として、私のアルゴリズムは、残っているランクの数に基づいて、デッキ内のカードのグループのカードごとに複合小数(非整数)ビットを使用します。かなりエレガントなアルゴリズムです。手作業でエンコードアルゴリズムを確認しましたが、見栄えは良いです。エンコーダは、正しいビット文字列と思われるものを出力します(簡単にするためにバイト形式で)。

私のアルゴリズムの概要は、カードのグループと複合フラクショナルビットエンコーディングの組み合わせを使用することです。たとえば、万枚のシャッフルデッキの共有テストファイルでは、最初のデッキには54 A 236 Jの最初の7枚のカードがあります。私が選んだ理由7と、カードブロックサイズを13枚のカードのランクが可能であるがためである13 7に"shoehorns"(ぴったりとフィット)26ビット(以降13 7 = 62 748 517及び2 26 = 67 108 3754A236J7131372613762748517226)。理想的には、これらの 2つの数値をできる限り近づけて(ただし、2の累乗をわずかに高くする)、ビットパッキングプロセスでビットのごく一部を無駄にしないようにします。私もGROUPSIZE選択されている可能性に注意 4を符号化するとき 13の以降のランクを 13 4 = 28 561及び 2 15 = 32 768。それはきついよう以来のフィットではありません 15 / 4 = 3.75が、 26 / 7 = 3.714671088642413134285612153276815/4=3.7526/7=3.714。したがって、パッキング方法を使用すると、カードごとのビット数はカードごとにわずかに少なくなります。26/7

だから、見て、我々は単に私たちのマスター「のそれらのランクの順序位置見上げる23456789 T J Q K A」ソートされたランクのリストを。たとえば、最初の実際のカードランク5は、ランクルックアップ文字列のルックアップ位置が4です。これらの7つのランク位置を、0から始まる13の基数として扱います(したがって、以前に取得した位置4は実際には3になります)。ベースに換算バック10(目的を確認するために)、我々が得る15 565 975。で2654A236J23456789TJQKA54713101556597526バイナリのビットはます。00111011011000010010010111

デコーダーは非常によく似た方法で機能します。これは、文字列は、(例えば)かかりバック小数ビットと変換し、(ベース10)を得るために15 565 975、次いで塩基に変換し、13それはランクを再構成、ランク検索文字列にオフセットを取得します一度に1枚ずつ、元の54 A 236 Jの最初の7枚のカードを受け取ります。ビットのブロックサイズは常に26ではありませんが、各デッキでは常に26から始まります。エンコーダーとデコーダーは両方とも、動作前でもデッキデータに関する重要な情報を持っています。これは、このアルゴリズムの非常に優れた点の1つです。26155659751354A236J7

残りのランクのそれぞれ#(例えば、独自GROUPSIZEとコスト(カード当たりのビット#)を有しています。これらは、実験だけの力で遊んで発見された13 12 11 ...との力213個のランクが表示される場合のグループサイズの取得方法については既に説明しましたが、12個の未使用ランクにドロップするとどうなりますか?同じ方法。12の累乗を見て、そのうちの1つが2の累乗に非常に近くなるが、そのわずかに下になると停止します。 13121121131211 ...21312122 = 248 832及び 2 18 = 262 144。それはかなりきついフィットです。このグループを符号化するビット数である 18 / 5 = 3.6。で 13ランクグループはあった 26 / 7 = 3.714あなたが見ることができるように充填されていないランクの数が減少するにつれて、(ランクは以下のような充填され 5555 3333、カードを符号化するビット数が減少します)。12524883221826214418/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

7567777KK1312713K2123 ...3131720

168131211

10777748747s。デッキがペア(77など)、トリプル/セット(777など)、またはクワッド(7777など)で終了する場合、アルゴリズムを使用してそのデッキでさらに節約できます。

3222613163232

データファイルの最初のデッキでは、カードのエンコードは次のとおりです(図は後ほど説明します)。形式は次のとおりです(グループサイズ、ビット、ランクエンコードモード):

7261372613
72613
72613
51812
51812
31010
3  9  8
617  7
513  6
3  5  3
1  0  1

521683.23

181/33.23.254545454722772277 ...322223333444455556666777788889999TTTTJJJJQQQQKKKKAAAA40

11037K810ランクモードでは、おそらくそのデッキに使用するビット数を減らすことができます。いくつかのカードの特別な「グループ化された」バケツにあるカードの1つが表示されたら、その特別な「ランク」(実際のランクではなく、その特別なバケツに何かを見ただけのインジケータ)を出力します。バケット内のどのカードを見たかをデコーダに伝えるためにビットを追加します(グループがいっぱいになったため)。これを手動でトレースして、それを使用してビット節約が可能かどうかを確認します。エンコーダーとデコーダーの両方がカードをカウントし、どのランクがのみを持っているかを知るため、この特別なバケットを使用してあいまいさがないようにしてください。1カードが残っています。これは、エンコーダーが余分なメッセージを渡すことなく、デコーダーが正しい仮定を立てることができる場合、エンコードプロセスをより効率的にするため、重要です。

313121110

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

2166175168ビット。デッキの最後に1つの4しか持っていないことに注意してください。代わりに4つすべてを4つ持っていれば、それはより良いケースであり、そのデッキをエンコードするために161ビットだけが必要でした。序数位置のストレートバイナリエンコードのエントロピー。

ビット要件を計算するためのコードを実装しました。平均で、デッキあたり約175ビット、300万デッキのテストファイルで155の最低値と183の最高値を示しています。したがって、私のアルゴリズムは、デッキごとに9余分なビットを使用するように見えます。わずか5.5%の追加ストレージスペースが必要なだけで、それほど悪くはありません。176ビットは正確に22バイトであるため、デッキあたり52バイトよりもかなり優れています。ベストケースデッキ(300万デッキのテストファイルには表示されませんでした)は136ビットにパックされ、ワー​​ストケースデッキ(テストファイルに8206回表示されました)は183ビットです。分析では、カード40に近づく(またはカード40になる)まで最初のクワッドが得られない場合が最悪のケースであることを示しています。その後、エンコードモードが急速に低下するため、高ビットエンコードモード。よくシャッフルされたデッキを使用して、カード40までクワッドを獲得しないのは非常にまれだと思うかもしれませんが、私のプログラムでは、300万デッキのテストファイルで321回発生したと言われています。それは私が予想していたよりも頻繁です。このケースをチェックして、より少ないビットで処理できますが、平均ビットに十分な影響を与えないほどまれです。

また、非常に興味深い何かがあります。生のデッキデータでデッキを並べ替えた場合、かなりの回数繰り返されるプレフィックスの長さは約6(222244など)だけです。ただし、パックされたデータでは、その長さが約16に増加します。つまり、パックされたデータを並べ替えると、デコーダに16ビットのプレフィックスを指定して残りのデッキを出力するだけで大​​幅な節約が得られるはずです。 (同じプレフィックスを除く)、同じプレフィックスがあり、次のプレフィックスに移動して繰り返します。この方法で1デッキあたり10ビットしか保存しないとすると、1デッキあたり166ビットを上回るはずです。他の人が述べた列挙手法では、プレフィックスがアルゴリズムの場合と同じかどうかはわかりません。また、私のアルゴリズムを使用したパッキングとアンパッキングの速度は驚くほど良好です。

アルゴリズムの出力ビット文字列を並べ替え、「差分」エンコーディングを使用する第2レベルの圧縮に関して:非常に簡単な方法は、出力データに少なくとも2回現れる最大61,278の一意の16ビットプレフィックスをエンコードすることです(最大報告された89回)単に出力の先頭ビット0として、プレフィクス(0000111100001111など)をエンコードしていることを第2レベルの解凍器に示し、その後、同じプレフィクスを持つパックされたデッキには、先頭に1ビットが続きますパックされたデッキの非プレフィックス部分を示します。同じプレフィックスを持つパックされたデッキの平均数は、各プレフィックスについて約49であり、一意の少数を含みません(その特定のプレフィックスを持つデッキは1つだけです)。この単純な戦略を使用して、デッキごとに約15ビットを節約できるようです(共通のプレフィックスを1回保存する)。

最初のエンコーダーのソートされたビットストリング出力の差分(プレフィックス)エンコードを使用した2番目のレベルの圧縮の後、デッキごとに約160ビットを取得しています。長さ18のプレフィックスを使用し、そのまま保存します。これらの可能な18ビットプレフィックスのほとんどすべて(262144のうち245013 = 93.5%)が表示されるため、プレフィックスをエンコードすることをお勧めします。おそらく、2ビットを使用して、所有しているデータのタイプをエンコードできます。00 =通常の長さの18プレフィックスを格納、01 =「1プレフィックスを追加」(1を追加することを除いて前のプレフィックスと同じ)、11 =第1レベルパッキングからのストレートエンコーディング(平均175ビット)。10 =ビットを節約するためにエンコードする他の何かを考えるときの将来の拡張。

他の誰かがデッキごとに160ビットを破ったか 上記で説明した2ビット記述子を使用し、実験を行うことで、少し低くなると思います。おそらく158ishで底を打つでしょう。私の目標は、156ビット(またはそれ以上)にすることです。カードあたり3ビット以下になるからです。非常に印象的。最初のレベルのエンコーディングを変更した場合、最高の2番目のレベルのエンコーディングである再テストを行う必要があり、試行する多くの組み合わせがあるため、そのレベルまで下げるための多くの実験があります。私が加えた変更は、他の同様のランダムデータには適している場合がありますが、このデータセットに偏っている場合もあります。確かではありませんが、もし衝動を得たら、別の300万デッキのデータセットを試して、同様の結果が得られたらどうなるかを確認できます。

1050

平均して各デッキのストレージのビットを削減する他のケースをエンコードする必要があるように、誰かが私のアルゴリズムを改善する方法についてアイデアを持っていますか?誰でも?

さらに2つのこと:1)より多くの人々が私のソリューションに賛成しなかったことに少しがっかりしています。これはスペース上は最適ではありませんが、まだまともで実装がかなり簡単です(私はうまくいきました)。2)私は300万デッキのデータファイルを分析し、1位が満たされる最も頻繁に発生するカード(4444など)がカード26にあることに気付きました。これは、時間の約6.711%に発生します(2013年の300万デッキ)。私はこの情報を使用して、12シンボルエンコードモードで開始するなど、さらに圧縮することを望んでいました。平均して、ミッドデッキについてはすべてのランクが表示されないことがわかっているためですが、この方法はオーバーヘッドが節約を超えるため、圧縮に失敗しました。実際にビットを節約できるアルゴリズムの調整を探しています。

だから誰も私のアルゴリズムを使用してデッキごとに数ビットを節約するために次にすべきことを考えていますか?デコーダに予想されるパターンを伝える余分なオーバーヘッドが発生した後でも、デッキあたりのビット数を減らすことができるように、頻繁に発生するパターンを探しています。目に見えない残りのカードの予想される確率で何かを考えていて、残りのすべての単一カードを単一のバケツにまとめました。これにより、私はより低いエンコードモードに素早くドロップし、おそらくいくつかのビットを節約できますが、私はそれを疑います。

また、参考までに、1,000万のランダムシャッフルを生成し、分析しやすいようにデータベースに保存しました。クワッドで終わるのはそのうちの488個(5555など)です。私のアルゴリズムを使用してそれらだけをパックすると、平均で157ビットの低ビットと173ビットの高ビットで165.71712ビットを取得します。他のエンコード方式を使用した166ビットよりわずかに下。私は、このケースの頻度が低いことに多少驚いています(平均で20,492シャッフルごとに約1)。


3
9時間の間に約24の編集を行ったことがわかります。あなたの答えを改善したいというあなたの願いに感謝します。ただし、回答を編集するたびに、これがフロントページの上部に移動します。そのため、過度の編集はお勧めしません。多数の編集を行う予定の場合、編集内容を一括して処理できるので、数時間ごとに1回だけ編集しますか?(ちなみに、答えに "EDIT:"と "UPDATE:"を入れるのは通常スタイルが悪いことに注意してください。meta.cs.stackexchange.com/ q / 657/755を参照してください
DW

4
これは、進捗レポート、ステータスの更新、またはブログアイテムを配置する場所ではありません。「近日中」や「解決策はありますが、それが何であるかを説明するつもりはありません」ではなく、完全な形式の回答が必要です。
DW

3
誰かが興味を持っている場合、彼は改善されたソリューションを見つけるでしょう。最善の方法は、完全な回答を待ってから投稿することです。いくつかの更新がある場合は、ブログが行います。これはお勧めしませんが、本当に必要な場合(理由が正当な理由がわからない場合)、投稿の下にコメントを書いて、後でマージできます。また、古いコメントをすべて削除し、1つのシームレスな質問に組み込むことをお勧めします。すべてを読むのは難しくなります。提示されているアルゴリズムとは異なる独自のアルゴリズムを作成しようとしていますが、結果に満足できません-そのため、編集するパーシャルを投稿しません-回答ボックスは完全なものです。

3
@DavidJames、わかりました。ただし、それでもガイドラインは変わりません。あまり編集しないでください。(Webサイトの改善を提案したい場合は、Computer Science Metaまたはmeta.stackexchange.comに投稿してください。開発者はこのコメントスレッドを読みません。)私たちが持っているソフトウェアを使って作業してください。多くの編集を行うことは、質問を一番上にぶつけるのでお勧めできません。この時点で、1日に1つの編集に制限することは、撮影するのに適したガイドラインです。オフラインエディターまたはStackEditを使用してください。
DW

3
いくつかの理由であなたの答えを支持していません。1)不必要に長く、FARは冗長すぎる。プレゼンテーションを大幅に減らすことができます。2)より良い回答が投稿されており、私には知らない理由で無視することを選択します。3)賛成票の不足について尋ねることは、通常私にとって「レッドフラッグ」です。4)これは、INSANEによる大量の編集のため、常にフロントページに残っています。
ニコラスマンクー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.