Не обладающий достаточными знаниями в Python, чтобы ответить на этот вопрос на требуемом вами языке, но в C / C ++, учитывая параметры вашего вопроса, я бы преобразовал нули и единицы в биты и поместил их в младшие значащие биты uint64_t. Это позволит вам сравнить все 55 бит одним махом - 1 такт.
Чрезвычайно быстро, и все это поместится в кэш-память на кристалле (209 880 байт). Аппаратная поддержка одновременного смещения всех 55 элементов списка доступна только в регистрах ЦП. То же самое касается сравнения всех 55 членов одновременно. Это позволяет отображать проблему «один на один» с программным решением. (и с использованием 256-битных регистров SIMD / SSE, при необходимости до 256 членов). В результате код сразу становится очевидным для читателя.
Возможно, вам удастся реализовать это в Python, я просто недостаточно хорошо это знаю, чтобы понять, возможно ли это или какова производительность.
Спать на нем несколько вещей стало очевидным, и все к лучшему.
1.) Закручивать циклически связанный список с помощью битов так легко, что очень умный трюк Дали не нужен. Внутри 64-битного регистра стандартное сдвиг битов будет выполнять вращение очень просто, и в попытке сделать все это более дружественным к Python, используя арифметику вместо битовых операций.
2.) Сдвиг бит может быть легко выполнен с помощью деления на 2.
3.) Проверка конца списка на 0 или 1 может быть легко выполнена по модулю 2.
4.) «Перемещение» 0 в начало списка из хвоста можно сделать, разделив на 2. Это потому, что если бы ноль был фактически перемещен, то это сделало бы 55-й бит ложным, что уже происходит, абсолютно ничего не делая.
5.) «Перемещение» 1 к началу списка из хвоста можно сделать, разделив на 2 и добавив 18 014 398 509 481 984 - это значение, созданное путем пометки 55-го бита истинным, а все остальные ложными.
6.) Если сравнение якоря и составного uint64_t имеет значение ИСТИНА после любого данного вращения, прервите и верните ИСТИНА.
Я бы преобразовал весь массив списков в массив uint64_ts сразу, чтобы избежать необходимости повторного преобразования.
Потратив несколько часов, пытаясь оптимизировать код, изучая язык ассемблера, я смог сэкономить 20% времени выполнения. Я должен добавить, что компилятор O / S и MSVC также обновился вчера. По любой причине / с качество кода, созданного компилятором C, значительно улучшилось после обновления (15.11.2014). Время выполнения теперь составляет ~ 70 часов, 17 наносекунд для составления и сравнения якорного кольца со всеми 55 витками испытательного кольца, и NxN всех колец против всех остальных выполняется за 12,5 секунд. .
Этот код настолько сжат, что все, кроме 4-х регистров, бездействуют в 99% случаев. Язык ассемблера соответствует коду C почти строка за строкой. Очень легко читать и понимать. Отличный сборочный проект, если кто-то учил себя этому.
Аппаратное обеспечение - Hazwell i7, MSVC 64-bit, полная оптимизация.
#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;
}