在Python中没有足够的知识来以您所请求的语言来回答这个问题,但是在C / C ++中,考虑到问题的参数,我会将零和一转换为位,然后将其压入uint64_t的最低有效位。这样一来,您就可以比较所有55位-1个时钟。
速度极快,整个过程都可以放入片上缓存(209,880字节)。仅在CPU的寄存器中提供对所有55个列表成员同时右移的硬件支持。同时比较所有55个成员时也是如此。这样可以将问题一对一地映射到软件解决方案。(并使用SIMD / SSE 256位寄存器,如果需要,最多可使用256个成员),因此,该代码对于读者而言立即显而易见。
您也许可以在Python中实现此功能,但我只是不太了解它,无法知道这是否可行或性能如何。
在它上睡觉之后,一些事情变得显而易见,并且一切都变得更好。
1.)使用位旋转循环链接列表非常容易,因此不必使用Dali的巧妙技巧。在64位寄存器内部,标准移位将非常简单地完成旋转,并通过使用算术而非位操作来使这一切对Python更友好。
2.)使用2分频可以轻松完成位移。
3.)通过模2可以很容易地检查列表末尾的0或1。
4.)通过从2的尾部将0从列表的尾部“移动”到列表的头部,这是因为,如果实际移动了0,则将使第55位为false,这已经是绝对不做的了。
5.)将a从尾移到列表的头可以通过除以2并加上18,014,398,509,481,984-这是通过将第55位标记为true并将其余所有标记为false来创建的值。
6.)如果在任何给定的旋转之后,锚点与组合uint64_t的比较为TRUE,则中断并返回TRUE。
我会将整个列表数组直接转换为uint64_ts数组,以避免重复进行转换。
在花了几个小时尝试优化代码之后,研究了汇编语言,我能够节省20%的运行时间。我还应该补充说,O / S和MSVC编译器也在昨天中午更新。无论出于何种原因,C编译器生成的代码质量在更新(11/15/2014)后都得到了显着改善。现在,运行时间约为70个时钟,约17纳秒,可在12.5秒内完成并比较锚环与测试环的所有55圈,并将所有环的NxN与其他环进行比较。
这段代码是如此紧凑,除了4个寄存器外,其余的99%的时间都无所作为。汇编语言几乎逐行匹配C代码。非常容易阅读和理解。如果有人自学的话,这是一个很棒的组装项目。
硬件是Hazwell i7,MSVC 64位,全面优化。
#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;
}