32


23

私は3D Morton数を計算するための迅速な方法を探しています。 このサイトには2D Mortonの番号に対してそれを行うためのマジックナンバーベースのトリックがありますが、それを拡張する方法が明らかではないようです3Dへ。

それで、基本的に私は3つの10ビット数を持っていて、それを最小の操作で1つの30ビット数にインターリーブしたいのです。

9 Answer


30


あなたは同じテクニックを使うことができます。 変数には32ビット整数が含まれており、最上位22ビットが「0」に設定されていると仮定しています(これは必要以上に制限されています)。 3つの10ビット整数の1つを含む各変数 `+ x +`に対して、次のことを行います。

x = (x | (x << 16)) & 0x030000FF;
x = (x | (x <<  8)) & 0x0300F00F;
x = (x | (x <<  4)) & 0x030C30C3;
x = (x | (x <<  2)) & 0x09249249;

次に、3つの操作された10ビット整数「+ x 」、「 y 」、および「 z +」を使用して、次のようにして結果を取得します。

x | (y << 1) | (z << 2)

この手法のしくみは次のとおりです。 `+ x = …​ +`行のそれぞれは、ビットのグループを半分に「分割」し、他の整数のビットのために十分なスペースがあるようにします。 たとえば、3つの4ビット整数を検討する場合、ビット1234の整数を000012000034に分割します。ここで、ゼロは他の整数用に予約されています。 次のステップで、12と34を同じように分割して001002003004を得ます。 10ビットでは2つのグループに分割してもうまくいきませんが、最後に最も高いものを失った16ビットと考えることができます。

最初の行からわかるように、実際には、各入力整数 `+ x `に対して、 ` x&0x03000000 == 0 +`が保持されていることだけが必要です。


15


  • Pythonスクリプトを使った私の解決策は次のとおりです。*

私は彼のコメントからヒントを得ました。 どのビットがどのくらい遠くまで行く必要があるかを追跡する必要があります。 その後、各ステップでこれらのビットを選択して移動し、ビットマスク(最後の行のコメントを参照)を適用してそれらをマスクします。

ビット距離:[0、2、4、6、8、10、12、14、16、18]ビット距離(2進数):['0'、 '10'、 '100'、 '110'、 '1000' 、 '1010'、 '1100'、 '1110'、 '10000'、 '10010'] idxビットのビット数を1ずつシフト:[] idxビットのビット数を2ずつシフト:[1、3、5、7、9]ビットidxの場合の4ビットシフト:[2、3、6、7]ビットidxの場合の8ビットシフト:[4、5、6、7]ビットidxの場合、16ビットシフト:[8、9]ビット位置:[ 0、1、2、3、4、5、6、7、8、9]シフトされたbef。:0000 0000 0000 0000 0000 0011 0000 16進数:0x300シフトされた:0000 0011 0000 0000 0000 0000 0000 0000 16進数:0x3000000 0000 0000 0000 0000 0000 1111 1111 16進数:0xffビットマスクは次のとおりです。0000 0011 0000 0000 0000 0000 1111 1111 16進数:0x30000ff

シフトされたbef。:0000 0000 0000 0000 0000 1111 0000 16進数:0xf0シフトされた:0000 0000 0000 0000 1111 0000 0000 0000 0000 0000 0000 0000 0000 0000 1111 16進数:0x300000fビットマスクは現在0000 0011 0000 0011 0000 0000 1111 16進数:0x300f00f

シフトした値:0000 0000 0000 0000 1100 0000 0000 1100 16進数:0xc00cシフト:0000 0000 0000 1100 0000 1100 0000 16進数:0000 0011 0000 0000 0011 0000 0000 0011 0000 0000 0011 16進数:0x3003003ビットマスクは現在0000 0011 0000 1100 0011 0000 1100 0011 16進数:0x30c30c3

シフトした値:0000 0010 0000 1000 0010 0000 1000 0010 16進数:0x2082082シフトした:0000 1000 0010 0000 1000 0010 0000 1000 16進数:0000 0001 0000 0100 0001 0000 0100 0001 16進数:0x1041041ビットマスクは現在0000 1001 0010 0100 1001 0010 0100 1001 16進数:0x9249249

バツ
  • 10ビットの数と2つのインターリーブビット(32ビットの場合)では、次のことを行う必要があります。

x &= 0x3ff
x = (x | x << 16) & 0x30000ff   #<<< THIS IS THE MASK for shifting 16 (for bit 8 and 9)
x = (x | x << 8) & 0x300f00f
x = (x | x << 4) & 0x30c30c3
x = (x | x << 2) & 0x9249249
  • 21ビットの数と2つのインターリーブビット(64ビットの場合)では、次のことを行う必要があります。

x &= 0x1fffff
x = (x | x << 32) & 0x1f00000000ffff
x = (x | x << 16) & 0x1f0000ff0000ff
x = (x | x << 8) & 0x100f00f00f00f00f
x = (x | x << 4) & 0x10c30c30c30c30c3
x = (x | x << 2) & 0x1249249249249249
  • 42ビット数と2インターリーブビット(128ビット用)の場合は、次の手順を実行する必要があります(必要な場合は;-))。*

x &= 0x3ffffffffff
x = (x | x << 64) & 0x3ff0000000000000000ffffffffL
x = (x | x << 32) & 0x3ff00000000ffff00000000ffffL
x = (x | x << 16) & 0x30000ff0000ff0000ff0000ff0000ffL
x = (x | x << 8) & 0x300f00f00f00f00f00f00f00f00f00fL
x = (x | x << 4) & 0x30c30c30c30c30c30c30c30c30c30c3L
x = (x | x << 2) & 0x9249249249249249249249249249249L

インターリーブパターンを生成してチェックするためのPythonスクリプト!!! *

def prettyBinString(x,d=32,steps=4,sep=".",emptyChar="0"):
    b = bin(x)[2:]
    zeros = d - len(b)

    if zeros <= 0:
        zeros = 0
        k = steps - (len(b) % steps)
    else:
        k = steps - (d % steps)

    s = ""
    #print("zeros" , zeros)
    #print("k" , k)
    for i in range(zeros):
        #print("k:",k)
        if(k%steps==0 and i!= 0):
            s+=sep
        s += emptyChar
        k+=1

    for i in range(len(b)):
        if( (k%steps==0 and i!=0 and zeros == 0) or  (k%steps==0 and zeros != 0) ):
            s+=sep
        s += b[i]
        k+=1
    return s

def binStr(x): return prettyBinString(x,32,4," ","0")

def computeBitMaskPatternAndCode(numberOfBits, numberOfEmptyBits):
    bitDistances=[ i*numberOfEmptyBits for i in range(numberOfBits) ]
    print("Bit Distances: " + str(bitDistances))
    bitDistancesB = [bin(dist)[2:] for dist in  bitDistances]
    print("Bit Distances (binary): " + str(bitDistancesB))
    moveBits=[] #Liste mit allen Bits welche aufsteigend um 2, 4,8,16,32,64,128 stellen geschoben werden müssen

    maxLength = len(max(bitDistancesB, key=len))
    abort = False
    for i in range(maxLength):
        moveBits.append([])
        for idx,bits in enumerate(bitDistancesB):
            if not len(bits) - 1 < i:
                if(bits[len(bits)-i-1] == "1"):
                    moveBits[i].append(idx)

    for i in range(len(moveBits)):
        print("Shifting bits by " + str(2**i) + "\t for bits idx: " + str(moveBits[i]))

    bitPositions = range(numberOfBits);
    print("BitPositions: " + str(bitPositions))
    maskOld = (1 << numberOfBits) -1

    codeString = "x &= " + hex(maskOld) + "\n"
    for idx in xrange(len(moveBits)-1, -1, -1):
        if len(moveBits[idx]):

           shifted = 0
           for bitIdxToMove in moveBits[idx]:
                shifted |= 1<

デコーディングコードもしばらくして追加します。


8


4Kの空き容量がある場合、最も簡単なのはおそらくルックアップテーブルです。

static uint32_t t [1024] = {0、0x1、0x8、... ;

uint32_t m(int a、int b、int c){戻り値t [a] | (t [b] << 1)| (t [c] << 2); }

ビットハックはシフトとマスクを使用してビットを拡散するため、値をシフトするたびに、または一部のビットを空のスペースにコピーしてから、元のビットだけが残るように組み合わせをマスクします。

例えば:

x = 0xabcd。 = 0000_0000_0000_0000_1010_1011_1100_1101

x =(x |(x << S [3]))

=(0x00abcd00 | 0x0000abcd)

各反復で、各ブロックは2つに分割され、ブロックの左端の半分の右端のビットが最終位置に移動され、必要なビットだけが残るようにマスクが適用されます。

入力の間隔を空けたら、一方の値がもう一方の値のゼロになるようにシフトします。

最終的な結果の値の間に2ビット以上のそのテクニックを拡張するためには、ビットが終わるところの間のシフトを増やす必要があります。 開始ブロックのサイズは2のべき乗ではないため、少しトリッキーになります。したがって、中央で分割するか、2の累乗境界で分割することができます。

だからこのような進化はうまくいくかもしれない:

0000_0000_0000_0000_0000_0011_1111_1111 0000_0011_0000_0000_0000_0000_1111_1111 0000_0011_0000_0000_1111_0000_1111 0000_0011_0000_1100_0011_0000_1100_0011 0000_1001_0010_10011100_1001

// 0000_0000_0000_0000_0000_0011_1111_1111
x =(x |(x << 16))
// 0000_0011_0000_0000_0000_0000_1111_1111
x =(x |(x << 8))
// 0000_0011_0000_0000_1111_0000_0000_1111
x =(x |(x << 4))
// 0000_0011_0000_1100_0011_0000_1100_0011
x =(x |(x << 2))
// 0000_1001_0010_0100_1001_0010_0100_1001

入力に対して同じ変換を実行するか、1つずつおよび2つずつずらすか、またはそれらを一緒に移動すれば完了です。


5


良いタイミング、私は先月これをやった!

キーは2つの機能を作ることでした。 1つは3ビットごとにビットを広げます。 それから、それらのうちの3つを(最後の2つのシフトで)組み合わせて、最終のMortonインターリーブ値を得ることができます。

このコードはHIGHビットから始まるインターリーブです(固定小数点値のほうが論理的です)。アプリケーションがコンポーネントあたり10ビットしかない場合は、各ビットを22だけ左にシフトして上位ビットから始めます。

/ *値を受け取り、HIGHビットを下位スロットに「拡散」してそれらを分離します。 つまり、ビット31はビット31のまま、ビット30はビット28、ビット29はビット25になります。 ビット21より下のものはすべて消えます。 Mortonコードの値をインターリーブするのに便利です。 * /インライン符号なしロングスプレッド3(符号なしロングx){x =(0xF0000000)

インライン符号なしlong morton(符号なしlong x、符号なしlong y、符号なしlong z){return spread3(x)| (spread3(y)>> 1)| (spread3(z)>> 2); }


4


次のコードは、3つの10ビット入力数のモートン数を求めます。 リンクからのアイデアを使用し、ステップ5-5、3-2-3-2、2-1-1-1-2-1-1-1、および1-1-1-でビット拡散を実行します。なぜなら、10は2のべき乗ではないからです。

......................9876543210
............98765..........43210
........987....56......432....10
......98..7..5..6....43..2..1..0
....9..8..7..5..6..4..3..2..1..0

上記の4つのステップのそれぞれの前と後のすべてのビットの位置を確認できます。

public static Int32 GetMortonNumber(Int32 x、Int32 y、Int32 z){return SpreadBits(x、0)| 0} SpreadBits(y、1)| SpreadBits(z、2); }

public static Int32 SpreadBits(Int32 x、Int32 offset){if((x <0)||(x> 1023)){新しいArgumentOutOfRangeException()をスローします。 }

if((offset <0)||(offset> 2)){新しいArgumentOutOfRangeException()をスローします。 }

x =(x |(x << 10))

x << offsetを返します。 }


4


私は上記を取り、それを修正して3つの16ビット数を48ビット(実際には64ビット)の数にまとめました。 おそらくそれは、誰かがそこにたどり着くことを考えるのを少し節約するでしょう。

#include #include uint64_t zorder3d(uint64_t x、uint64_t y、uint64_t z){static const uint64_t B [] = {0x00000000FF0000FF、0x000000F00F00F00F、0x00000C30C30C30C3、0X0000249249249249}; static const int S [] = {16、8、4、2};静的定数uint64_t MAXINPUT = 65536。

assert(((x <MAXINPUT))

x =(x |(x << S [0]))

y =(y |(y << S [0]))

z =(z |(z << S [0]))

(x |(y << 1)|(z << 2))を返す。 }


2


以下は、3Dポイント用にサイズ64ビットのMortonキーを生成するためのコードスニペットです。

名前空間stdを使用する。

符号なしlong longスプレッドビット(符号なしlong long x){x =(x |(x << 20))

int main(){unsigned long long x、y、z、con = 1; con = con << 63; printf( "%#llx \ n"、(spreadBits(x)|(spreadBits(y)<< 1)|(spreadBits(z)<< 2))| con); }


2


私は今日同様の問題を抱えていました、しかし3つの数の代わりに、私は任意のビット長の任意の数の数を組み合わせる必要があります。 私は自分の種類のビット拡散およびマスキングアルゴリズムを採用し、それをC#BigIntegersに適用しました。 これが私が書いたコードです。 コンパイルステップとして、与えられた次元数とビット深度に対するマジックナンバーとマスクを計算します。 その後、オブジェクトを複数の変換に再利用できます。

///
/// Convert an array of integers into a Morton code by interleaving the bits.
/// Create one Morton object for a given pair of Dimension and BitDepth and reuse if when encoding multiple
/// Morton numbers.
///
public class Morton {/// ///インターリーブされている各数値を表すために使用するビット数。 /// public int BitDepth {get;}プライベートセット}

/// /// Morton番号にインターリーブするための個別の番号の数。 /// public int Dimensions {get;};プライベートセット}

/// /// MagicNumbersは正しい位置にビットを広げます。 ///マジックナンバーを1つだけ使用した場合はビットが重なるため、それぞれを適用してマスクする必要があります。 /// public BigInteger LargeMagicNumber {get;プライベートセットpublic BigInteger SmallMagicNumber {get;}プライベートセット}

/// ///マスクは、他の次元で必要とされる位置に広がった余分なビットを取り除きます。 /// public BigInteger Mask {get;プライベートセット}

public Morton(intディメンション、int bitDepth){BitDepth = bitDepth;寸法=寸法。 BigInteger magicNumberUnit = new BigInteger(1UL <<(int)(寸法 -  1)); LargeMagicNumber = magicNumberUnit; BigInteger maskUnit = new BigInteger(1UL <<(int)(寸法 -  1)); Mask = maskUnit; (var i = 0; i <bitDepth  -  1; i){LargeMagicNumber =(LargeMagicNumber <<(Dimensions  -  1))|の場合(i%2 == 1? magicNumberUnit:BigInteger.Zero);マスク=(マスク<<寸法)| maskUnit; SmallMagicNumber =(LargeMagicNumber >> BitDepth)<< 1; //厄介なものを少し削除する必要があります。 }

/// ///複数の整数のビットを1つのBigIntegerにインターリーブします。 ///最初の番号の上位ビットがMorton番号の上位ビットになります。 /// 2番目の数値の上位ビットは、Morton番号の2番目に上位のビットになります。 ///      /// 使い方。 /// ///あなたがマジックナンバーであなたがそれらを掛け算している数の複数のコピーを作るとき、あなたはそれぞれ異なる量だけシフトした。 ///結局のところ、ある数値の最上位コピーの上位ビットは、2番目のコピーの2番目のビットの///左側のNビットです。 ///これは、各コピーがコピー数のN倍より1ビット少なくシフトされるためです。 ///その後、ANDマスクを適用して、位置が正しくないすべてのビットを設定解除します。 /// /// 2つのマジックナンバーが必要です。なぜなら、各コピーはbitDepthよりも1つ少なくシフトされるので、連続する///コピーが重なり合ってアルゴリズムを台無しにするからです。 したがって、1つのマジックナンバー(LargeMagicNumber)はコピー1、3、5などを処理し、///秒(SmallMagicNumber)はコピー2、4、6などを処理します。 /// ///結合する整数。 /// Dimensions * BitDepthビットで構成されるMorton数。 public BigInteger Interleave(int [] vector){if(vector == null || vector.Length!= Dimensions)は、新しいArgumentExceptionをスローします( "Interleaveは長さ" Dimensions、 "vector"の配列を期待します)。 var morton = BigInteger.Zero; (var i = 0; i <次元; i){morton | =(((LargeMagicNumber * vector [i]))

パブリックオーバーライド文字列ToString(){return "Morton(Dimension:" Dimensions "、BitDepth:" BitDepth)
            + ", MagicNumbers: " + Convert.ToString((long)LargeMagicNumber, 2) + ", " + Convert.ToString((long)SmallMagicNumber, 2)
            + ", Mask: " + Convert.ToString((long)Mask, 2) + ")";
}}


1


これは私がRubyで作った任意の長さのエンコーディングメソッドを作るためのジェネレータです。

def morton_code_for(bits)method = ''

limit_mask =(1 <<(ビット* 3)) -  1 split =(2 **((Math.log(ビット)/ Math.log(2))。to_i 1))。to_i level = 1

「// 3#{bits} -bit値のコーディング」

ループシフトシフト=分割分割/ = 2レベル* = 2

mask =(['1' * split] * level).join( '0' * split * 2).to_i(2)

expression = "v =(v |(v <<%2d))

メソッド<<式

"%s // 0b%064b"%を置きます[式、マスク]

(split <= 1)終了時にブレーク

//メソッド結果のテスト: "v =(1 << bits) -  1はeval(method).to_s(2)を書きます。end

morton_code_for(21)

出力は適切には一般的なものであり、必要に応じて適合させることができる。 出力例:

// Coding for 3 21-bit values
v =(v |(v << 32))

// Test of method results: 1001001001001001001001001001001001001001001001001001001001001