要求された言語でこれに答えるのに十分なPythonの知識はありませんが、C / C ++では、質問のパラメーターを考慮して、0と1をビットに変換し、uint64_tの最下位ビットにプッシュします。これにより、1つのクロック-1クロックで55ビットすべてを比較できます。
驚くほど高速で、すべてがオンチップキャッシュ(209,880バイト)に収まります。55のすべてのリストメンバーを同時に右にシフトするためのハードウェアサポートは、CPUのレジスタでのみ使用できます。55人すべてのメンバーを同時に比較する場合も同様です。これにより、問題をソフトウェアソリューションに1対1でマッピングできます。(そしてSIMD / SSE 256ビットレジスタを使用し、必要に応じて最大256のメンバーを使用します)その結果、コードは読者にすぐにわかります。
あなたはこれをPythonで実装することができるかもしれませんが、それが可能かどうか、またはパフォーマンスがどうなるかを知るのに十分なほど知りません。
その上で眠った後、いくつかのことが明らかになり、すべてが良くなりました。
1.)ビットを使用して循環リンクリストをスピンするのは非常に簡単なので、Daliの非常に巧妙なトリックは必要ありません。64ビットレジスタの内部では、ビットシフトの代わりに算術を使用することにより、標準のビットシフトによりローテーションを非常に簡単に実行できます。
2.)2による除算を使用すると、ビットシフトを簡単に実行できます。
3.)リストの最後の0または1をチェックすることは、2を法として簡単に行うことができます。
4.)0を末尾からリストの先頭に「移動」するには、2で除算します。これは、実際にゼロが移動された場合、55番目のビットがfalseになるためです。
5.)1を末尾からリストの先頭に「移動」するには、2で割り、18,014,398,509,481,984を追加します。これは、55番目のビットをtrueに、残りをすべてfalseにマークすることによって作成される値です。
6.)任意の回転後にアンカーと構成されたuint64_tの比較がTRUEの場合、中断してTRUEを返します。
リストの配列全体をすぐにuint64_tsの配列に変換して、変換を繰り返し行う必要がないようにします。
コードを最適化するために数時間を費やした後、アセンブリ言語を研究したところ、ランタイムを20%削減できました。昨日もO / SおよびMSVCコンパイラーが正午に更新されたことも付け加えておきます。理由が何であれ、Cコンパイラが生成したコードの品質は、更新(11/15/2014)後に劇的に向上しました。実行時間は現在、約70クロック、17ナノ秒であり、アンカーリングを構成してテストリングの55ターンすべてと比較し、すべてのリングのNxNを他のすべてのリングに対して12.5秒で実行しています。
このコードは非常にタイトですが、99%の時間、4つのレジスタが何もしないで座っています。アセンブリ言語は、Cコードとほぼ一行ずつ一致します。非常に読みやすく、理解しやすい。誰かが自分でそれを教えていたら、素晴らしい組立プロジェクト。
ハードウェアはHazwell i7、MSVC 64ビット、完全最適化です。
#include "stdafx.h"
#include "stdafx.h"
#include <string>
#include <memory>
#include <stdio.h>
#include <time.h>
const uint8_t LIST_LENGTH = 55; // uint_8 supports full witdth of SIMD and AVX2
// max left shifts is 32, so must use right shifts to create head_bit
const uint64_t head_bit = (0x8000000000000000 >> (64 - LIST_LENGTH));
const uint64_t CPU_FREQ = 3840000000; // turbo-mode clock freq of my i7 chip
const uint64_t LOOP_KNT = 688275225; // 26235^2 // 1000000000;
// ----------------------------------------------------------------------------
__inline uint8_t is_circular_identical(const uint64_t anchor_ring, uint64_t test_ring)
{
// By trial and error, try to synch 2 circular lists by holding one constant
// and turning the other 0 to LIST_LENGTH positions. Return compare count.
// Return the number of tries which aligned the circularly identical rings,
// where any non-zero value is treated as a bool TRUE. Return a zero/FALSE,
// if all tries failed to find a sequence match.
// If anchor_ring and test_ring are equal to start with, return one.
for (uint8_t i = LIST_LENGTH; i; i--)
{
// This function could be made bool, returning TRUE or FALSE, but
// as a debugging tool, knowing the try_knt that got a match is nice.
if (anchor_ring == test_ring) { // test all 55 list members simultaneously
return (LIST_LENGTH +1) - i;
}
if (test_ring % 2) { // ring's tail is 1 ?
test_ring /= 2; // right-shift 1 bit
// if the ring tail was 1, set head to 1 to simulate wrapping
test_ring += head_bit;
} else { // ring's tail must be 0
test_ring /= 2; // right-shift 1 bit
// if the ring tail was 0, doing nothing leaves head a 0
}
}
// if we got here, they can't be circularly identical
return 0;
}
// ----------------------------------------------------------------------------
int main(void) {
time_t start = clock();
uint64_t anchor, test_ring, i, milliseconds;
uint8_t try_knt;
anchor = 31525197391593472; // bits 55,54,53 set true, all others false
// Anchor right-shifted LIST_LENGTH/2 represents the average search turns
test_ring = anchor >> (1 + (LIST_LENGTH / 2)); // 117440512;
printf("\n\nRunning benchmarks for %llu loops.", LOOP_KNT);
start = clock();
for (i = LOOP_KNT; i; i--) {
try_knt = is_circular_identical(anchor, test_ring);
// The shifting of test_ring below is a test fixture to prevent the
// optimizer from optimizing the loop away and returning instantly
if (i % 2) {
test_ring /= 2;
} else {
test_ring *= 2;
}
}
milliseconds = (uint64_t)(clock() - start);
printf("\nET for is_circular_identical was %f milliseconds."
"\n\tLast try_knt was %u for test_ring list %llu",
(double)milliseconds, try_knt, test_ring);
printf("\nConsuming %7.1f clocks per list.\n",
(double)((milliseconds * (CPU_FREQ / 1000)) / (uint64_t)LOOP_KNT));
getchar();
return 0;
}