前回の記事では、IEEE浮動小数点数を置き換えることができる一連のコーディング方式を提案することを目的として、IEEE浮動小数点数の設計上の欠陥を情報理論の観点から明らかにしました:精度反転アルゴリズム。しかし、最初に、アルゴリズムの基礎であるVLQコーディングを理解する必要があります。
Base127 VLQ:可変長物理量
VLQは可変長の数量、つまり可変長の数量を指します。この数量は任意の情報の数量にすることができます。大企業はネーミングに非常に特別であり、一般に名詞自体の使用をバイパスし、PWAなどのより抽象的な意味を引用したい:プログレッシブWebアプリケーション、非常に高いように見えるが、実際にはローカルにインストールできるセットWebアプリケーションのAPI。
同じことがVLQにも当てはまります。元々、VLQは整数をエンコードするためにのみ使用されていました。絶対値が小さい整数ほど、小さなスペースを占めることが期待されていました。その後、VLQによって物理量をエンコードできることが発見されたため、この名前が付けられました。
この命名規則は科学的根拠があると言われています。いわゆる「業界の障壁」とは、インサイダーが常に無意識のうちにアウトサイダーの学習しきい値を上げるという知識を指します。個人は無意識の自己利益行動ですが、グループ行動の結果は業界の障壁の増加。もともと非常に単純な名詞は「ミステリー化」されます。
VLQは、7ビットグループに基づく可変長エンコーディングです。それ自体は非常に単純です。各バイトの最初のビットを使用して、後続のバイトがあるかどうかを示します。
0XXXXXXX
1XXXXXXX 0XXXXXXX
1XXXXXXX 1XXXXXXX 0XXXXXXX
……
これの利点は、オブジェクトの長さをプレフィックスに書き込む必要がなく、各バイトの最後の「0」が「残り」を表すことです。これは、情報理論のもう1つの重要な概念です。
たとえば、10進数の自然数106,903をVLQバイト文字列に変換する概略図は上記のようになります。106903= 6 * 2 ^ 14 + 67 * 2 ^ 7 + 23で、これは単純明快です。
終了信号をスキャンする2つのモード:プレフィックスVSレスト
スキャナー(デコーダー)がシリアル化されたデータの一部を左から右にスキャンするとき、特定の「サブエレメント/オブジェクト/文字/値」までスキャンするとき、それが終了することが重要なポイントである場合、通常2つの方法があります。いつ停止するかを示唆する。
接頭辞:子要素の長さを接頭辞に格納します。
休憩:末尾の「休憩」は、スキャナーを促すために使用され、終了文字または終了バイトにすることができます。
プレフィックスに長さを書き込む前者の方法は、多くのIPサブプロトコルやバイナリシリアル化形式などのバイナリプロトコル形式で非常に一般的です。「レスト」で終了する後者の方法は、大規模なテキスト形式や古代のテキストベースの通信プロトコルでは、DNAの解読でさえ、ペプチド鎖を分離するために「ターミネーター」を使用していました。
プレフィックスと比較した休符の利点は柔軟性です。そのため、文字列のEOF終了文字など、長さの上限を気にする必要はありません。スキャナーがEOFに触れない限り、スキャンは続行されます。もちろん、VLQは「レストタイプ」に属しています。
VLQオフセット自然数(冗長性のキャンセルとキャンセル)
前の記事で述べたコーディングの2つの基本原則、「あいまいさなし」と「冗長性なし」です。VLQを使用して自然数を表す場合、1バイト(0〜127)で表すことができる数値が2バイト(0〜16383)で表すことができるという状況が発生します。nバイトのVLQはn-1バイトのVLQと互換性があります。1バイトのVLQが0〜127の自然数を表す場合、2バイトは最初は128から数えます。
マルチバイトVLQ自然数の実際の値は、その面の値にオフセット値を加えたものに等しく、これは前のレベルの最大バイト数に1を加えたものに等しく、このレベルの最小値です。
オフセットの理由は、自然状態では、異なる実数の長さが実数スペースの一部を共有するためです。たとえば、3バイトの実数には2バイトのスペース全体が含まれます。たとえば、00 00 01と00 01は両方とも1です。
したがって、各長さの実数の実際の値を、より短い長さのすべてのスペースの合計に追加する必要があります。たとえば、00 01は1を表し、00 00 01は257(255 + 2)を表します。
異なるバイト数のVLQ整数と対応する実際の値には、次の関係があります。
バイト数 |
整数空間 |
分 |
最高 |
---|---|---|---|
1 | 2 ^ 7 | 0 | -1 + 2 ^ 7 |
2 | 2 ^ 14 | 2 ^ 7 | -1 + 2 ^ 7 + 2 ^ 14 |
3 | 2 ^ 21 | 2 ^ 7 + 2 ^ 14 | -1 + 2 ^ 7 + 2 ^ 14 + 2 ^ 21 |
ん | 2 ^ 7n | 2 ^ 7 + 2 ^ 14 + ... + 2 ^ 7(n-1) | -1 + 2 ^ 7 + 2 ^ 14 + ... + 2 ^ 7n |
それらの中で、各minは前の行のmax + 1に等しい。
minは、この整数空間のいくつかの7ビットグループの「すべて0」の意味を表し、maxは、この整数空間の「すべて1」の意味を表します。
バイナリの金種とVLQの実際の値の間のマッピング関係:
VLQ |
自然数 |
0000 0000 |
0 |
…… |
|
0111 1111 |
127 |
1000 0000 0000 0000 |
128 |
…… |
|
1111 1111 0111 1111 | 16511 |
1000 0000 1000 0000 0000 0000 | 16512 |
…… |
ある1対1のマッピング(全単射)が、それはスペース効率の面で延長を実現するだけでなく、任意のスペースを無駄にしないだけでなく、独特の自然数、に解析することができ、バイトの文字列がランダムに取られても、。これが「精度反転アルゴリズム」の基礎です。VLQオフセット自然数。VLQ自然数と呼ばれます。VLQと自然数の相互変換機能も生まれました。
VLQオフセット自然数は私のオリジナルの作成ではないことに注意してください(私はそれが私のオリジナルの作成であると思ったが、それについて考えた後、私はそれほど賢くなかったので、他のものを考えることができます。)検索した後、Gitはこのアルゴリズムを既に実装していることがわかりました。彼はまた、彼に特別な名前を付けました:全単射記数法は1対1のマッピングを意味します。
全単射VLQのコード実現
const r7 = 2 ** 7;
const r14 = 2 ** 14;
const r21 = 2 ** 21;
const r28 = 2 ** 28;
const r35 = 2 ** 35;
const r42 = 2 ** 42;
const R7 = r7;
const R14 = r14 + r7;
const R21 = r21 + r14 + r7;
const R28 = r28 + r21 + r14 + r7;
const R35 = r35 + r28 + r21 + r14 + r7;
const R42 = r42 + r35 + r28 + r21 + r14 + r7;
const r = [1, r7, r14, r21, r28, r35, r42];
const R = [0, R7, R14, R21, R28, R35, R42];
一部の定数は、以下で使用するために時間とスペースを交換するために、上記で事前に計算されています。
自然数をVLQバイト文字列に変換する関数:
function nature2vlq(number) {
let tobeUint8Array;
R.find((RR, index) => {
if (number < RR) {
const faceValue = number - R[index - 1];
tobeUint8Array = Array.from({ length: index })
.map((x, i) => (faceValue / r[i]) % r7 | (i ? r7 : 0))
.reverse();
return true;
} else return false;
});
return new Uint8Array(tobeUint8Array);
}
VLQバイト文字列を自然数に変換する関数:
function vlq2nature(vlq) {
return (
vlq
.map((x) => x & (r7 - 1))
.reduce((sum, next, i) => {
sum += next * r[vlq.length - i - 1];
return sum;
}, 0) + R[vlq.length - 1]
);
}
VLQオフセット自然数では、2の補数やジグザグなどの従来の整数コーディングを組み合わせ、自然数としてVLQにマッピングする限り、可変長整数コーディングは当然のことです。
写真:「リック&モーティ」シーズン4