วิธีตรวจสอบว่ามีสองรายการที่เหมือนกันแบบวงกลมใน Python หรือไม่


145

ตัวอย่างเช่นฉันมีรายการ:

a[0] = [1, 1, 1, 0, 0]
a[1] = [1, 1, 0, 0, 1]
a[2] = [0, 1, 1, 1, 0]
# and so on

พวกเขาดูเหมือนจะแตกต่างกัน แต่ถ้ามันควรจะเชื่อมต่อจุดเริ่มต้นและจุดสิ้นสุดแล้วพวกเขาจะเหมือนกันแบบวงกลม

ปัญหาคือแต่ละรายการที่ฉันมีความยาว 55 และมีเพียงสามคนและศูนย์ 52 ในนั้น ไม่มีเงื่อนไขแบบวงกลมมีรายการ 26,235 (55 เลือก 3) อย่างไรก็ตามหากมีเงื่อนไข 'เวียน' อยู่ก็จะมีรายการที่เหมือนกันเป็นวงกลมจำนวนมาก

ขณะนี้ฉันตรวจสอบตัวตนแบบวนโดยทำตาม:

def is_dup(a, b):
    for i in range(len(a)):
        if a == list(numpy.roll(b, i)): # shift b circularly by i
            return True
    return False

ฟังก์ชั่นนี้ต้องการการทำงาน 55 cyclic shift ในกรณีที่แย่ที่สุด และมีรายการที่จะเปรียบเทียบกัน 26,235 รายการ ในระยะสั้นฉันต้องการ 55 * 26,235 * (26,235 - 1) / 2 = 18,926,847,225 การคำนวณ มันเกือบ 20 Giga!

มีวิธีที่ดีที่จะทำอย่างไรกับการคำนวณน้อยลง? หรือชนิดข้อมูลใด ๆ ที่สนับสนุนวงกลม ?


เพียงลางสังหรณ์: ฉันรู้สึกว่าต้นไม้ต่อท้ายอาจช่วยได้ที่นี่ en.wikipedia.org/wiki/Suffix_tree หากต้องการสร้างหนึ่งดูen.wikipedia.org/wiki/Ukkonen%27s_algorithm
Rerito

1
@ Mehrdad แต่เวลาทำงานแย่กว่าคำตอบใด ๆ ที่แปลงเป็นรูปแบบบัญญัติเวลาทำงานแย่กว่าแปลงเป็นจำนวนเต็มและไกลยิ่งแย่กว่า David Eisenstat
Veedrac

2
คำตอบทั้งหมดพยายามแก้ปัญหาทั่วไป แต่ในกรณีนี้มีเพียง 3 ข้อเท่านั้นคุณสามารถแสดงทุกรายการโดยมี 3 หมายเลขเป็นจำนวนศูนย์ระหว่างแต่ละรายการ รายการจากคำถามสามารถแสดงเป็น [0,0,2], [0,2,0], [2,0,0] คุณสามารถลดรายการในการวิ่งครั้งเดียวจากนั้นตรวจสอบรายการที่ลดลง หากพวกเขา "เหมือนกันเวียน" ต้นฉบับนั้นก็เช่นกัน
abc667

1
ฉันเดาว่า Stack Overflow ไม่จำเป็นต้องลงคะแนนในตอนนั้น ทั้งหมดที่เราต้องการคือการเรียกใช้รหัสในโซลูชันทั้งหมดและนำเสนอในลำดับที่พวกเขาเสร็จสิ้น
Dawood ibn Kareem

2
เพราะมันยังไม่ได้รับการกล่าวถึงเพื่อให้ห่างไกลที่ "รูปแบบที่ยอมรับ" เรียกโดย @ abc667, Veedrac และ Eisenstat เรียกว่าเรียกความยาวเข้ารหัสen.wikipedia.org/wiki/Run-length_encoding
เดวิดโลเวลล์

คำตอบ:


133

ก่อนอื่นสิ่งนี้สามารถทำได้ในO(n)แง่ของความยาวของรายการคุณสามารถสังเกตเห็นว่าถ้าคุณจะทำซ้ำรายการของคุณ 2 ครั้ง ( [1, 2, 3]) จะเป็น[1, 2, 3, 1, 2, 3]รายการใหม่ของคุณจะมีรายการวงจรที่เป็นไปได้ทั้งหมด

ดังนั้นสิ่งที่คุณต้องทำก็คือตรวจสอบว่ารายการที่คุณกำลังค้นหานั้นอยู่ในรายการเริ่มต้นของคุณ 2 ครั้งหรือไม่ ในไพ ธ อนคุณสามารถทำสิ่งนี้ได้ด้วยวิธีต่อไปนี้ (สมมติว่าความยาวเท่ากัน)

list1 = [1, 1, 1, 0, 0]
list2 = [1, 1, 0, 0, 1]
print ' '.join(map(str, list2)) in ' '.join(map(str, list1 * 2))

คำอธิบายบางอย่างเกี่ยวกับ oneliner ฉัน: list * 2จะรวมรายการด้วยตัวเองmap(str, [1, 2])แปลงตัวเลขทั้งหมดสตริงและ ' '.join()จะแปลงอาร์เรย์เป็นสตริง['1', '2', '111']'1 2 111'

บางคนชี้ให้เห็นในความคิดเห็น oneliner อาจให้ผลบวกปลอมดังนั้นเพื่อครอบคลุมทุกกรณีขอบที่เป็นไปได้:

def isCircular(arr1, arr2):
    if len(arr1) != len(arr2):
        return False

    str1 = ' '.join(map(str, arr1))
    str2 = ' '.join(map(str, arr2))
    if len(str1) != len(str2):
        return False

    return str1 in str2 + ' ' + str2

PS1เมื่อพูดถึงความซับซ้อนของเวลามันมีค่าสังเกตที่O(n)จะทำได้ถ้าพบ substring ในO(n)เวลา มันไม่ได้เป็นอย่างนั้นเสมอและขึ้นอยู่กับการใช้งานในภาษาของคุณ ( แม้ว่าอาจจะสามารถทำได้ในเชิงเส้นเวลา KMP ตัวอย่าง)

PS2สำหรับคนที่กลัวการใช้งานแบบ จำกัด และด้วยเหตุผลนี้คิดว่าคำตอบนั้นไม่ดี สิ่งที่สำคัญคือความซับซ้อนและความเร็ว อัลกอริทึมนี้อาจทำงานในO(n)เวลาและO(n)พื้นที่ซึ่งทำให้ดีกว่าอะไรในO(n^2)โดเมน หากต้องการดูสิ่งนี้ด้วยตนเองคุณสามารถเรียกใช้เกณฑ์มาตรฐานขนาดเล็ก (สร้างรายการแบบสุ่มปรากฏองค์ประกอบแรกและผนวกเข้ากับส่วนท้ายเพื่อสร้างรายการแบบวนรอบคุณมีอิสระที่จะทำกิจวัตรของคุณเอง)

from random import random
bigList = [int(1000 * random()) for i in xrange(10**6)]
bigList2 = bigList[:]
bigList2.append(bigList2.pop(0))

# then test how much time will it take to come up with an answer
from datetime import datetime
startTime = datetime.now()
print isCircular(bigList, bigList2)
print datetime.now() - startTime    # please fill free to use timeit, but it will give similar results

0.3 วินาทีในเครื่องของฉัน ไม่นานจริงๆ ตอนนี้ลองเปรียบเทียบกับO(n^2)โซลูชัน ในขณะที่กำลังเปรียบเทียบคุณสามารถเดินทางจากสหรัฐอเมริกาไปยังออสเตรเลีย (ส่วนใหญ่อาจเป็นโดยเรือล่องเรือ)


3
เพียงแค่เพิ่มช่องว่างการขยาย (1 ก่อนและ 1 หลังจากแต่ละสาย) จะทำเคล็ดลับ ไม่จำเป็นต้องซับซ้อนสิ่งต่าง ๆ ด้วย regexes (แน่นอนฉันสมมติว่าเราเปรียบเทียบรายการที่มีความยาวเท่ากัน)
Rerito

2
@Rerito ยกเว้นว่ารายการใดรายการหนึ่งมีสตริงซึ่งตัวเองอาจมีช่องว่างนำหน้าหรือต่อท้าย ยังสามารถทำให้เกิดการชน
Adam Smith

12
ฉันไม่ชอบคำตอบนี้ เรื่องไร้สาระการดำเนินงานของสายทำให้ฉันไม่ชอบมันและคำตอบของ David Eisenstatทำให้ฉันยินดีที่จะลงคะแนน การเปรียบเทียบนี้สามารถทำได้ในเวลา O (n) ด้วยสตริง แต่ก็สามารถทำได้ในเวลา O (n) ด้วยจำนวนเต็ม [ต้องการ 10k เป็นการลบตัวเอง] ซึ่งเร็วกว่า อย่างไรก็ตามคำตอบของ David Eisenstat แสดงให้เห็นว่าการทำการเปรียบเทียบใด ๆ นั้นไม่มีประโยชน์เนื่องจากคำตอบนั้นไม่ต้องการ
Veedrac

7
@Veedrac คุณล้อเล่นรึเปล่า? คุณเคยได้ยินเกี่ยวกับความซับซ้อนในการคำนวณไหม? คำตอบของดาวิดต้องใช้เวลา O (n ^ 2) และพื้นที่ O (n ^ 2) เพียงเพื่อสร้างการซ้ำของเขาทั้งหมดซึ่งแม้กระทั่งสำหรับอินพุตขนาดเล็ก 10 ^ 4 ความยาวใช้เวลา 22 วินาทีและใครจะรู้ว่าแรมมากแค่ไหน ไม่ต้องพูดถึงว่าเรายังไม่ได้เริ่มค้นหาอะไรเลยในตอนนี้ (เราเพิ่งสร้างการหมุนวนแบบวงกลมทั้งหมด) และสตริงไร้สาระของฉันให้ผลลัพธ์ที่สมบูรณ์สำหรับอินพุตเช่น 10 ^ 6 ในเวลาน้อยกว่า 0.5 วินาที นอกจากนี้ยังต้องการพื้นที่ O (n) ในการจัดเก็บ ดังนั้นโปรดใช้เวลาทำความเข้าใจคำตอบก่อนที่จะสรุป
Salvador Dali

1
@SalvadorDali คุณดูนุ่มนวล (ไม่มาก) เน้นเวลา ;-)
e2-e4

38

มีความรู้ไม่เพียงพอใน 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;
}

ป้อนคำอธิบายรูปภาพที่นี่


23
ผู้คนพูดถึง "วิธีแก้ปัญหาของ Salvador dali" และฉันก็นั่งอยู่ที่นี่ด้วยความสับสนสงสัยว่าจิตรกรชื่อเดียวกันนี้เป็นนักคณิตศาสตร์ที่มีส่วนช่วยอัลกอริธึมแบบคลาสสิก ฉันรู้แล้วว่าเป็นชื่อผู้ใช้ของคนที่โพสต์คำตอบยอดนิยม ฉันไม่ใช่คนฉลาด
Woodrow Barlow

สำหรับทุกคนที่มี 10k rep และมีการใช้งานที่นี่โดยใช้ Numpy และ vectorization กระจกสรุปสาระสำคัญสำหรับผู้ที่ <10k ฉันลบคำตอบของฉันเพราะคำตอบของDavid Eisenstatชี้ให้เห็นว่าคุณไม่จำเป็นต้องทำการเปรียบเทียบเลยเพราะคุณสามารถสร้างรายการที่ไม่ซ้ำกันได้ทันทีและฉันต้องการสนับสนุนให้ผู้คนใช้คำตอบที่ดีกว่าของเขา
Veedrac

@RocketRoy ทำไมคุณถึงคิดว่า Python จะไม่มีการทำงานบิต? Heck ผมใช้การดำเนินงานบิตในรหัสฉันที่เชื่อมโยง ฉันยังคงคิดว่าคำตอบนี้ส่วนใหญ่ไม่จำเป็น (คำตอบของ David Eisenstat ใช้เวลาทั้งหมด 1 มิลลิวินาที) แต่ฉันพบว่าคำสั่งนั้นแปลก FWIW อัลกอริทึมที่คล้ายกันใน Numpy สำหรับการค้นหา 262M- "รายการ" ใช้เวลาประมาณ 15 วินาทีในคอมพิวเตอร์ของฉัน (สมมติว่าไม่พบคู่ที่ตรงกัน) เฉพาะการหมุนของรายการที่เกิดขึ้นในวงรอบนอกไม่ใช่วงใน
Veedrac

@Quincunx ขอบคุณสำหรับการแก้ไขของคุณเพื่อให้การระบายสีไวยากรณ์ถูกต้องสำหรับ C ++ ชื่นชมอย่างมาก!

@RocketRoy ไม่มีปัญหา เมื่อคุณตอบคำถามมากมายผ่านทางPPCGคุณจะได้เรียนรู้วิธีการทำสีซินแทกซ์
Justin

33

การอ่านระหว่างบรรทัดดูเหมือนว่าคุณกำลังพยายามแจกแจงตัวแทนหนึ่งตัวแทนของแต่ละคลาสสมมูลของสตริงที่มี 3 อันและ 52 ศูนย์ ลองเปลี่ยนจากการแสดงหนาแน่นเป็นหนึ่งเบาบาง (ชุดตัวเลขสามตัวrange(55)) ในการเป็นตัวแทนนี้กะวงกลมsโดยจะได้รับจากความเข้าใจk set((i + k) % 55 for i in s)ตัวแทนขั้นต่ำพจนานุกรมในชั้นเรียนมักจะมีตำแหน่ง 0. รับชุดของรูปแบบ{0, i, j}กับ0 < i < jผู้สมัครคนอื่น ๆ อย่างน้อยในชั้นเรียนที่มีและ{0, j - i, 55 - i} {0, 55 - j, 55 + i - j}ดังนั้นเราต้องการ(i, j) <= min((j - i, 55 - i), (55 - j, 55 + i - j))ให้ต้นฉบับมีค่าน้อยที่สุด นี่คือรหัสการแจงนับ

def makereps():
    reps = []
    for i in range(1, 55 - 1):
        for j in range(i + 1, 55):
            if (i, j) <= min((j - i, 55 - i), (55 - j, 55 + i - j)):
                reps.append('1' + '0' * (i - 1) + '1' + '0' * (j - i - 1) + '1' + '0' * (55 - j - 1))
    return reps

2
@SalvadorDali คุณเข้าใจผิดคำตอบ (ฉันก็ทำเช่นนั้นจนกระทั่งเขาชี้ให้เห็น!) สิ่งนี้สร้างโดยตรง "ตัวแทนหนึ่งคนของแต่ละคลาสสมมูลของสตริงที่มี 3 ตัวและ 52 ศูนย์" รหัสของเขาไม่ได้สร้างการหมุนวนทั้งหมด ราคาดั้งเดิมคือ T (55²·26235²) รหัสของคุณปรับปรุง55²ถึง 55 ดังนั้นก็แค่ T (55 * 26235²) คำตอบเดวิด Eisenstat คือระหว่าง55²และ55³ สำหรับสิ่งที่ทั้ง 55³≪ 55 ·26235² ot ไม่พูดถึงคำศัพท์ขนาดใหญ่ที่นี่เป็นค่าใช้จ่ายจริงใน O (1) ในทุกกรณี
Veedrac

1
@Veedrac แต่ 99% ของผู้อ่านที่จะมาที่คำถามนี้ในอนาคตจะไม่มีข้อ จำกัด ของเขาและฉันเชื่อว่าคำตอบของฉันจะเหมาะกับพวกเขาดีกว่า ฉันจะออกไปที่ห้อง OP เพื่ออธิบายว่าเขาต้องการอะไรอย่างแน่นอน
Salvador Dali

5
@SalvadorDali OP ดูเหมือนจะตกเป็นเหยื่อของปัญหา XYแล้ว โชคดีที่คำถามตัวเองชัดเจนว่าชื่อไม่ได้เป็นอะไรและเดวิดก็สามารถอ่านระหว่างเรื่องได้ หากเป็นกรณีนี้สิ่งที่ถูกต้องคือการแก้ไขชื่อและแก้ปัญหาจริงแทนที่จะตอบคำถามและเพิกเฉยต่อคำถาม
Aaron Dufour

1
@SalvadorDali ภายใต้รหัส Python ของคุณกำลังเรียกเทียบเท่ากับ strstr ของ C () ซึ่งค้นหาสตริงสำหรับสตริงย่อย ในทางกลับกันจะเรียก strcmp () ซึ่งจะทำงานสำหรับ for () การเปรียบเทียบแต่ละอักขระใน string1 กับ string2 ดังนั้นสิ่งที่ดูเหมือน O (n) คือ O (n * 55 * 55) โดยถือว่าการค้นหาเกิดความล้มเหลว ภาษาระดับสูงเป็นดาบสองคม พวกเขาซ่อนรายละเอียดการใช้งานจากคุณ แต่พวกเขาก็ซ่อนรายละเอียดการใช้งานจากคุณ FWIW ความเข้าใจของคุณในการต่อรายชื่อนั้นยอดเยี่ยม เร็วขึ้นเป็น uint8 และเร็วกว่าบิตซึ่งสามารถหมุนได้ง่ายในฮาร์ดแวร์

2
@AleksandrDubinsky เรียบง่ายขึ้นสำหรับคอมพิวเตอร์ซับซ้อนกว่าสำหรับมนุษย์ มันเร็วพอที่จะเป็น
David Eisenstat

12

ทำซ้ำอาร์เรย์แรกจากนั้นใช้อัลกอริทึม Z (O (n)) เพื่อค้นหาอาร์เรย์ที่สองภายในอาร์เรย์แรก

(หมายเหตุ: คุณไม่จำเป็นต้องคัดลอกอาร์เรย์แรกคุณสามารถล้อมรอบในระหว่างการจับคู่)

สิ่งที่ดีเกี่ยวกับอัลกอริทึม Z คือมันง่ายมากเมื่อเทียบกับ KMP, BM ฯลฯ
อย่างไรก็ตามหากคุณรู้สึกทะเยอทะยานคุณสามารถทำการจับคู่สตริงในเวลาเชิงเส้นและพื้นที่คงที่ - strstrเช่นนี้ การใช้งานมันจะเจ็บปวดมากกว่า


6

การติดตามโซลูชันที่ชาญฉลาดของ Salvador Dali วิธีที่ดีที่สุดในการจัดการคือตรวจสอบให้แน่ใจว่าองค์ประกอบทั้งหมดมีความยาวเท่ากันตลอดจน LISTS ทั้งสองมีความยาวเท่ากัน

def is_circular_equal(lst1, lst2):
    if len(lst1) != len(lst2):
        return False
    lst1, lst2 = map(str, lst1), map(str, lst2)
    len_longest_element = max(map(len, lst1))
    template = "{{:{}}}".format(len_longest_element)
    circ_lst = " ".join([template.format(el) for el in lst1]) * 2
    return " ".join([template.format(el) for el in lst2]) in circ_lst

ไม่มีเงื่อนงำถ้านี่เร็วกว่าหรือช้ากว่าโซลูชัน regex ที่แนะนำโดย AshwiniChaudhary ในคำตอบของ Salvador Dali ซึ่งอ่าน:

import re

def is_circular_equal(lst1, lst2):
    if len(lst2) != len(lst2):
        return False
    return bool(re.search(r"\b{}\b".format(' '.join(map(str, lst2))),
                          ' '.join(map(str, lst1)) * 2))

1
wiki ทำสิ่งนี้ตั้งแต่ฉันเพิ่งปรับคำตอบของ Salvador Dali และจัดรูปแบบการเปลี่ยนแปลงของ Ashwini สิ่งนี้เป็นของฉันน้อยมาก
Adam Smith

1
ขอบคุณสำหรับการป้อนข้อมูล ฉันคิดว่าฉันครอบคลุมทุกกรณีที่เป็นไปได้ในโซลูชันที่แก้ไขของฉัน แจ้งให้เราทราบหากมีบางอย่างขาดหายไป
Salvador Dali

@SalvadorDali อ่าใช่ ... ตรวจสอบว่าสตริงมีความยาวเท่ากัน ฉันสนับสนุนที่จะง่ายกว่าการเรียกใช้ผ่านรายการที่กำลังมองหาองค์ประกอบที่ยาวที่สุดจากนั้นเรียกstr.format nเวลาเพื่อจัดรูปแบบสตริงผลลัพธ์ ฉันสนับสนุน .... :)
อดัมสมิ ธ

3

ระบุว่าคุณต้องทำการเปรียบเทียบหลาย ๆ ครั้งมันอาจจะคุ้มค่าในขณะที่คุณผ่านรายการเริ่มต้นเพื่อแปลงเป็นรูปแบบมาตรฐานที่สามารถเปรียบเทียบได้ง่าย?

คุณกำลังพยายามหาชุดของรายการที่ไม่ซ้ำกันแบบวงกลมหรือไม่? ถ้าเป็นเช่นนั้นคุณสามารถโยนพวกเขาเป็นชุดหลังจากแปลงเป็น tuples

def normalise(lst):
    # Pick the 'maximum' out of all cyclic options
    return max([lst[i:]+lst[:i] for i in range(len(lst))])

a_normalised = map(normalise,a)
a_tuples = map(tuple,a_normalised)
a_unique = set(a_tuples)

ขออภัยที่ David Eisenstat ไม่เห็นคำตอบ v.similar ของเขา


3

คุณสามารถหมุนหนึ่งรายการดังนี้:

list1, list2 = [0,1,1,1,0,0,1,0], [1,0,0,1,0,0,1,1]

str_list1="".join(map(str,list1))
str_list2="".join(map(str,list2))

def rotate(string_to_rotate, result=[]):
    result.append(string_to_rotate)
    for i in xrange(1,len(string_to_rotate)):
        result.append(result[-1][1:]+result[-1][0])
    return result

for x in rotate(str_list1):
    if cmp(x,str_list2)==0:
        print "lists are rotationally identical"
        break

3

แปลงแรกทุกองค์ประกอบของรายการของคุณ (ในการคัดลอกถ้าจำเป็น) ไปที่รุ่นที่หมุนที่เป็น lexically ที่ยิ่งใหญ่ที่สุด

จากนั้นเรียงลำดับรายการผลลัพธ์ของรายการ (การเก็บดัชนีไว้ในตำแหน่งรายการดั้งเดิม) และรวมรายการที่เรียงลำดับแล้วทำเครื่องหมายรายการที่ซ้ำกันทั้งหมดในรายการเดิมตามต้องการ


2

การสำรวจบน @ SalvadorDali ของ Piggybacking ในการค้นหาไม้ขีดไฟขนาด a ในระยะยาวใน b + b นี่คือวิธีการแก้ปัญหาโดยใช้การดำเนินการรายการ

def rollmatch(a,b):
    bb=b*2
    return any(not any(ax^bbx for ax,bbx in zip(a,bb[i:])) for i in range(len(a)))

l1 = [1,0,0,1]
l2 = [1,1,0,0]
l3 = [1,0,1,0]

rollmatch(l1,l2)  # True
rollmatch(l1,l3)  # False

วิธีที่ 2: [ถูกลบ]


รุ่นแรกคือ O (n²) rollmatch([1, 0, 1, 1], [0, 1, 1, 1])และครั้งที่สองไม่ได้ทำงาน
Veedrac

เยี่ยมมากฉันจะลบมัน!
PaulMcG

1

ไม่ใช่คำตอบที่สมบูรณ์และไม่มีคำตอบ แต่ในหัวข้อของการปรับให้เหมาะสมโดยลดการเปรียบเทียบฉันก็นึกถึงการเป็นตัวแทนแบบปกติ

กล่าวคือถ้าตัวอักษรอินพุตของคุณคือ {0, 1} คุณสามารถลดจำนวนการเปลี่ยนลำดับที่อนุญาตได้อย่างมาก หมุนรายการแรกไปยังรูปแบบ (หลอก -) ที่ทำให้เป็นมาตรฐาน (จากการแจกแจงในคำถามของคุณฉันจะเลือกอันที่หนึ่งใน 1 บิตอยู่ทางซ้ายสุดและหนึ่งใน 0 บิตอยู่ทางขวาสุด) ตอนนี้ก่อนการเปรียบเทียบแต่ละรายการหมุนรายการอื่นอย่างต่อเนื่องผ่านตำแหน่งที่เป็นไปได้ด้วยรูปแบบการจัดตำแหน่งเดียวกัน

ตัวอย่างเช่นหากคุณมีทั้งหมด 1 บิตสี่บิตอาจมีการเรียงสับเปลี่ยนได้ไม่เกิน 4 ครั้งด้วยการจัดตำแหน่งนี้และหากคุณมีกลุ่มของบิตที่อยู่ติดกัน 1 บิตแต่ละบิตเพิ่มเติมในคลัสเตอร์จะลดจำนวนตำแหน่ง

List 1   1 1 1 0 1 0

List 2   1 0 1 1 1 0  1st permutation
         1 1 1 0 1 0  2nd permutation, final permutation, match, done

สิ่งนี้สรุปให้กับตัวอักษรที่ใหญ่ขึ้นและรูปแบบการจัดตำแหน่งที่แตกต่างกัน ความท้าทายหลักคือการหาการฟื้นฟูที่ดีโดยมีตัวแทนเพียงเล็กน้อยเท่านั้น เป็นการดีที่มันจะเป็นมาตรฐานที่เหมาะสมด้วยการเป็นตัวแทนเพียงอันเดียว แต่ด้วยปัญหาฉันไม่คิดว่าเป็นไปได้


0

สร้างเพิ่มเติมจากคำตอบของ RocketRoy: แปลงรายการทั้งหมดของคุณล่วงหน้าเป็นตัวเลข 64 บิตที่ไม่ได้ลงชื่อ สำหรับแต่ละรายการให้หมุน 55 บิตเหล่านั้นเพื่อค้นหาค่าตัวเลขที่เล็กที่สุด

ขณะนี้คุณเหลือค่า 64 บิตที่ไม่ได้ลงนามสำหรับแต่ละรายการที่คุณสามารถเปรียบเทียบโดยตรงกับค่าของรายการอื่น ๆ ไม่จำเป็นต้องใช้ฟังก์ชัน is_circular_identical () อีกต่อไป

(ในสาระสำคัญคุณจะสร้างมูลค่าข้อมูลประจำตัวสำหรับรายการของคุณที่ไม่ได้รับผลกระทบจากการหมุนเวียนขององค์ประกอบรายการ) ซึ่งอาจทำงานได้หากคุณมีจำนวนรายการในรายการของคุณโดยพลการ


0

นี่เป็นแนวคิดเดียวกันของ Salvador Dali แต่ไม่ต้องการการแปลงสตริง ด้านหลังเป็นแนวคิด KMP เดียวกันที่กู้คืนความคิดเพื่อหลีกเลี่ยงการตรวจสอบกะเป็นไปไม่ได้ พวกเขาเรียก KMPM ที่แก้ไขแล้วเท่านั้น (list1, list2 + list2)

    public class KmpModified
    {
        public int[] CalculatePhi(int[] pattern)
        {
            var phi = new int[pattern.Length + 1];
            phi[0] = -1;
            phi[1] = 0;

            int pos = 1, cnd = 0;
            while (pos < pattern.Length)
                if (pattern[pos] == pattern[cnd])
                {
                    cnd++;
                    phi[pos + 1] = cnd;
                    pos++;
                }
                else if (cnd > 0)
                    cnd = phi[cnd];
                else
                {
                    phi[pos + 1] = 0;
                    pos++;
                }

            return phi;
        }

        public IEnumerable<int> Search(int[] pattern, int[] list)
        {
            var phi = CalculatePhi(pattern);

            int m = 0, i = 0;
            while (m < list.Length)
                if (pattern[i] == list[m])
                {
                    i++;
                    if (i == pattern.Length)
                    {
                        yield return m - i + 1;
                        i = phi[i];
                    }
                    m++;
                }
                else if (i > 0)
                {
                    i = phi[i];
                }
                else
                {
                    i = 0;
                    m++;
                }
        }

        [Fact]
        public void BasicTest()
        {
            var pattern = new[] { 1, 1, 10 };
            var list = new[] {2, 4, 1, 1, 1, 10, 1, 5, 1, 1, 10, 9};
            var matches = Search(pattern, list).ToList();

            Assert.Equal(new[] {3, 8}, matches);
        }

        [Fact]
        public void SolveProblem()
        {
            var random = new Random();
            var list = new int[10];
            for (var k = 0; k < list.Length; k++)
                list[k]= random.Next();

            var rotation = new int[list.Length];
            for (var k = 1; k < list.Length; k++)
                rotation[k - 1] = list[k];
            rotation[rotation.Length - 1] = list[0];

            Assert.True(Search(list, rotation.Concat(rotation).ToArray()).Any());
        }
    }

หวังว่าจะช่วยได้!


0

ทำให้ปัญหาง่ายขึ้น

  • ปัญหาประกอบด้วยรายการของรายการสั่งซื้อ
  • โดเมนของค่าคือไบนารี (0,1)
  • เราสามารถลดปัญหานี้ได้ด้วยการจับคู่1s เข้ากับการนับ
  • และติดต่อกัน0เป็นจำนวนลบ

ตัวอย่าง

A = [ 1, 1, 1, 0, 0, 1, 1, 0 ]
B = [ 1, 1, 0, 1, 1, 1, 0, 0 ]
~
A = [ +3, -2, +2, -1 ]
B = [ +2, -1, +3, -2 ]
  • กระบวนการนี้กำหนดให้รายการแรกและรายการสุดท้ายต้องแตกต่างกัน
  • สิ่งนี้จะลดจำนวนการเปรียบเทียบโดยรวม

กระบวนการตรวจสอบ

  • หากเราสมมติว่ามันซ้ำซ้อนเราสามารถสรุปสิ่งที่เรากำลังมองหาได้
  • โดยทั่วไปรายการแรกจากรายการแรกจะต้องอยู่ที่ไหนสักแห่งในรายการอื่น
  • ตามด้วยสิ่งที่ตามมาในรายการแรกและในลักษณะเดียวกัน
  • รายการก่อนหน้าควรเป็นรายการสุดท้ายจากรายการแรก
  • เนื่องจากมันเป็นวงกลมลำดับจึงเหมือนกัน

The Grip

  • คำถามที่นี่คือที่ที่จะเริ่มรู้จักเทคนิคlookupและlook-ahead
  • เราจะตรวจสอบว่าองค์ประกอบแรกของรายการแรกผ่านรายการที่สอง
  • ความน่าจะเป็นขององค์ประกอบที่พบบ่อยจะลดลงเนื่องจากเราทำแผนที่รายการเป็นฮิสโตแกรม

Pseudo-Code

FUNCTION IS_DUPLICATE (LIST L1, LIST L2) : BOOLEAN

    LIST A = MAP_LIST(L1)
    LIST B = MAP_LIST(L2)

    LIST ALPHA = LOOKUP_INDEX(B, A[0])

    IF A.SIZE != B.SIZE
       OR COUNT_CHAR(A, 0) != COUNT_CHAR(B, ALPHA[0]) THEN

        RETURN FALSE

    END IF

    FOR EACH INDEX IN ALPHA

        IF ALPHA_NGRAM(A, B, INDEX, 1) THEN

            IF IS_DUPLICATE(A, B, INDEX) THEN

                RETURN TRUE

            END IF

        END IF

    END FOR

    RETURN FALSE

END FUNCTION

FUNCTION IS_DUPLICATE (LIST L1, LIST L2, INTEGER INDEX) : BOOLEAN

    INTEGER I = 0

    WHILE I < L1.SIZE DO

        IF L1[I] != L2[(INDEX+I)%L2.SIZE] THEN

            RETURN FALSE

        END IF

        I = I + 1

    END WHILE

    RETURN TRUE

END FUNCTION

ฟังก์ชั่น

  • MAP_LIST(LIST A):LIST MAP องค์ประกอบที่มีคุณสมบัติตามที่ระบุในรายการใหม่

  • LOOKUP_INDEX(LIST A, INTEGER E):LISTกลับรายการของดัชนีที่มีEอยู่ในรายการA

  • COUNT_CHAR(LIST A , INTEGER E):INTEGERCOUNT วิธีการหลายครั้งที่Eองค์ประกอบในรายการA

  • ALPHA_NGRAM(LIST A,LIST B,INTEGER I,INTEGER N):BOOLEANตรวจสอบหากB[I]เทียบเท่ากับในA[0] N-GRAMทิศทางทั้งสอง


ในที่สุด

หากขนาดของรายการจะใหญ่มากหรือถ้าองค์ประกอบที่เราเริ่มตรวจสอบวงจรนั้นสูงบ่อยเราสามารถทำสิ่งต่อไปนี้:

  • ค้นหารายการที่น้อยที่สุดในรายการแรกที่เริ่มต้นด้วย

  • เพิ่มพารามิเตอร์ n-gram N เพื่อลดความน่าจะเป็นในการตรวจสอบเชิงเส้น


0

"แบบฟอร์มมาตรฐาน" ที่มีประสิทธิภาพและรวดเร็วในการคำนวณสำหรับรายการที่เป็นปัญหาสามารถรับได้ดังนี้:

  • นับจำนวนศูนย์ระหว่างจำนวน (ละเว้นการล้อมรอบ) เพื่อรับตัวเลขสามตัว
  • หมุนตัวเลขทั้งสามเพื่อให้หมายเลขที่ใหญ่ที่สุดเป็นอันดับแรก
  • หมายเลขแรก ( a) จะต้องอยู่ระหว่าง18และ52(รวม) Re-encode เป็นระหว่างและ034
  • ตัวเลขที่สอง ( b) ต้องอยู่ระหว่าง0และ26แต่ไม่สำคัญเท่าไหร่
  • วางหมายเลขที่สามเนื่องจากเป็นเพียง52 - (a + b)และไม่เพิ่มข้อมูล

รูปแบบที่เป็นที่ยอมรับเป็นจำนวนเต็มb * 35 + aซึ่งอยู่ระหว่าง0และ936(รวม) ซึ่งเป็นธรรมที่มีขนาดกะทัดรัด (มี477รายการ circularly ซ้ำกันทั้งหมด)


0

ฉันเขียนคำตอบที่ตรงไปตรงมาซึ่งเปรียบเทียบทั้งสองรายการและเพิ่งเพิ่ม (และล้อมรอบ) ดัชนีของค่าที่เปรียบเทียบสำหรับการวนซ้ำแต่ละครั้ง

ฉันไม่รู้จักงูหลามอย่างดีดังนั้นฉันจึงเขียนเป็นภาษาจาวา แต่มันง่ายมากดังนั้นควรปรับให้เข้ากับภาษาอื่นได้ง่าย

จากนี้คุณสามารถเปรียบเทียบรายการประเภทอื่น ๆ

public class Main {

    public static void main(String[] args){
        int[] a = {0,1,1,1,0};
        int[] b = {1,1,0,0,1};

        System.out.println(isCircularIdentical(a, b));
    }

    public static boolean isCircularIdentical(int[] a, int[]b){
        if(a.length != b.length){
            return false;
        }

        //The outer loop is for the increase of the index of the second list
        outer:
        for(int i = 0; i < a.length; i++){
            //Loop trough the list and compare each value to the according value of the second list
            for(int k = 0; k < a.length; k++){
                // I use modulo length to wrap around the index
                if(a[k] != b[(k + i) % a.length]){
                    //If the values do not match I continue and shift the index one further
                    continue outer;
                }
            }
            return true;
        }
        return false;
    }
}

0

อย่างที่คนอื่น ๆ พูดถึงเมื่อคุณพบการหมุนปกติของรายการคุณสามารถเปรียบเทียบได้

นี่คือรหัสการทำงานบางอย่างที่ทำเช่นนี้วิธีการพื้นฐานคือการค้นหาการหมุนแบบปกติสำหรับแต่ละรายการและเปรียบเทียบ:

  • คำนวณดัชนีการหมุนปกติในแต่ละรายการ
  • วนรอบรายการทั้งสองด้วยออฟเซ็ตเปรียบเทียบแต่ละรายการกลับมาถ้าพวกเขาจับคู่ผิด

โปรดทราบว่าวิธีนี้มันไม่ได้ขึ้นอยู่กับตัวเลขคุณสามารถส่งผ่านในรายการของสตริง (ค่าใด ๆ ที่สามารถเปรียบเทียบได้)

แทนที่จะค้นหารายการในรายการเรารู้ว่าเราต้องการให้รายการเริ่มต้นด้วยค่าต่ำสุด - เพื่อให้เราสามารถวนซ้ำค่าต่ำสุดค้นหาจนกว่าเราจะพบว่าสิ่งใดมีค่าต่อเนื่องต่ำที่สุดเก็บไว้เพื่อการเปรียบเทียบเพิ่มเติม จนกว่าเราจะมีสิ่งที่ดีที่สุด

มีโอกาสมากมายที่จะออกก่อนกำหนดเมื่อคำนวณดัชนีรายละเอียดเกี่ยวกับการเพิ่มประสิทธิภาพบางอย่าง

  • ข้ามการค้นหาค่าต่ำสุดที่ดีที่สุดเมื่อมีเพียงหนึ่งค่า
  • ข้ามการค้นหาค่าต่ำสุดเมื่อก่อนหน้านี้เป็นค่าต่ำสุดด้วย (มันจะไม่เป็นการจับคู่ที่ดีกว่า)
  • ข้ามการค้นหาเมื่อค่าทั้งหมดเหมือนกัน
  • ล้มเหลวในช่วงต้นเมื่อรายการมีค่าต่ำสุดที่แตกต่างกัน
  • ใช้การเปรียบเทียบปกติเมื่อออฟเซ็ตตรงกัน
  • ปรับออฟเซ็ตเพื่อหลีกเลี่ยงการตัดค่าดัชนีในรายการใดรายการหนึ่งในระหว่างการเปรียบเทียบ

โปรดทราบว่าใน Python การค้นหาแบบลิสต์ในลิสต์อาจจะเร็วกว่า แต่ฉันก็สนใจที่จะหาอัลกอริทึมที่มีประสิทธิภาพซึ่งสามารถใช้ในภาษาอื่นได้เช่นกัน นอกจากนี้ยังมีข้อได้เปรียบในการหลีกเลี่ยงการสร้างรายการใหม่

def normalize_rotation_index(ls, v_min_other=None):
    """ Return the index or -1 (when the minimum is above `v_min_other`) """

    if len(ls) <= 1:
        return 0

    def compare_rotations(i_a, i_b):
        """ Return True when i_a is smaller.
            Note: unless there are large duplicate sections of identical values,
            this loop will exit early on.
        """
        for offset in range(1, len(ls)):
            v_a = ls[(i_a + offset) % len(ls)]
            v_b = ls[(i_b + offset) % len(ls)]
            if v_a < v_b:
                return True
            elif v_a > v_b:
                return False
        return False

    v_min = ls[0]
    i_best_first = 0
    i_best_last = 0
    i_best_total = 1
    for i in range(1, len(ls)):
        v = ls[i]
        if v_min > v:
            v_min = v
            i_best_first = i
            i_best_last = i
            i_best_total = 1
        elif v_min == v:
            i_best_last = i
            i_best_total += 1

    # all values match
    if i_best_total == len(ls):
        return 0

    # exit early if we're not matching another lists minimum
    if v_min_other is not None:
        if v_min != v_min_other:
            return -1
    # simple case, only one minimum
    if i_best_first == i_best_last:
        return i_best_first

    # otherwise find the minimum with the lowest values compared to all others.
    # start looking after the first we've found
    i_best = i_best_first
    for i in range(i_best_first + 1, i_best_last + 1):
        if (ls[i] == v_min) and (ls[i - 1] != v_min):
            if compare_rotations(i, i_best):
                i_best = i

    return i_best


def compare_circular_lists(ls_a, ls_b):
    # sanity checks
    if len(ls_a) != len(ls_b):
        return False
    if len(ls_a) <= 1:
        return (ls_a == ls_b)

    index_a = normalize_rotation_index(ls_a)
    index_b = normalize_rotation_index(ls_b, ls_a[index_a])

    if index_b == -1:
        return False

    if index_a == index_b:
        return (ls_a == ls_b)

    # cancel out 'index_a'
    index_b = (index_b - index_a)
    if index_b < 0:
        index_b += len(ls_a)
    index_a = 0  # ignore it

    # compare rotated lists
    for i in range(len(ls_a)):
        if ls_a[i] != ls_b[(index_b + i) % len(ls_b)]:
            return False
    return True


assert(compare_circular_lists([0, 9, -1, 2, -1], [-1, 2, -1, 0, 9]) == True)
assert(compare_circular_lists([2, 9, -1, 0, -1], [-1, 2, -1, 0, 9]) == False)
assert(compare_circular_lists(["Hello" "Circular", "World"], ["World", "Hello" "Circular"]) == True)
assert(compare_circular_lists(["Hello" "Circular", "World"], ["Circular", "Hello" "World"]) == False)

ดู: ตัวอย่างนี้สำหรับการทดสอบ / ตัวอย่างเพิ่มเติม


0

คุณสามารถตรวจสอบเพื่อดูว่ารายการ A เท่ากับ cyclic shift ของรายการ B ในเวลา O (N) ที่คาดไว้ค่อนข้างง่ายดาย

ฉันจะใช้ฟังก์ชันแฮชพหุนามเพื่อคำนวณ hash ของ list A และทุกๆการเปลี่ยนแปลงแบบ cyclic ของ list B โดยที่การเปลี่ยนแปลงของ list B มี hash เหมือนกับรายการ A ฉันจะเปรียบเทียบองค์ประกอบจริงเพื่อดูว่าพวกมันเท่ากันหรือไม่ .

เหตุผลที่รวดเร็วนี้คือฟังก์ชั่นแฮชแบบโพลิโนเมียล (ซึ่งเป็นเรื่องธรรมดามาก!) คุณสามารถคำนวณการแฮชของการเลื่อนแต่ละรอบจากครั้งก่อนในเวลาคงที่ดังนั้นคุณสามารถคำนวณการแฮชสำหรับการเลื่อนแบบวงกลมทั้งหมดใน O ( เวลา)

มันได้ผลเช่นนี้:

สมมุติว่า B มีองค์ประกอบ N แล้วความยุ่งเหยิงของ B ที่ใช้ไพรม์ P คือ:

Hb=0;
for (i=0; i<N ; i++)
{
    Hb = Hb*P + B[i];
}

นี่เป็นวิธีที่ได้รับการปรับปรุงเพื่อประเมินพหุนามใน P และเทียบเท่ากับ:

Hb=0;
for (i=0; i<N ; i++)
{
    Hb += B[i] * P^(N-1-i);  //^ is exponentiation, not XOR
}

สังเกตว่าการคูณ B [i] ทุกตัวด้วย P ^ (N-1-i) อย่างไร ถ้าเราเลื่อน B ไปทางซ้ายทีละ 1 ทุก ๆ B [i] จะถูกคูณด้วย P พิเศษยกเว้นอันแรก เนื่องจากการคูณจะกระจายเกินนอกจากนี้เราสามารถคูณส่วนประกอบทั้งหมดในครั้งเดียวเพียงแค่คูณแฮชทั้งหมดแล้วแก้ไขปัจจัยสำหรับองค์ประกอบแรก

แฮชของการเปลี่ยนแปลงด้านซ้ายของ B เป็นเพียง

Hb1 = Hb*P + B[0]*(1-(P^N))

กะซ้ายที่สอง:

Hb2 = Hb1*P + B[1]*(1-(P^N))

และอื่น ๆ ...

หมายเหตุ: คณิตศาสตร์ทั้งหมดข้างต้นจะดำเนินการโมดูโลขนาดคำศัพท์ของเครื่องบางส่วนและคุณจะต้องคำนวณ P ^ N เพียงครั้งเดียว


-1

หากต้องการกาวด้วยวิธีที่เรียบง่ายที่สุดให้ใช้ชุด!

from sets import Set
a = Set ([1, 1, 1, 0, 0])
b = Set ([0, 1, 1, 1, 0]) 
c = Set ([1, 0, 0, 1, 1])
a==b
True
a==b==c
True

สิ่งนี้จะจับคู่สตริงที่มีจำนวน 0 และ 1 เท่ากันโดยไม่จำเป็นต้องเรียงตามลำดับเดียวกัน
GeneralBecos

GeneralBecos: เพียงแค่เลือกสตริงเหล่านั้นและตรวจสอบคำสั่งในขั้นตอนที่สอง
Louis

พวกเขาไม่ได้อยู่ในลำดับเชิงเส้นเดียวกัน พวกเขาอยู่ในลำดับ 'วงกลม' เดียวกัน สิ่งที่คุณอธิบายในขั้นตอนที่ 2 เป็นปัญหาดั้งเดิม
GeneralBecos
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.