ค้นหา XOR ของตัวเลขทั้งหมดในช่วงที่กำหนด


99

คุณจะได้รับช่วงขนาดใหญ่ [a, b] โดยที่ 'a' และ 'b' สามารถอยู่ระหว่าง 1 ถึง 4,000,000,000 รวม คุณต้องหา XOR ของตัวเลขทั้งหมดในช่วงที่กำหนด

ปัญหานี้ถูกใช้ใน TopCoder SRM ฉันเห็นหนึ่งในโซลูชันที่ส่งมาในการแข่งขันและฉันไม่สามารถเข้าใจได้ว่ามันทำงานอย่างไร

ใครช่วยอธิบายวิธีแก้ปัญหาที่ชนะได้:

long long f(long long a) {
     long long res[] = {a,1,a+1,0};
     return res[a%4];
}

long long getXor(long long a, long long b) {
     return f(b)^f(a-1);
}

นี่getXor()คือฟังก์ชันจริงในการคำนวณ xor ของจำนวนทั้งหมดในช่วงที่ผ่าน [a, b] และ "f ()" เป็นฟังก์ชันตัวช่วย


ฉันแก้ไขคำถามของคุณเพียงเล็กน้อย เราไม่สนใจที่จะอธิบายสาเหตุของโค้ดบางตัว แต่เราไม่จำเป็นต้องมีรายการวิธีอื่น ๆ ในการแก้ปัญหานี้ ปล่อยให้ TopCoder
Kev

@Kev ไม่มีประเด็น! ฉันเขียนว่าเพราะบางคนชอบที่จะให้แนวทางของตัวเองมากกว่าที่จะอธิบายสิ่งที่เขียนไปแล้ว และความคิดใหม่ ๆ จะไม่เสียเปล่า ... ;)
rajneesh2k10

แห่งนี้มีพฤติกรรมที่ไม่ได้กำหนดไว้สำหรับการหรือ a<=0 มีการลงนามชนิดเพื่อให้เป็นลบ (หรือ 0) สำหรับปัจจัยการผลิตในเชิงลบ บางทีคุณอาจต้องการและ / หรือจัดทำดัชนีอาร์เรย์? b<0long longx%4unsigned long longa & 3
Peter Cordes

คำตอบ:


153

นี่เป็นวิธีแก้ปัญหาที่ค่อนข้างชาญฉลาด - ใช้ประโยชน์จากความจริงที่ว่ามีรูปแบบของผลลัพธ์ใน XOR ที่กำลังทำงานอยู่ f()ฟังก์ชั่นคำนวณระยะแฮคเกอร์ได้รวมจาก [0 เป็น] ดูตารางนี้สำหรับตัวเลข 4 บิต:

0000 <- 0  [a]
0001 <- 1  [1]
0010 <- 3  [a+1]
0011 <- 0  [0]
0100 <- 4  [a]
0101 <- 1  [1]
0110 <- 7  [a+1]
0111 <- 0  [0]
1000 <- 8  [a]
1001 <- 1  [1]
1010 <- 11 [a+1]
1011 <- 0  [0]
1100 <- 12 [a]
1101 <- 1  [1]
1110 <- 15 [a+1]
1111 <- 0  [0]

โดยที่คอลัมน์แรกคือการแทนค่าฐานสองจากนั้นผลลัพธ์ทศนิยมและความสัมพันธ์กับดัชนี (a) ลงในรายการ XOR สิ่งนี้เกิดขึ้นเนื่องจากบิตบนทั้งหมดยกเลิกและสองบิตต่ำสุดจะวนทุกๆ 4 ดังนั้นนั่นคือวิธีที่จะมาถึงตารางการค้นหาเล็ก ๆ นั้น

ตอนนี้ให้พิจารณาช่วงทั่วไปของ [a, b] เราสามารถใช้f()เพื่อค้นหา XOR สำหรับ [0, a-1] และ [0, b] เนื่องจากค่า XOR ใด ๆ ที่มีตัวมันเองเป็นศูนย์f(a-1)เพียงแค่ยกเลิกค่าทั้งหมดใน XOR จะรันน้อยกว่าaทำให้คุณมี XOR ของช่วง [a, b]


เกณฑ์ช่วงต่ำสุดคือ 1 ไม่ใช่ 0
Pencho Ilchev

2
@PenchoIlchev ไม่ว่าจะรวม 0 หรือไม่ก็ตาม - (n ^ 0) == n
FatalError

2
@ rajneesh2k10 ในการรัน 4 (เริ่มต้นที่ผลคูณของ 4) บิตทั้งหมดยกเว้นค่าต่ำสุดจะเท่ากันดังนั้นจึงสลับกันระหว่างการยกเลิกซึ่งกันและกันหรือมีค่าเดิม เป็นความจริงที่ว่าบิตต่ำสุดจะวนรอบทุกๆ 2 แต่ 0 ^ 1 == 1 (กล่าวคือไม่ยกเลิก) เหตุผลที่สองค่าต่ำสุดเป็นพิเศษก็เพราะว่า (00 ^ 01 ^ 10 ^ 11) == 00 กล่าวคือทุกๆ 4 ค่าที่คุณหมุนเวียนจะทำให้คุณกลับมาเป็น 0 ดังนั้นคุณจึงสามารถยกเลิกวงจรดังกล่าวทั้งหมดได้ซึ่งก็คือ ทำไม% 4 จึงมีความสำคัญ
FatalError

3
@Pandrei aมี 2 ​​ไม่ใช่ 0
harold

1
คอลัมน์นั้นคือ xor ที่รันและ 1 xor 2 คือ 3 ดังนั้นค่าปัจจุบันในแถวนั้นจึงถูกต้องสำหรับฉัน
FatalError

58

การเพิ่มคำตอบที่ยอดเยี่ยมของ FatalError บรรทัดreturn f(b)^f(a-1);นี้สามารถอธิบายได้ดีขึ้น ในระยะสั้นเป็นเพราะ XOR มีคุณสมบัติที่ยอดเยี่ยมเหล่านี้:

  • มันเชื่อมโยง - วางวงเล็บทุกที่ที่คุณต้องการ
  • เป็นการสับเปลี่ยน - ซึ่งหมายความว่าคุณสามารถเคลื่อนย้ายโอเปอเรเตอร์ไปรอบ ๆ (พวกเขาสามารถ "เดินทาง" ได้)

นี่คือการดำเนินการทั้งสองอย่าง:

(a ^ b ^ c) ^ (d ^ e ^ f) = (f ^ e) ^ (d ^ a ^ b) ^ c
  • มันกลับตัวเอง

แบบนี้:

a ^ b = c
c ^ a = b

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

ก่อนอื่นให้กำหนดสิ่งที่เราต้องการและเรียกมันว่า n:

n      = (a ^ a+1 ^ a+2 .. ^ b)

ถ้าช่วยได้ให้นึกถึง XOR (^) ราวกับว่ามันเป็นส่วนเสริม

ลองกำหนดฟังก์ชั่น:

f(b)   = 0 ^ 1 ^ 2 ^ 3 ^ 4 .. ^ b

bมีค่ามากกว่าaดังนั้นเพียงแค่ใส่วงเล็บเสริมสองสามอันอย่างปลอดภัย (ซึ่งเราทำได้เพราะมันเชื่อมโยงกัน) เรายังสามารถพูดสิ่งนี้ได้:

f(b)   = ( 0 ^ 1 ^ 2 ^ 3 ^ 4 .. ^ (a-1) ) ^ (a ^ a+1 ^ a+2 .. ^ b)

ซึ่งง่ายต่อการ:

f(b)   = f(a-1) ^ (a ^ a+1 ^ a+2 .. ^ b)

f(b)   = f(a-1) ^ n

ต่อไปเราจะใช้คุณสมบัติการกลับตัวและการสับเปลี่ยนเพื่อให้เส้นเวทย์มนตร์แก่เรา:

n      = f(b) ^ f(a-1)

หากคุณเคยคิดว่า XOR เป็นเหมือนการเพิ่มคุณจะได้ลบลบตรงนั้น XOR คือ XOR สิ่งที่เพิ่มคือการลบ!

ฉันจะทำสิ่งนี้ด้วยตัวเองได้อย่างไร?

จำคุณสมบัติของตัวดำเนินการทางตรรกะ ทำงานกับพวกเขาเกือบจะเหมือนการบวกหรือการคูณถ้ามันช่วยได้ รู้สึกผิดปกติที่และ (&), xor (^) และหรือ (|) เชื่อมโยงกัน แต่เป็น!

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


3
สิ่งนี้ทำให้ฉันนึกถึงหลักสูตรคณิตศาสตร์ไม่ต่อเนื่องที่ฉันเรียนในมหาวิทยาลัยเมื่อปีที่แล้ว วันที่สนุกสนาน สิ่งที่อยู่ในใจของฉันทันทีหลังจากอ่านนี่คือการ์ตูน XKCD เรื่องนี้
Sean Francis N. Ballais

3

ฉันพบว่าโค้ดด้านล่างนี้ใช้งานได้เหมือนกับวิธีแก้ปัญหาที่ให้ไว้ในคำถาม

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

ฉันต้องการทราบ / เข้าใจการพิสูจน์ทางคณิตศาสตร์ที่อยู่เบื้องหลังรหัสที่กำหนดเช่นอธิบายไว้ในคำตอบโดย @Luke Briggs

นี่คือรหัส JAVA

public int findXORofRange(int m, int n) {
    int[] patternTracker;

    if(m % 2 == 0)
        patternTracker = new int[] {n, 1, n^1, 0};
    else
        patternTracker = new int[] {m, m^n, m-1, (m-1)^n};

    return patternTracker[(n-m) % 4];
}

2

ฉันได้แก้ปัญหาด้วยการเรียกซ้ำแล้ว ฉันแค่แบ่งชุดข้อมูลออกเป็นส่วนที่เกือบเท่ากันสำหรับการทำซ้ำทุกครั้ง

public int recursion(int M, int N) {
    if (N - M == 1) {
        return M ^ N;
    } else {
        int pivot = this.calculatePivot(M, N);
        if (pivot + 1 == N) {
            return this.recursion(M, pivot) ^ N;
        } else {
            return this.recursion(M, pivot) ^ this.recursion(pivot + 1, N);
        }
    }
}
public int calculatePivot(int M, int N) {
    return (M + N) / 2;
}

แจ้งให้เราทราบความคิดของคุณเกี่ยวกับวิธีแก้ปัญหา ยินดีที่จะรับคำตอบในการปรับปรุง โซลูชันที่เสนอจะคำนวณ XOR ด้วยความซับซ้อน 0 (log N)

ขอบคุณ


อันนี้มีความซับซ้อนในการคำนวณเหมือนกันกับการคำนวณ m ^ (m + 1) ปกติ ^ ... ^ (n-1) ^ n นี่คือ 0 (n)
Thế Anh Nguyễn

0

เพื่อรองรับ XOR ตั้งแต่ 0 ถึง N รหัสที่กำหนดให้ต้องแก้ไขดังต่อไปนี้

int f(int a) {
    int []res = {a, 1, a+1, 0};
    return res[a % 4];
}

int getXor(int a, int b) {
    return f(b) ^ f(a);
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.