Không đủ kiến thức về Python để trả lời câu này bằng ngôn ngữ bạn yêu cầu, nhưng trong C / C ++, với các tham số của câu hỏi của bạn, tôi sẽ chuyển đổi các số 0 và số một thành bit và đẩy chúng lên các bit có trọng số thấp nhất của uint64_t. Điều này sẽ cho phép bạn so sánh tất cả 55 bit trong một lần trượt - 1 đồng hồ.
Rất nhanh, và toàn bộ điều này sẽ phù hợp với bộ nhớ cache trên chip (209.880 byte). Hỗ trợ phần cứng để chuyển đồng thời tất cả 55 thành viên danh sách chỉ có sẵn trong các thanh ghi của CPU. Điều tương tự cũng xảy ra khi so sánh tất cả 55 thành viên cùng một lúc. Điều này cho phép ánh xạ 1 đến 1 của vấn đề đối với giải pháp phần mềm. (và sử dụng các thanh ghi 256 bit SIMD / SSE, tối đa 256 thành viên nếu cần) Kết quả là mã ngay lập tức rõ ràng đối với người đọc.
Bạn có thể thực hiện điều này trong Python, tôi chỉ không biết rõ về nó nếu có thể hoặc hiệu suất có thể là gì.
Sau khi ngủ trên đó một vài điều đã trở nên rõ ràng, và tất cả đều tốt hơn.
1.) Thật dễ dàng để quay danh sách liên kết vòng tròn bằng cách sử dụng các bit mà thủ thuật rất thông minh của Dali là không cần thiết. Bên trong một dịch chuyển bit tiêu chuẩn 64 bit sẽ thực hiện xoay vòng rất đơn giản và trong nỗ lực làm cho điều này trở nên thân thiện hơn với Python, bằng cách sử dụng số học thay vì bit ops.
2.) Dịch chuyển bit có thể được thực hiện dễ dàng bằng cách chia cho 2.
3.) Việc kiểm tra kết thúc danh sách cho 0 hoặc 1 có thể dễ dàng thực hiện bằng modulo 2.
4.) "Di chuyển" 0 đến đầu danh sách từ đuôi có thể được thực hiện bằng cách chia cho 2. Điều này bởi vì nếu số 0 thực sự được di chuyển, nó sẽ làm cho bit thứ 55 sai, mà nó hoàn toàn không làm gì cả.
5.) "Di chuyển" 1 đến đầu danh sách từ đuôi có thể được thực hiện bằng cách chia cho 2 và thêm 18,014,398,509,481,984 - là giá trị được tạo bằng cách đánh dấu bit thứ 55 đúng và tất cả phần còn lại là sai.
6.) Nếu so sánh neo và uint64_t được soạn thảo là TRUE sau bất kỳ phép quay nào, ngắt và trả về TRUE.
Tôi sẽ chuyển đổi toàn bộ mảng danh sách thành một mảng uint64_ts ngay trước mặt để tránh phải thực hiện chuyển đổi nhiều lần.
Sau khi dành vài giờ cố gắng tối ưu hóa mã, nghiên cứu ngôn ngữ lắp ráp, tôi đã có thể tắt 20% thời gian chạy. Tôi nên thêm rằng trình biên dịch O / S và MSVC cũng đã được cập nhật vào giữa ngày hôm qua. Vì bất kỳ lý do gì, chất lượng mã mà trình biên dịch C tạo ra được cải thiện đáng kể sau khi cập nhật (15/11/2014). Thời gian chạy bây giờ là ~ 70 đồng hồ, 17 nano giây để soạn thảo và so sánh một vòng neo với tất cả 55 vòng của vòng thử nghiệm và NxN của tất cả các vòng so với tất cả các vòng khác được thực hiện trong 12,5 giây .
Mã này rất chặt chẽ, nhưng 4 thanh ghi đang ngồi xung quanh không làm gì 99% thời gian. Ngôn ngữ lắp ráp khớp với mã C gần như dòng cho dòng. Rất dễ đọc và dễ hiểu. Một dự án lắp ráp tuyệt vời nếu ai đó đang tự dạy mình điều đó.
Phần cứng là Hazwell i7, MSVC 64 bit, tối ưu hóa đầy đủ.
#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;
}
