มีความรู้ไม่เพียงพอใน Python ที่จะตอบคำถามนี้ในภาษาที่คุณร้องขอ แต่ใน C / C ++ ตามพารามิเตอร์ของคำถามของคุณฉันจะแปลงค่าศูนย์และอันเป็นบิตและผลักพวกมันไปบนบิตที่มีนัยสำคัญน้อยที่สุดของ uint64_t สิ่งนี้จะช่วยให้คุณสามารถเปรียบเทียบ 55 บิตทั้งหมดในคราวเดียว - 1 นาฬิกา
รวดเร็วชั่วร้ายและทุกอย่างจะพอดีกับแคชบนชิป (209,880 ไบต์) การสนับสนุนฮาร์ดแวร์สำหรับการเลื่อนสมาชิกในรายชื่อทั้งหมด 55 รายพร้อมกันนั้นมีอยู่ในการลงทะเบียนของ CPU เท่านั้น เช่นเดียวกันสำหรับการเปรียบเทียบสมาชิกทั้ง 55 คนพร้อมกัน สิ่งนี้อนุญาตให้ทำการแมปแบบ 1 ต่อ 1 ของปัญหากับโซลูชันซอฟต์แวร์ (และการใช้การลงทะเบียน SIMD / SSE 256 บิตมากถึง 256 สมาชิกหากจำเป็น) ด้วยเหตุนี้จึงทำให้ผู้อ่านรหัสเห็นได้ชัดเจนในทันที
คุณอาจจะสามารถใช้สิ่งนี้ใน Python ฉันแค่ไม่รู้ว่ามันดีพอที่จะรู้ว่ามันเป็นไปได้หรือประสิทธิภาพนั้นเป็นอย่างไร
หลังจากนอนลงบนมันก็เห็นได้ชัดบางอย่างและทั้งหมดก็เพื่อสิ่งที่ดีกว่า
1. ) มันง่ายมากที่จะหมุนรายการที่เชื่อมโยงแบบวงกลมโดยใช้บิตที่เคล็ดลับที่ฉลาดมากของ Dali นั้นไม่จำเป็น ภายในบิตมาตรฐานการเปลี่ยนบิตแบบ 64 บิตจะทำให้การหมุนเป็นไปอย่างง่ายดายและในการพยายามทำให้ Python เป็นมิตรมากขึ้นโดยใช้เลขคณิตแทนบิต ops
2. ) การเปลี่ยนบิตสามารถทำได้อย่างง่ายดายโดยใช้การหารด้วย 2
3. ) การตรวจสอบจุดสิ้นสุดของรายการสำหรับ 0 หรือ 1 สามารถทำได้อย่างง่ายดายโดย modulo 2
4. ) "การย้าย" 0 ไปยังส่วนหัวของรายการจากหางสามารถทำได้โดยการหารด้วย 2 นี่เพราะถ้าหากศูนย์ถูกย้ายจริงมันจะทำให้บิตที่ 55 ผิดซึ่งมันไม่ได้ทำอะไรเลย
5. ) "การย้าย" 1 ไปยังส่วนหัวของรายการจากหางสามารถทำได้โดยการหารด้วย 2 และเพิ่ม 18,014,398,509,481,984 - ซึ่งเป็นค่าที่สร้างขึ้นโดยการทำเครื่องหมายบิตที่ 55 จริงและส่วนที่เหลือเป็นเท็จทั้งหมด
6. ) ถ้าการเปรียบเทียบตัวยึดและประกอบ uint64_t เป็น TRUE หลังจากการหมุนใด ๆ ให้แบ่งและส่งคืน TRUE
ฉันจะแปลงอาร์เรย์ทั้งหมดของรายการเป็นอาร์เรย์ uint64_ts ที่อยู่ด้านหน้าเพื่อหลีกเลี่ยงการแปลงซ้ำ ๆ
หลังจากใช้เวลาสองสามชั่วโมงเพื่อพยายามเพิ่มประสิทธิภาพของรหัสเรียนภาษาแอสเซมบลีฉันสามารถโกนรันไทม์ 20% ฉันควรเพิ่มว่าคอมไพเลอร์ O / S และ MSVC ได้รับการอัพเดทเมื่อวานนี้เช่นกัน ไม่ว่าจะด้วยเหตุผลใดก็ตามคุณภาพของโค้ดที่คอมไพเลอร์ C สร้างขึ้นก็เพิ่มขึ้นอย่างมากหลังจากการอัพเดท (11/15/2014) เวลาทำงานในขณะนี้คือ ~ 70 นาฬิกา 17 นาโนวินาทีในการเขียนและเปรียบเทียบแหวนยึดกับทุก 55 เปลี่ยนของแหวนการทดสอบและการ NxN ของแหวนทั้งหมดกับคนอื่น ๆ ทั้งหมดจะทำใน12.5 วินาที
รหัสนี้แน่นมาก แต่มีผู้ลงทะเบียน 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;
}