私がこれまでに得た最良のアイデアは、最初の配列を{0、90、180、270}度回転させ、それを水平または垂直に反映させることです。基本的に、最初の配列の16のバリエーション[1]を取得し、それらを2番目の配列と比較します。それらのどれもが一致しない場合、2つの配列は回転的かつ反射的に区別されます。
このブルートフォースアプローチよりも最適なソリューションはあるのでしょうか。
[1]
0度、反射なし 0度、x上で反射 0度、y上で反射 0度、xとyで反射 90度、反射なし ...
これを2つの問題に分解します。
素朴なアプローチで最初の問題を実装すると、非常に簡単な答えが得られます。行列を並行して歩き、等しくない値が見つかったら停止します。等しくない値なしで行列全体を歩くと、行列は等しくなります。行列をウォークするには、列をループし、各行を内部ループして、値を比較します。マトリックスをウォークすると、単一の線形コレクションであるかのように、マトリックス内のすべてのアイテムを列挙できることに注意してください。
2番目の問題については、行列を回転および反映する方法が8つあります。開始点として使用される4つのコーナーがあり、アルゴリズムは各コーナーから2方向(つまり、水平または垂直)に歩くことができます。したがって、元のマトリックスの8つの視点のそれぞれに対して「問題1」を実行する必要があります。
これをどのように実装しますか? 8つの行列を並行して、または順番に歩くことができます。これを順次実装する可能性があります(最初にチェックされたマトリックスが一致する場合、他のチェックは必要ありません)。
チェックする8つの行列のそれぞれは、xth
値を取得するためのウォーキング関数f(x)
で表す必要があります。たとえば、n
x n
正方配列A
では、
f1(x) = A[x % n, x / n]; // walking rows first, then columns
f2(x) = A[x / n, x % n]; // walking columns first, then rows
// using a different starting point
f3(x) = A[n - (x / n), x % n];
f4(x) = A[n - (x % n), x / n];
等々。今簡単に、
foreach walking function
foreach value in walking function
if value not equal to corresponding value in target
next walking function
return true // not non-equal values found
return false // none of the functions matched
適切な戦略を考案するには、入力がどのようになりそうかを知ることが役立ちます。たとえば、1の手を含むゼロでいっぱいの大きな配列がある場合、チェックサムを計算するとすぐに否定的な回答が得られますが、配列がランダムデータでいっぱいの場合、チェックサムはそれほど役に立ちません。最初の係数を比較します。通常、1つの配列を他の配列と区別するのに十分です。
このブルートフォースアプローチよりも最適なソリューションはあるのでしょうか。
一般的な入力については、これ以上の解決策はありません。ただし、チェックされた配列にアクセスするためのカスタムアクセサー、つまり配列の座標と16の順列の1つと引数を取る関数を定義することで、配列のコピーを回避できることに注意してください。
アレイのチェックサムを計算して、2つのアレイのチェックサムを比較することを検討する場合があります。
チェックサムは順序に依存しません。 2つの配列のチェックサムが異なる場合、同じ要素を異なる(回転など)配置にすることはできません。
配列を何度も比較する必要がある場合は、合計をキャッシュして再計算を節約できます。
正方形を8つの小さな三角形に分割します。これらの各三角形のハッシュ/チェックサムを計算します。これには、正方形全体で1回の反復のみが必要です。
さて、対称性が存在するためには、これらの三角形のいくつかが等しくなければならず、したがってそれらのハッシュ値も等しくなければなりません。したがって、すべての値が異なる場合は、完了です。
それ以外の場合は、三角形を同じハッシュ値のみと比較します。これにより時間を節約できますが、実装するのはもちろん少し難しいです。