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


29

この質問は、範囲を格納するために必要なビット数に関するものです。または、別の言い方をすれば、指定されたビット数に対して、保存できる最大範囲とその方法は何ですか?

0〜255の範囲内のサブ範囲を保存することを想像してください。

たとえば、45-74。

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

どんな圧縮技術でも限界的な結果を生むと思うので、「1バイトに保存できる最大範囲はどれくらいですか?」と尋ねる方が良い質問かもしれません。これは、2つの数値を別々に保存することで達成可能な値よりも大きくする必要があります。

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


範囲の開始も保存する必要がありますか?
ユアン

@ユアン私は本当にフォローしていません。上記の例では、45が開始(最小)、74が終了(最大)であり、両方を保存する必要があります。
rghome

2
範囲を格納できる型がどれだけのスペースを必要とするかという問題も同様です。または、45〜74を格納できるタイプにはどのくらいのスペースが必要ですか?
ユアン

1
これについては確かに良いことですが、実際のアプリケーションではこれを行わないことを期待しています。その理由は、実際のアプリケーションの複雑さが非常に大きいため、100%未満の最適化コードを受け入れる必要があるためです。...それがコンパイラが存在した理由です。
NoChance

3
@rghome、同意する、最も単純な要件でも数百行のコードを生成します。それぞれエラーが発生しやすいです。個人的には、ソフトウェアの複雑さを増すよりもハードウェアにお金を払うでしょう。
NoChance

回答:


58

可能な範囲の数を数えるだけです。下限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の可能性を与えます。したがって、0〜21のサブ範囲には1バイトで十分です。

問題を見る別の方法は次のとおりです:0からn-1の範囲の整数のペアを格納することは、0-(n-1)の部分範囲と最初の数を決定する単一ビットを格納することとほぼ同じです。 2番目のものよりも低いか高い。(違いは両方の整数が等しい場合から生じますが、nが大きくなるにつれてこの可能性はますます小さくなります。)それが、この手法で1ビットしか節約できない理由であり、おそらくほとんど使用されない主な理由です。


ありがとう。nの範囲に必要なビット数はlog(n)/ log2です。すべてをWolfram Alphaに入力すると、指定されたビット数の部分範囲の最大値を計算するための次のExcel互換式が得られました:= INT((SQRT(POWER(2、N + 3)+ 1)-1)/ 2 )
rghome

9
TLDRを使用すると、約半分のビットが得られるため、一般的に圧縮する価値はありません。
rghome

ええ、Nが大きい場合は1ビットになる傾向がありますが、面倒な価値はありません。
Glorfindel

参考までに、方程式のN + 3は奇妙に見えますが、2のべき乗の1つは方程式から得られ、残りの2つは2次式の4ac部分から得られます。
rghome

1
ところで、あなたのカウントは、すべての非カウントの組み合わせが立っている空の範囲を割引きます。だからn * (n + 1) / 2 + 1!わずかな変更。
デデュプリケーター

17

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 * 10000 = 640 000ビットが必要になります。

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

これは、エンコードに32ビットを要するドメインの10000個の値に対して、

  • 最良の場合のスマートデルタエンコーディングを使用した120 032ビット
  • ナイーブスタート、エンドエンコーディング、常に64 000ビット(ベストまたはワーストケースなし)
  • 最悪の場合のスマートデルタエンコーディングを使用した740 032ビット

単純なエンコーディングをベースラインとして使用する場合、最大81.25%のコスト削減、または最大15.625%のコスト削減が可能です。

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

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

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

このエンコードは、明らかにインデックス付けが非常に悪いです。x番目の値を単に読み取ることはできません。ほとんど順番にしか読み取れません。これは、ネットワークまたは大容量記憶装置(テープまたはHDDなど)を介したストリーミングなど、状況によっては適切です。

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


8

この種の問題は、Claude Shannonの論文「A Mathematical Theory of Communication」の主題であり、「ビット」という言葉を導入し、データ圧縮を多かれ少なかれ考案しました。

一般的な考え方は、範囲をエンコードするために使用されるビット数は、その範囲が発生する確率に反比例するということです。たとえば、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ビット以上が必要な場合があります。しかし、めったに表示されないので大丈夫です。

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

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


1
ええ、ビジネスドメインは本当に重要です。実際に開始日のバイアスにハフマンコーディングを使用することを検討しましたが、実際のデータで統計分析を実行した後、最終的にはハフマンコーディングに反対しました。バイアスとデルタに同じエンコーディングを使用することのシンプルさは、ハフマンを上に追加するよりも重要でした。さらに、ハフマンツリー全体も送信する必要があります。ただし、ハフマンコーディングを念頭に置くことをお勧めします。
ポリグノーム

1

@Glorfindelからの回答を展開するには:

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


1

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

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

重要なのは、数値2は、最も情報価値のある値(エンコードされたビットごと)が最初に来るように物事をエンコードすることを意味します。たとえば、ソートされたリストを「そのまま」エンコードすることを提案しましたが、通常は「バイナリツリー」としてエンコードする方が賢明です。つまり、幅でソートされ、len要素がある場合は、エンコード要素から始めますlen/2。幅がwだったとします。これで、[0、w]のどこかに幅がある前のすべての要素、および[w、max val you accept]のどこかに幅があるすべての要素がわかりました。len要素をカバーするまで再帰的に繰り返します(各半分のリストを半分に再分割するなど)(修正されない限り、エンコードする必要があります)len最初に、終了トークンに煩わされる必要はありません)。「あなたが受け入れる最大valが」本当に開いている場合、それはつまり、最後の要素ということは、実際にあなたのデータに表示される最高値、最初のエンコードにスマートであってもよく、その後、バイナリ分割を行います。繰り返しになりますが、ビットごとに最も情報量の多いものが最初になります。

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.