Bit Manipulation — พื้นฐาน & แนวคิด
มองตัวเลขเป็นชุดของ bits (บิต) แล้วใช้ bitwise operators (ตัวดำเนินการบิต) แก้โจทย์ได้เร็วและประหยัด memory (หน่วยความจำ)
Bit Manipulation (การจัดการบิต) คือการมอง integer (จำนวนเต็ม) เป็นชุดของ bits (บิต — เลข 0 กับ 1) แล้วเล่นกับมันตรง ๆ ด้วย bitwise operators (ตัวดำเนินการบิต) หลายโจทย์ที่ดูยากถ้าคิดเป็นเลขฐานสิบ จะกลายเป็นเรื่องง่ายและเร็วมากเมื่อคิดเป็น bits เพราะคอมพิวเตอร์ทำงานกับ bits ได้เร็วในระดับ hardware อยู่แล้ว หมวดนี้ใช้ตอนที่โจทย์เกี่ยวกับการนับ bits หาตัวที่ไม่ซ้ำ หรือจัดการค่าทีละ bit
เลขฐานสองเบื้องต้น
ทุก integer เก็บในคอมพิวเตอร์เป็นชุดของ bits เช่น เลข 13 ในฐานสิบ เขียนเป็นฐานสองได้ 1101 ซึ่งอ่านว่า 8 + 4 + 0 + 1 = 13 (bit ขวาสุดคือหลัก 1, ถัดมา 2, 4, 8 ไล่เป็นเท่าตัวไปเรื่อย ๆ)
ใน Python เราแปลงและดูค่าฐานสองได้ง่าย ๆ ด้วย bin() และเขียนเลขฐานสองตรง ๆ ด้วยคำนำหน้า 0b
print(bin(13)) # '0b1101'
print(0b1101) # 13
print(13 >> 1) # 6 เลื่อนบิตไปขวา 1 ตำแหน่ง (หารสองปัดลง)
print(13 << 1) # 26 เลื่อนบิตไปซ้าย 1 ตำแหน่ง (คูณสอง)ตัวดำเนินการบิต
bitwise operators จะทำงานกับ bits ทีละตำแหน่ง (bit position) พร้อมกันทุกตำแหน่ง มีดังนี้
| operator | ชื่อ (name) | กฎ (rule) | ตัวอย่าง (example) |
|---|---|---|---|
| & | AND | ได้ 1 เมื่อทั้งสองบิตเป็น 1 | 0b1100 & 0b1010 = 0b1000 (8) |
| | | OR | ได้ 1 เมื่อมีบิต 1 อย่างน้อยหนึ่งฝั่ง | 0b1100 | 0b1010 = 0b1110 (14) |
| ^ | XOR | ได้ 1 เมื่อสองบิตต่างกัน | 0b1100 ^ 0b1010 = 0b0110 (6) |
| ~ | NOT | พลิกทุกบิต (ใน Python ได้ -(x+1)) | ~5 = -6 |
| << | shift ซ้าย | เลื่อนบิตไปซ้าย เท่ากับคูณ 2 ยกกำลัง | 5 << 2 = 20 |
| >> | shift ขวา | เลื่อนบิตไปขวา เท่ากับหาร 2 ยกกำลัง (ปัดลง) | 20 >> 2 = 5 |
เทคนิคบิตที่ต้องจำ
มีสองสามลูกเล่นที่โผล่มาในโจทย์บ่อยมาก จำไว้แล้วชีวิตง่ายขึ้นเยอะ
n = 12 # 0b1100
# 1) n & 1 เช็คว่าเลขคี่หรือคู่ (ดูบิตขวาสุด)
print(n & 1) # 0 -> คู่ (ถ้าได้ 1 คือคี่)
# 2) n & (n - 1) ลบบิต 1 ที่อยู่ขวาสุดออกหนึ่งตัว
# 12 = 1100, 11 = 1011, 12 & 11 = 1000 (8)
print(n & (n - 1)) # 8
# 3) คุณสมบัติของ XOR
# a ^ a = 0 เลขเดียวกัน xor กันได้ 0
# a ^ 0 = a xor กับ 0 ได้ตัวเดิม
# xor สลับลำดับได้ (a ^ b ^ a = b)
print(7 ^ 7) # 0
print(7 ^ 0) # 7
print(7 ^ 3 ^ 7) # 3การลบ 1 จะพลิก bit 1 ขวาสุดให้เป็น 0 และพลิก bit 0 ที่อยู่ขวากว่าให้เป็น 1 ทั้งหมด พอเอามา AND กับ n เดิม bit ที่ต่ำกว่าจึงถูกล้างหายไปหมด เหลือเป็นการลบ bit 1 ขวาสุดพอดี ใช้ iterate (วน) นับจำนวน bit 1 ได้เร็ว
หมวดนี้มี 3 ข้อ (LC338, LC136, LC1318) กดถัดไปเริ่มข้อแรกได้เลย