No estoy lo suficientemente informado en Python para responder esto en el lenguaje solicitado, pero en C / C ++, dados los parámetros de su pregunta, convertiría los ceros y unos en bits y los insertaría en los bits menos significativos de un uint64_t. Esto le permitirá comparar los 55 bits de una sola vez: 1 reloj.
Perversamente rápido, y todo encajará en cachés en chip (209,880 bytes). El soporte de hardware para desplazar los 55 miembros de la lista a la derecha simultáneamente está disponible solo en los registros de una CPU. Lo mismo ocurre con la comparación de los 55 miembros simultáneamente. Esto permite un mapeo 1 por 1 del problema a una solución de software. (y utilizando los registros SIMD / SSE de 256 bits, hasta 256 miembros si es necesario) Como resultado, el código es inmediatamente obvio para el lector.
Es posible que pueda implementar esto en Python, pero no lo sé lo suficiente como para saber si eso es posible o cuál podría ser el rendimiento.
Después de dormir, algunas cosas se volvieron obvias, y todo para mejor.
1.) Es tan fácil girar la lista enlazada circularmente con bits que el ingenioso truco de Dali no es necesario. Dentro de un registro de 64 bits, el desplazamiento de bits estándar logrará la rotación de manera muy simple, y en un intento de hacer que esto sea más amigable con Python, usando aritmética en lugar de operaciones de bits.
2.) El desplazamiento de bits se puede lograr fácilmente usando dividir entre 2.
3.) El módulo 2 puede verificar fácilmente el final de la lista para 0 o 1.
4.) "Mover" un 0 al principio de la lista desde la cola se puede hacer dividiendo entre 2. Esto porque si el cero se moviera realmente haría que el bit 55 sea falso, lo que ya es al no hacer absolutamente nada.
5.) "Mover" un 1 al comienzo de la lista desde la cola se puede hacer dividiendo por 2 y sumando 18,014,398,509,481,984, que es el valor creado al marcar el 55 ° bit verdadero y todo lo demás falso.
6.) Si una comparación del ancla y el compuesto uint64_t es VERDADERO después de una rotación dada, rompa y devuelva VERDADERO.
Convertiría toda la matriz de listas en una matriz de uint64_ts por adelantado para evitar tener que hacer la conversión repetidamente.
Después de pasar algunas horas tratando de optimizar el código, estudiando el lenguaje ensamblador, pude ahorrar un 20% del tiempo de ejecución. Debo agregar que ayer también se actualizó el compilador O / S y MSVC. Por cualquier motivo, la calidad del código que el compilador C produjo mejoró dramáticamente después de la actualización (15/11/2014). El tiempo de ejecución es ahora ~ 70 relojes, 17 nanosegundos para componer y comparar un anillo de anclaje con las 55 vueltas de un anillo de prueba y NxN de todos los anillos contra todos los demás se realiza en 12.5 segundos .
Este código es tan estricto que todos menos 4 registros están sentados sin hacer nada el 99% del tiempo. El lenguaje ensamblador coincide con el código C casi línea por línea. Muy fácil de leer y entender. Un gran proyecto de montaje si alguien se estuviera enseñando eso.
El hardware es Hazwell i7, MSVC de 64 bits, optimizaciones 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;
}