私はついに故障し、いくらかのお金を払いました。AIP (American Institute of Physics) には、C のソース コードを含むすばらしい短い記事があります。John Skilling による「ヒルベルト曲線のプログラミング」(AIP Conf. Proc. 707, 381 (2004) から) には、次のコードを含む付録があります。双方向のマッピング。1 を超える任意の数の次元に対して機能し、再帰的ではなく、大量のメモリを消費する状態遷移ルックアップ テーブルを使用せず、主にビット操作を使用します。したがって、適度に高速で、メモリ フットプリントが良好です。
記事を購入することを選択した場合、ソース コードにエラーが見つかりました。
次のコード行 (関数 TransposetoAxes にあります) にはエラーがあります。
for( i = n-1; i >= 0; i-- ) X[i] ^= X[i-1];
修正は、より大きいか等しい (>=) をより大きい (>) に変更することです。この修正を行わないと、変数「i」がゼロになると X 配列が負のインデックスを使用してアクセスされ、プログラムが失敗します。
この記事 (コードを含めて 7 ページの長さ) を読むことをお勧めします。この記事では、アルゴリズムがどのように機能するかが説明されていますが、これは明らかではありません。
私は彼のコードを自分で使用するために C# に翻訳しました。コードは次のとおりです。Skilling はその場で変換を実行し、渡されたベクトルを上書きします。入力ベクトルのクローンを作成し、新しいコピーを返すことにしました。また、メソッドを拡張メソッドとして実装しました。
Skilling のコードは、ヒルベルト インデックスを転置として表し、配列として格納します。ビットをインターリーブして単一の BigInteger を形成する方が便利だと思います (辞書でより便利で、ループでの反復処理が簡単など) が、その操作とその逆をマジック ナンバー、ビット操作などで最適化し、コードは長いので割愛します。
namespace HilbertExtensions
{
/// <summary>
/// Convert between Hilbert index and N-dimensional points.
///
/// The Hilbert index is expressed as an array of transposed bits.
///
/// Example: 5 bits for each of n=3 coordinates.
/// 15-bit Hilbert integer = A B C D E F G H I J K L M N O is stored
/// as its Transpose ^
/// X[0] = A D G J M X[2]| 7
/// X[1] = B E H K N <-------> | /X[1]
/// X[2] = C F I L O axes |/
/// high low 0------> X[0]
///
/// NOTE: This algorithm is derived from work done by John Skilling and published in "Programming the Hilbert curve".
/// (c) 2004 American Institute of Physics.
///
/// </summary>
public static class HilbertCurveTransform
{
/// <summary>
/// Convert the Hilbert index into an N-dimensional point expressed as a vector of uints.
///
/// Note: In Skilling's paper, this function is named TransposetoAxes.
/// </summary>
/// <param name="transposedIndex">The Hilbert index stored in transposed form.</param>
/// <param name="bits">Number of bits per coordinate.</param>
/// <returns>Coordinate vector.</returns>
public static uint[] HilbertAxes(this uint[] transposedIndex, int bits)
{
var X = (uint[])transposedIndex.Clone();
int n = X.Length; // n: Number of dimensions
uint N = 2U << (bits - 1), P, Q, t;
int i;
// Gray decode by H ^ (H/2)
t = X[n - 1] >> 1;
// Corrected error in Skilling's paper on the following line. The appendix had i >= 0 leading to negative array index.
for (i = n - 1; i > 0; i--)
X[i] ^= X[i - 1];
X[0] ^= t;
// Undo excess work
for (Q = 2; Q != N; Q <<= 1)
{
P = Q - 1;
for (i = n - 1; i >= 0; i--)
if ((X[i] & Q) != 0U)
X[0] ^= P; // invert
else
{
t = (X[0] ^ X[i]) & P;
X[0] ^= t;
X[i] ^= t;
}
} // exchange
return X;
}
/// <summary>
/// Given the axes (coordinates) of a point in N-Dimensional space, find the distance to that point along the Hilbert curve.
/// That distance will be transposed; broken into pieces and distributed into an array.
///
/// The number of dimensions is the length of the hilbertAxes array.
///
/// Note: In Skilling's paper, this function is called AxestoTranspose.
/// </summary>
/// <param name="hilbertAxes">Point in N-space.</param>
/// <param name="bits">Depth of the Hilbert curve. If bits is one, this is the top-level Hilbert curve.</param>
/// <returns>The Hilbert distance (or index) as a transposed Hilbert index.</returns>
public static uint[] HilbertIndexTransposed(this uint[] hilbertAxes, int bits)
{
var X = (uint[])hilbertAxes.Clone();
var n = hilbertAxes.Length; // n: Number of dimensions
uint M = 1U << (bits - 1), P, Q, t;
int i;
// Inverse undo
for (Q = M; Q > 1; Q >>= 1)
{
P = Q - 1;
for (i = 0; i < n; i++)
if ((X[i] & Q) != 0)
X[0] ^= P; // invert
else
{
t = (X[0] ^ X[i]) & P;
X[0] ^= t;
X[i] ^= t;
}
} // exchange
// Gray encode
for (i = 1; i < n; i++)
X[i] ^= X[i - 1];
t = 0;
for (Q = M; Q > 1; Q >>= 1)
if ((X[n - 1] & Q)!=0)
t ^= Q - 1;
for (i = 0; i < n; i++)
X[i] ^= t;
return X;
}
}
}
C# で動作するコードを github に投稿しました。
https://github.com/paulchernoch/HilbertTransformationを参照してください。
更新: 私はちょうど (2019 年秋) crates.io で "hilbert" と呼ばれる Rust クレートを公開しました。また、Skilling のアルゴリズムも使用します。https://crates.io/crates/hilbertを参照