• 13 min

คำนวณ Hamming Distance จากโจทย์ Programming.in.th

ไม่ง่ายแต่ก็ไม่ยาก รอบนี้เอาทริคการ optimize การคำนวณมาเสนอ ทำให้คำนวณ Hamming Distance ในเคสพิเศษได้เร็วขึ้น

เป็ดไอคอนของเรื่องเล่าชาวอัลกอ Practical Algorithms
Practical Algorithms: เรื่องเล่าชาวอัลกอ
เพจที่อยากให้คนไทยมีเนื้อหาอัลกอริทึมดีๆ ให้ได้อ่านกัน
เป็ดชาวอัลกอ ยืนชี้กระดานดำอธิบายว่าเลขฐานสอง สองตัวต่างกันอย่างไร

โจทย์

ความยาก ★★★☆☆
นิยาม Hamming distance คือจำนวนบิตที่แตกต่างกันในเลขฐานสองระหว่างการเทียบ 2 เลข เช่น 101021010_2 กับ 001120011_2 มี Hamming distance เป็น 2 เพราะต่างกันที่บิตแรกและบิตสุดท้าย

โจทย์จะให้ค่า K,NK,N มาให้ อยากรู้ว่าผลรวม Hamming distance สำหรับเลขฐานสองที่มี KK หลักของ x1x-1 กับ xx เมื่อ xx มีค่าตั้งแต่ 11 ถึง NN คือเท่าไหร่


ข้อมูลนำเข้า บรรทัดแรก ระบุจำนวนเต็ม K,NK,N (K32,N2K1K\leq32,N\leq2^{K}-1) แทนจำนวนหลักของเลขฐานสองและช่วงของค่าที่ดู

ข้อมูลส่งออก แสดงค่าผลรวมของ Hamming distance


ตัวอย่างข้อมูลนำเข้าและข้อมูลส่งออก

Input Output
3 4 7

คำอธิบายตัวอย่างแรก:

K=3K=3 และ N=4N=4 แปลว่าเราต้องดูเลข 33 หลัก ตั้งแต่ 00 จนถึง 44 ที่คำนวณได้ดังนี้

  • 0002000_2 และ 0012001_2 มี Hamming Distance เท่ากับ 11
  • 0012001_2 และ 0102010_2 มี Hamming Distance เท่ากับ 22
  • 0102010_2 และ 0112011_2 มี Hamming Distance เท่ากับ 11
  • 0112011_2 และ 1002100_2 มี Hamming Distance เท่ากับ 33

ทำให้ผลรวมของ Hamming Distance ทั้งหมด เป็น 1+2+1+3=71+2+1+3 = 7


วิเคราะห์โจทย์

การหา Hamming Distance

หา Hamming Distance ของเลข 2 ตัวเนี่ย เราต้องการรู้แค่ว่ามีบิตที่ต่างกันกี่ตัว ดังนั้นเราสามารถทำ xor bit ใส่เลขทั้งสองตัวได้
ในผลลัพธ์ของแต่ละบิตจะเป็นว่า ถ้าบิตของทั้งสองเลขต่างกัน ในผลลัพธ์ก็จะเป็น 11 แต่ถ้าเหมือนกันจะเป็น 00 ดังนั้นโจทย์ก็จะเปลี่ยนเป็นว่าหาว่ามีบิต 1 กี่ตัวแทน

ทีนี้โจทย์ก็จะเหลือแค่ นับจำนวนเลข 11 ในผลลัพธ์ ซึ่งทำได้ใน O(log(N))\mathcal{O}(log(N)) หรือ O(1)\mathcal{O}(1) แล้วแต่ว่าจะ implement ยังไง

แต่ถ้าไปอ่าน Constraint ใหม่ จะเห็นว่า NN เป็นได้ถึง 23212^{32} - 1 ซึ่งมันเยอะไป ค่อยๆทำทีละคู่ไม่ได้เพราะจะไม่ทันเวลาเอา

ข้อสังเกต

โจทย์ไม่ได้ถามหา Hamming Distance ของเลขมั่วๆ แต่เป็นทุกคู่ตัวเลขที่ติดกันเท่านั้น ดังนั้นเราไม่จำเป็นต้องใช้วิธีคำนวณที่รองรับทุกรูปแบบตัวเลขก็ได้

ทีนี้สังเกตเพิ่มว่า การที่ค่า Hamming Distance เท่ากับ pp หมายความว่าจะมีทั้งหมด pp บิตที่แตกต่างกัน ซึ่งแต่ละบิตเนี่ยเราสามารถคิดแยกจากกันได้ แล้วการที่ Hamming Distance เพิ่มขึ้นมา 1 ได้นั้นมาจากการเทียบเลข ที่มีบิตที่มีค่าอันนึงเป็น 0 และอีกอันเป็น 1 ถ้าเรามาดู pattern แยกทีละบิตก็จะเห็นว่า

  • บิตที่ 1 จะเห็นว่าบิตจะแตกต่างกันทุกครั้งเลย สังเกตตอนคำนวณ 0,1 หรือ 1,2 หรือ 2,3 หรือทุกครั้งที่ xx ที่หารด้วย 1 ลงตัว(ก็คือทุกครั้งแหละ 🤣) เพราะบิตนี้จะสลับ 0 กับ 1 ในการบวกทุกครั้ง
0
0

เลขฐานสอง แสดงค่าของ 0

0
1

เลขฐานสอง แสดงค่าของ 1

1
0

เลขฐานสอง แสดงค่าของ 2

  • บิตที่ 2 จะแตกต่างกันในกรณีที่มีค่า 1,2 หรือ 3,4 หรือ 5,6 หรือ xx ที่หารด้วย 2 ลงตัว เพราะบิตนี้จะสลับ 0 กับ 1 ในการบวกทุกๆ 2 ครั้ง
0
0
1

เลขฐานสอง แสดงค่าของ 1

0
1
0

เลขฐานสอง แสดงค่าของ 2

0
1
1

เลขฐานสอง แสดงค่าของ 3

1
0
0

เลขฐานสอง แสดงค่าของ 4

  • บิตที่ 3 ก็จะต่างกันในตอนที่มีคำนวณค่า 3,4 หรือ 7,8 หรือ 11,12 ก็คือเมื่อ xx ที่หารด้วย 4 ลงตัว เพราะบิตนี้จะสลับ 0 กับ 1 ในการบวกทุก 4 ครั้ง
0
1
1

เลขฐานสอง แสดงค่าของ 3

1
0
0

เลขฐานสอง แสดงค่าของ 4

จากการลองทดดูจะสังเกตได้ว่าสำหรับบิตที่ qq ใดๆ บิตนี้จะแตกต่างกันตอนที่เราเทียบ x1x-1 กับ xx เมื่อ xx หารด้วย 2q12^{q-1} ลงตัว

ดังนั้นเราสรุปได้ว่าถ้าคิดแค่บิตที่ qq จะมีทั้งหมด n2q\lfloor{\frac{n}{2^q}}\rfloor คู่ที่แตกต่างกัน ซึ่งเราสามารถคำนวณได้ใน O(1)\mathcal{O}(1) ได้เลย

ประกอบเป็นคำตอบ

โจทย์ของเราก็จะลดรูปเหลือแค่ผลรวมของจำนวนเลขที่หารลงตัวด้วย 1,2,4,8,16,1,2,4,8,16,\dots มีทั้งหมดกี่ตัว ซึ่งหาได้จากการ loop บวกตรงๆ ของ n2q\lfloor{\frac{n}{2^q}}\rfloor ได้ ทำให้ทำได้ใน O(log(N))\mathcal{O}(log(N))

1
uint64_t HammingDistance(uint32_t n, uint8_t k)
2
{
3
uint64_t accumulatedDistance = 0;
4
for(uint8_t i = 0; i < k; i++)
5
accumulatedDistance += n / (1 << i);
6
return accumulatedDistance;
7
}

Challenge

  1. ถ้าเปลี่ยนเป็นถามได้ 100000 ครั้ง และแต่ละครั้งให้ค่า xx เป็นได้ตั้งแต่ aa ถึง bb จะแตกต่างจากวิธีเดิมยังไงบ้าง
  2. ต่อจากข้อ 1 ถ้า K2641K\leq 2^{64}-1 จะแตกต่างจากวิธีในข้อ 1 ยังไงได้บ้าง

ลิงก์

ลิงก์โจทย์ Programming.in.th
0