Non abbastanza ben informato in Python per rispondere a questo nel linguaggio richiesto, ma in C / C ++, dati i parametri della tua domanda, convertivo gli zeri e quelli in bit e li spingerei sui bit meno significativi di un uint64_t. Ciò ti consentirà di confrontare tutti i 55 bit in un colpo solo - 1 orologio.
Wickedly fast, e il tutto andrà bene nella cache on-chip (209.880 byte). Il supporto hardware per spostare contemporaneamente tutti e 55 i membri dell'elenco è disponibile solo nei registri di una CPU. Lo stesso vale per il confronto di tutti e 55 i membri contemporaneamente. Ciò consente una mappatura 1 per 1 del problema su una soluzione software. (e utilizzando i registri a 256 bit SIMD / SSE, fino a 256 membri se necessario) Di conseguenza il codice è immediatamente ovvio per il lettore.
Potresti essere in grado di implementarlo in Python, semplicemente non lo so abbastanza bene da sapere se è possibile o quali potrebbero essere le prestazioni.
Dopo aver dormito su di esso, alcune cose sono diventate ovvie, e tutto per il meglio.
1.) È così facile girare l'elenco collegato in modo circolare usando bit che il trucco molto intelligente di Dalì non è necessario. All'interno di un registro a 64 bit, lo spostamento dei bit standard compirà la rotazione in modo molto semplice e nel tentativo di rendere tutto questo più compatibile con Python, usando l'aritmetica invece delle operazioni a bit.
2.) Lo spostamento dei bit può essere realizzato facilmente usando la divisione per 2.
3.) Il controllo della fine dell'elenco per 0 o 1 può essere eseguito facilmente dal modulo 2.
4.) "Spostare" uno 0 in testa alla lista dalla coda può essere fatto dividendo per 2. Questo perché se lo zero fosse effettivamente spostato renderebbe falso il 55 ° bit, cosa che già non fa assolutamente nulla.
5.) "Spostare" un 1 in testa alla lista dalla coda può essere fatto dividendo per 2 e aggiungendo 18.014.398.509.481.984 - che è il valore creato contrassegnando il 55esimo bit vero e tutto il resto falso.
6.) Se un confronto tra l'ancora e composto uint64_t è TRUE dopo una determinata rotazione, rompi e ritorna TRUE.
Vorrei convertire l'intero array di liste in un array di uint64_ts direttamente per evitare di dover ripetere la conversione ripetutamente.
Dopo aver trascorso alcune ore a cercare di ottimizzare il codice, studiando il linguaggio assembly sono stato in grado di ottenere uno sconto del 20% sul tempo di esecuzione. Vorrei aggiungere che ieri anche il compilatore O / S e MSVC è stato aggiornato a metà giornata. Per qualsiasi motivo, la qualità del codice prodotto dal compilatore C è migliorata notevolmente dopo l'aggiornamento (15/11/2014). Il tempo di esecuzione è ora di circa 70 orologi, 17 nanosecondi per comporre e confrontare un anello di ancoraggio con tutti i 55 giri di un anello di prova e NxN di tutti gli anelli rispetto a tutti gli altri viene eseguito in 12,5 secondi .
Questo codice è così stretto ma tutti e 4 i registri sono seduti in giro senza fare nulla il 99% delle volte. Il linguaggio assembly corrisponde al codice C quasi riga per riga. Molto facile da leggere e capire. Un grande progetto di assemblaggio se qualcuno lo insegnasse a loro stessi.
L'hardware è Hazwell i7, MSVC a 64 bit, ottimizzazioni complete.
#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;
}