web-dev-qa-db-ja.com

数値範囲を保存する最も効率的な方法は何ですか?

この質問は、範囲を格納するために必要なビット数についてです。または別の言い方をすると、与えられたビット数に対して、保存できる最大範囲はどのくらいですか?

0-255の範囲内のサブ範囲を格納したいとします。

たとえば、45-74です。

上記の例は2つの符号なしバイトとして格納できますが、そこには情報の冗長性が必要だと思います。 2番目の値が最初の値よりも大きいことがわかっているため、最初の値が大きい場合は2番目の値に必要なビットが少なくなり、2番目の値が大きい場合は最初の値に必要なビットが少なくなります。 。

どんな圧縮手法でも限界的な結果がもたらされるのではないかと思うので、「1バイトに格納できる最大範囲はいくつですか?」これは、2つの数値を別々に格納することで得られる値よりも大きくする必要があります。

この種のことを行うための標準的なアルゴリズムはありますか?

29
rghome

可能な範囲の数を数えるだけです。下限0(0-0、0-1、... 0-254、0-255)の256の範囲、下限1の255の範囲、そして最後に下限255(255- 255)。したがって、合計数は(256 + 255 + ... + 1)= 257 * 128 = 32,896になります。これは2より少し高いので15 = 32,768。この情報を保存するには、少なくとも16ビット(2バイト)が必要です。

一般に、0からn-1までの数値の場合、可能な範囲の数はn *(n + 1)/ 2です。 nが22以下の場合、これは256未満です。n= 22は22 * 23/2 = 253の可能性を与えます。つまり1バイト-21のサブ範囲には十分です。

問題を調べる別の方法は次のとおりです。0からn-1の範囲の整数のペアを格納することは、0n-1)にサブレンジを追加して格納することとほぼ同じです---(単一ビット =最初の数値が2番目の数値より小さいか大きいかを決定します。 (違いは両方の整数が等しい場合から来ますが、nが大きくなるにつれてこの可能性はますます小さくなります。)このため、この手法では1ビット程度しか節約できず、おそらくそれがほとんど使用されない主な理由です。

58
Glorfindel

そのようなビット数が少ない場合、 Glorfindelが指摘した のように多くのビットを保存することは不可能です。ただし、使用しているドメインのビット数がさらに多い場合は、範囲を開始値とデルタでエンコードすることにより、平均的なケースで大幅な節約を実現できます。

ドメインが整数、つまり32ビットであると仮定しましょう。単純なアプローチでは、範囲を格納するために64ビット(開始、終了)が必要です。

(start、delta)のエンコーディングに切り替えると、それから範囲の終わりを構築できます。最悪の場合、開始は0で、デルタは32ビットです。

2 ^ 5は32であるため、デルタの長さを5ビットでエンコードし(ゼロ長なし、常に1を追加)、エンコーディングは(開始、長さ、デルタ)になります。最悪の場合、これは32 * 2 + 5ビットなので、69ビットになります。したがって、最悪の場合、すべての範囲が長いと、これは単純なエンコーディングよりも悪くなります。

最良のケースでは、32 + 5 + 1 = 38ビットです。

つまり、多くの範囲をエンコードする必要があり、それらの範囲がそれぞれドメインのごく一部しかカバーしていない場合、平均してより少ないスペースを使用することになりますこのエンコーディングを使用します。開始は常に32ビットを取るため、開始がどのように分散されるかは関係ありませんが、範囲の長さがどのように分散されるかは重要です。長さが短いほど、圧縮率が高くなり、ドメインの全長をカバーする範囲が増えるほど、このエンコーディングは悪化します。

ただし、(たとえば、センサーから値を取得するため)同様の開始点の周りにグループ化された範囲のロットがある場合、さらに大きな節約を達成できます。同じ手法を開始値に適用し、バイアスを使用して開始値をオフセットすることができます。

10000個の範囲があるとします。範囲は特定の値を中心にグループ化されます。バイアスを32ビットでエンコードします。

単純なアプローチを使用すると、これらのすべての範囲を格納するために32 * 2 * 10 000 = 640 000ビットが必要になります。

バイアスのエンコードには32ビットが必要で、各範囲のエンコードには5 + 1 + 5 + 1 = 12ビットが最適で、合計で120 000 + 32 = 120 032ビットになります。最悪の場合、5 + 32 + 5 + 32ビット、つまり74ビットが必要になり、合計で740 032ビットになります。

つまり、エンコードに32ビットを必要とするドメインの10 000個の値に対して、

  • 最良の場合のスマートデルタエンコーディングを備えた120 032ビット
  • 単純な開始、終了のエンコードを使用した64万000ビット(常に最良または最悪のケースはありません)
  • 最悪の場合のスマートデルタエンコーディングを使用した740 032ビット

ナイーブエンコーディングをベースラインとする場合、最大81.25%または最大15.625%のコスト削減を意味します。

値がどのように配分されるかに応じて、これらの節約は重要です。あなたのビジネスドメインを知ってください!何をエンコードしたいかを知ってください。

拡張機能として、バイアスを変更することもできます。データを分析して値のグループを識別する場合、データをバケットに分類し、それらのバケットをそれぞれ独自のバイアスで個別にエンコードできます。つまり、この手法は、1つの開始値を中心にグループ化されている範囲だけでなく、複数の値を中心にグループ化されている範囲にも適用できます。

開始点が均等に分散されている場合、このエンコーディングは実際にはうまく機能しません。

このエンコーディングは明らかにインデックス化に非常に悪いです。単にx番目の値を読み取ることはできません。それはほとんど順番に読むことができるだけです。これは、状況によっては適切です。ネットワークまたはバルクストレージ(テープやHDDなど)を介したストリーミング。

データの評価、グループ化、正しいバイアスの選択は、かなりの作業になる可能性があり、最適な結果を得るために微調整が必​​要になる場合があります。

17
Polygnome

この種の問題は、Claude Shannonの独創的な論文 Aの数学的理論 の主題であり、Wordの「ビット」を導入し、多かれ少なかれデータ圧縮を発明しました。

一般的な考え方は、範囲のエンコードに使用されるビット数は、その範囲が発生する確率に反比例するということです。たとえば、45〜74の範囲が時間の約1/4であるとします。シーケンス00は45-74に対応すると言えます。 45〜74の範囲をエンコードするには、「00」を出力してそこで停止します。

また、99〜100と140〜155の範囲がそれぞれ約1/8の時間で表示されるとします。それぞれを3ビットシーケンスでエンコードできます。 45〜74の範囲ですでに予約されている「00」で始まらない限り、どの3ビットでも機能します。

00: 45-74
010: 99-100
101: 140-155

すべての可能な範囲がエンコードされるまで、この方法で続行できます。確率が最も低い範囲では、100ビット以上が必要になる場合があります。しかし、ほとんど表示されないので問題ありません。

最適なコーディングを見つけるためのアルゴリズムareがあります。ここでは説明しませんが、詳細は上記のリンクにアクセスするか、「情報理論」、「シャノンファーノコーディング」、または「ハフマンコーディング」を検索してください。

他の人が指摘したように、開始番号と開始番号と終了番号の差を保存しておく方が良いでしょう。確率分布が異なるため、最初のコーディングと差異のコーディングを使用する必要があります(後者の方が冗長であると思います)。 polygnomeが示唆しているように、最適なアルゴリズムはドメインによって異なります。

8

@Glorfindelからの答えを拡張するには:

N→∞として、(n-1)→n。したがって、Ω(範囲)→n²/ 2およびlog(Ω(範囲))→(2n-1)。単純なエンコードでは2nビットを使用するため、漸近最大圧縮では1ビットしか節約できません。

1
Jared Goguen

同様の答えがありますが、最適な圧縮を実現するには、次のものが必要です。

  1. 最適なエントロピーエンコーディング方式( 算術符号化 を参照)と基本的に同等(同じ圧縮率、少し高速だが把握が難しい) [〜#〜] ans [〜#〜 ]
  2. データの分布について可能な限り多くの情報。重要なことに、これは1つの数値が出現する頻度を「推測」するだけでなく、特定の可能性を確実に除外できることがよくあります。たとえば、有効な間隔の定義方法に応じて、負のサイズ、および場合によっては0サイズの間隔を除外できます。一度にエンコードする間隔が複数ある場合は、それらを並べ替えることができます。幅の減少順、または開始/終了値の増加順、および多くの値を除外(たとえば、幅の減少により順序を保証する場合、前の間隔の幅は100で、次の間隔の開始値は47、最終値は147までの可能性を考慮する必要があるだけです。

重要なのは、2番は、最も有益な値(ビットごとのエンコード)が最初になるようにエンコードすることを意味します。たとえば、並べ替えられたリストを「そのまま」エンコードすることを提案しましたが、通常、「バイナリツリー」としてエンコードする方が賢明です。つまり、幅で並べ替えられており、len要素、要素len/2をエンコードすることから始めます。幅wあるとしましょう。これで、[0、w]のどこかに幅がある前のすべての要素と、[w、max val受け入れる]のどこかに幅がある後のすべての要素がわかりました。 len要素をカバーするまで、再帰的に繰り返します(各半分のリストを半分に分割するなど)(修正されていない限り、最初にlenをエンコードする必要があるため、終了トークンに煩わされる)。 「max val you accept」が本当に開かれている場合、最初に実際にデータに表示される最大値、つまり最後の要素をエンコードし、、次にバイナリパーティショニングを行います。繰り返しますが、最初にビットごとに最も有益なものは何でもです。

また、間隔の幅を最初にエンコードしていて、処理している可能な最大値がわかっている場合は、明らかに、オーバーフローするすべての開始値を除外することができます...デコード時に残りのデータについて可能な限り推測できるようにデータを変換および順序付けします。最適なエントロピーエンコードアルゴリズムにより、「既知の」ビットエンコード情報を無駄にしないようにします。 。

1
tohoho