X
2進数と形状の配列が(2, 2, ..., 2)
あり、インデックスの合計が2を法として0になり、残りのエントリに値0が割り当てられるすべてのエントリに値1を割り当てたいと思います。
たとえば、他の4つのエントリX.shape = (2, 2, 2)
に1を割り当てX[0, 0, 0], X[0, 1, 1], X[1, 0, 1], X[1, 1, 0]
、0を割り当てたいとします。
これを行う最も効率的な方法は何ですか?この配列はnp.boolデータ型で作成する必要があると思います。そのため、ソリューションはそれを念頭に置いて機能するはずです。
ここに直接法とトリッキーな方法があります。トリッキーなものはビットパッキングを使用し、特定の反復パターンを利用します。大規模な場合、n
これによりかなりのスピードアップが得られます(>50 @ n=19
)。
import functools as ft
import numpy as np
def direct(n):
I = np.arange(2, dtype='u1')
return ft.reduce(np.bitwise_xor, np.ix_(I[::-1], *(n-1)*(I,)))
def smartish(n):
assert n >= 6
b = np.empty(1<<(n-3), 'u1')
b[[0, 3, 5, 6]] = 0b10010110
b[[1, 2, 4, 7]] = 0b01101001
i = b.view('u8')
jp = 1
for j in range(0, n-7, 2):
i[3*jp:4*jp] = i[:jp]
i[jp:3*jp].reshape(2, -1)[...] = 0xffff_ffff_ffff_ffff ^ i[:jp]
jp *= 4
if n & 1:
i[jp:] = 0xffff_ffff_ffff_ffff ^ i[:jp]
return np.unpackbits(b).reshape(n*(2,))
from timeit import timeit
assert np.all(smartish(19) == direct(19))
print(f"direct {timeit(lambda: direct(19), number=100)*10:.3f} ms")
print(f"smartish {timeit(lambda: smartish(19), number=100)*10:.3f} ms")
2^19
ボックスでのサンプル実行:
direct 5.408 ms
smartish 0.079 ms
これらは次のようなuint8
配列を返すことに注意してください。
>>> direct(3)
array([[[1, 0],
[0, 1]],
[[0, 1],
[1, 0]]], dtype=uint8)
しかし、これらはbool
実質的にゼロコストでビューキャストすることができます。
>>> direct(3).view('?')
array([[[ True, False],
[False, True]],
[[False, True],
[ True, False]]])
説明者:
直接法:ビットパリティをチェックする簡単な方法の1つはxor
、ビットをまとめることです。これを「削減」する方法で行う必要があります。つまりxor
、最初の2つのオペランド、次に結果と3番目のオペランド、次にその結果と4番目のオペランドなどに2項演算を適用する必要があります。これが何をするかfunctools.reduce
です。
また、これを1回だけではなく、2^n
グリッドの各ポイントで実行します。これnumpy
を行う方法は、オープングリッドです。これらは、を使用して、np.ix_
または単純な場合はを使用して、1D軸から生成できますnp.ogrid
。反転パリティが必要であるという事実を説明するために、最初の軸を反転することに注意してください。
賢い方法。2つの主要な最適化を行います。1)xor
はビット単位の演算です。つまり、ビットを64ビットのuintにパックすると、「64ウェイ並列計算」が無料で実行されます。2)2 ^ n超立方体を平坦化すると、線形配置の位置nは、超立方体のセル(bit1、bit2、bit3、...)に対応します。ここで、bit1、bit2などはのバイナリ表現(先行ゼロ付き)です。 n。ここで、位置0 .. 0b11..11 = 2 ^ k-1のパリティを計算した場合、コピーして反転するだけで2 ^ k..2 ^(k + 1)-1のパリティを取得できることに注意してください。すでに計算されたパリティ。たとえば、k = 2:
0b000, 0b001, 0b010, 0b011 would be what we have and
0b100, 0b101, 0b110, 0b111 would be what we need to compute
^ ^ ^ ^
これらの2つのシーケンスはマークされたビットのみが異なるため、実際にそれらの桁間の合計が1つ異なり、パリティが反転していることは明らかです。
演習として、次の2 ^ kエントリとその後の2 ^ kエントリについて同様の流れで何が言えるかを考えます。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加