Não conhecedor o suficiente em Python para responder a isso na linguagem solicitada, mas em C / C ++, dados os parâmetros de sua pergunta, eu converteria os zeros e uns em bits e os empurraria para os bits menos significativos de um uint64_t. Isso permitirá que você compare todos os 55 bits de uma só vez - 1 relógio.
Muito rápido, e tudo se encaixa nos caches no chip (209.880 bytes). O suporte de hardware para mudar todos os 55 membros da lista para a direita simultaneamente está disponível apenas nos registros de uma CPU. O mesmo vale para comparar todos os 55 membros simultaneamente. Isso permite um mapeamento 1 por 1 do problema para uma solução de software. (e usando os registros SIMD / SSE de 256 bits, até 256 membros, se necessário) Como resultado, o código é imediatamente óbvio para o leitor.
Você pode implementar isso em Python, mas não o conheço o suficiente para saber se isso é possível ou qual pode ser o desempenho.
Depois de dormir, algumas coisas se tornaram óbvias, e tudo para melhor.
1.) É tão fácil girar a lista vinculada circularmente usando bits que o truque muito inteligente de Dali não é necessário. Dentro de um registro de 64 bits, a troca de bits padrão realizará a rotação de maneira muito simples e na tentativa de tornar tudo mais amigável ao Python, usando aritmética em vez de operações de bits.
2.) A troca de bits pode ser realizada facilmente usando a divisão por 2.
3.) Verificar o final da lista como 0 ou 1 pode ser facilmente realizado pelo módulo 2.
4.) "Mover" um 0 para o início da lista a partir da cauda pode ser feito dividindo-se por 2. Isso porque se o zero fosse realmente movido, tornaria o 55º bit falso, o que já é feito sem absolutamente nada.
5.) "Mover" um 1 para o topo da lista a partir da cauda pode ser feito dividindo por 2 e adicionando 18.014.398.509.481.984 - que é o valor criado pela marcação do 55º bit como verdadeiro e o restante como falso.
6.) Se uma comparação da âncora e do uint64_t composto for TRUE após uma rotação, interrompa e retorne TRUE.
Eu converteria toda a matriz de listas em uma matriz de uint64_ts logo no início, para evitar a conversão repetida.
Depois de passar algumas horas tentando otimizar o código, estudando a linguagem assembly, consegui economizar 20% do tempo de execução. Devo acrescentar que o compilador O / S e MSVC também foi atualizado no meio do dia ontem. Por qualquer motivo, a qualidade do código que o compilador C produziu melhorou drasticamente após a atualização (15/11/2014). O tempo de execução agora é de ~ 70 relógios, 17 nanossegundos para compor e comparar um anel de ancoragem com todas as 55 voltas de um anel de teste e o NxN de todos os anéis contra todos os outros é feito em 12,5 segundos .
Esse código é tão restrito, com exceção de quatro registros, sem fazer nada em 99% do tempo. A linguagem assembly corresponde ao código C quase linha por linha. Muito fácil de ler e entender. Um ótimo projeto de montagem se alguém estivesse ensinando isso a si mesmo.
O hardware é Hazwell i7, MSVC de 64 bits, otimizações completas.
#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;
}