In Python nicht gut genug informiert, um dies in der von Ihnen gewünschten Sprache zu beantworten, aber in C / C ++ würde ich angesichts der Parameter Ihrer Frage die Nullen und Einsen in Bits konvertieren und sie auf die niedrigstwertigen Bits eines uint64_t übertragen. Auf diese Weise können Sie alle 55 Bits auf einen Schlag vergleichen - 1 Uhr.
Unglaublich schnell, und das Ganze passt in On-Chip-Caches (209.880 Bytes). Die Hardwareunterstützung zum gleichzeitigen Verschieben aller 55 Listenmitglieder nach rechts ist nur in den Registern einer CPU verfügbar. Gleiches gilt für den gleichzeitigen Vergleich aller 55 Mitglieder. Dies ermöglicht eine 1-zu-1-Zuordnung des Problems zu einer Softwarelösung. (und unter Verwendung der SIMD / SSE 256-Bit-Register bis zu 256 Mitglieder, falls erforderlich) Als Ergebnis ist der Code für den Leser sofort offensichtlich.
Möglicherweise können Sie dies in Python implementieren. Ich weiß es einfach nicht gut genug, um zu wissen, ob dies möglich ist oder wie hoch die Leistung sein könnte.
Nach dem Schlafen wurden einige Dinge offensichtlich und alles zum Besseren.
1.) Es ist so einfach, die zirkulär verknüpfte Liste mit Bits zu drehen, dass Dalis sehr cleverer Trick nicht notwendig ist. Innerhalb eines 64-Bit-Registers wird die Standard-Bitverschiebung die Rotation sehr einfach durchführen und in dem Versuch, dies alles Python-freundlicher zu machen, indem Arithmetik anstelle von Bit-Ops verwendet wird.
2.) Die Bitverschiebung kann leicht durch Teilen durch 2 erreicht werden.
3.) Das Überprüfen des Listenendes auf 0 oder 1 kann mit Modulo 2 problemlos durchgeführt werden.
4.) Das "Verschieben" einer 0 vom Ende zum Ende der Liste kann durch Teilen durch 2 erfolgen. Wenn die Null tatsächlich verschoben würde, würde dies das 55. Bit falsch machen, was es bereits ist, indem absolut nichts getan wird.
5.) Das Verschieben einer 1 vom Ende zum Ende der Liste kann durch Teilen durch 2 und Addieren von 18.014.398.509.481.984 erfolgen. Dies ist der Wert, der durch Markieren des 55. Bits als wahr und aller anderen als falsch erzeugt wird.
6.) Wenn ein Vergleich des Ankers und des zusammengesetzten uint64_t nach einer bestimmten Drehung WAHR ist, brechen Sie und geben Sie WAHR zurück.
Ich würde das gesamte Array von Listen direkt vorab in ein Array von uint64_ts konvertieren, um zu vermeiden, dass die Konvertierung wiederholt durchgeführt werden muss.
Nachdem ich einige Stunden damit verbracht hatte, den Code zu optimieren und die Assemblersprache zu studieren, konnte ich 20% der Laufzeit sparen. Ich sollte hinzufügen, dass der O / S- und MSVC-Compiler gestern auch mittags aktualisiert wurde. Aus welchen Gründen auch immer, die Qualität des vom C-Compiler erstellten Codes hat sich nach dem Update (15.11.2014) dramatisch verbessert. Die Laufzeit beträgt jetzt ~ 70 Uhren, 17 Nanosekunden , um einen Ankerring mit allen 55 Windungen eines Testrings zusammenzustellen und zu vergleichen, und NxN aller Ringe gegen alle anderen ist in 12,5 Sekunden erledigt .
Dieser Code ist so eng, dass bis auf 4 Register 99% der Zeit nichts tun. Die Assemblersprache entspricht fast Zeile für Zeile dem C-Code. Sehr leicht zu lesen und zu verstehen. Ein großartiges Montageprojekt, wenn sich jemand das selbst beibringt.
Hardware ist Hazwell i7, MSVC 64-Bit, vollständige Optimierungen.
#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;
}