Je ne connais pas suffisamment Python pour répondre à cela dans le langage demandé, mais en C / C ++, étant donné les paramètres de votre question, je convertirais les zéros et les uns en bits et les pousserais sur les bits les moins significatifs d'un uint64_t. Cela vous permettra de comparer les 55 bits d'un seul coup - 1 horloge.
Très rapide, et le tout rentrera dans des caches sur puce (209 880 octets). La prise en charge matérielle du décalage simultané des 55 membres de la liste vers la droite n'est disponible que dans les registres d'un processeur. Il en va de même pour la comparaison des 55 membres simultanément. Cela permet un mappage 1 pour 1 du problème vers une solution logicielle. (et en utilisant les registres SIMD / SSE 256 bits, jusqu'à 256 membres si nécessaire) En conséquence, le code est immédiatement évident pour le lecteur.
Vous pourrez peut-être l'implémenter en Python, je ne le connais tout simplement pas assez bien pour savoir si c'est possible ou quelles pourraient être les performances.
Après avoir dormi dessus, certaines choses sont devenues évidentes, et tout pour le mieux.
1.) Il est si facile de faire tourner la liste circulaire en utilisant des bits que l'astuce très intelligente de Dali n'est pas nécessaire. À l'intérieur d'un registre 64 bits, le décalage de bits standard accomplira la rotation très simplement, et dans une tentative de rendre tout cela plus convivial pour Python, en utilisant l'arithmétique au lieu d'opérations sur les bits.
2.) Le décalage de bits peut être accompli facilement en utilisant la division par 2.
3.) La vérification de la fin de la liste pour 0 ou 1 peut être facilement effectuée par modulo 2.
4.) "Déplacer" un 0 à la tête de la liste à partir de la queue peut être fait en divisant par 2. Ceci parce que si le zéro était réellement déplacé, cela rendrait le 55ème bit faux, ce qu'il est déjà en ne faisant absolument rien.
5.) "Déplacer" un 1 à la tête de la liste à partir de la queue peut être fait en divisant par 2 et en ajoutant 18 014 398 509 481 984 - qui est la valeur créée en marquant le 55ème bit vrai et tout le reste faux.
6.) Si une comparaison de l'ancre et de uint64_t composé est TRUE après une rotation donnée, interrompre et retourner TRUE.
Je convertirais tout le tableau de listes en un tableau de uint64_ts dès le départ pour éviter d'avoir à faire la conversion à plusieurs reprises.
Après avoir passé quelques heures à essayer d'optimiser le code, à étudier le langage d'assemblage, j'ai pu réduire de 20% le temps d'exécution. Je dois ajouter que le compilateur O / S et MSVC a également été mis à jour en milieu de journée hier. Pour quelque raison que ce soit, la qualité du code produit par le compilateur C s'est considérablement améliorée après la mise à jour (15/11/2014). Le temps d'exécution est maintenant d'environ 70 horloges, 17 nanosecondes pour composer et comparer un anneau d'ancrage avec les 55 tours d'un anneau de test et NxN de tous les anneaux contre tous les autres se fait en 12,5 secondes .
Ce code est si serré que tous les registres sauf 4 sont assis à ne rien faire 99% du temps. Le langage d'assemblage correspond presque ligne pour ligne au code C. Très facile à lire et à comprendre. Un grand projet d'assemblage si quelqu'un apprenait cela.
Le matériel est Hazwell i7, MSVC 64 bits, optimisations complètes.
#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;
}