Bitwise และแทนที่ตัวดำเนินการโมดูลัส


91

เรารู้ว่าตัวอย่างโมดูโลของกำลังสองสามารถแสดงได้ดังนี้:

  x % 2 inpower n == x & (2 inpower n - 1).

ตัวอย่าง:

x % 2 == x & 1
x % 4 == x & 3
x % 8 == x & 7 

แล้ว nonpower ทั่วไปของสองจำนวนล่ะ?

สมมติว่า:

x% 7 ==?


8
@Neil - Modulo และ Binary และเป็นการดำเนินการขั้นพื้นฐานที่ค่อนข้างดีฉันเดาว่ามันเหมือนกันในภาษาคอมพิวเตอร์ใด ๆ
James Kolpack

1
ฉันเบื่อนิดหน่อยที่ไม่เห็นภาษาที่โพสต์ :) แม้ว่าโดยปกติฉันเดาว่าถ้าพวกเขาไม่ระบุ แต่ฉันคิดว่านั่นหมายถึง C ++ หรือ C ฉันสงสัยว่ามันจริงแค่ไหน ..
Garet Claborn

1
เพียงสำหรับทุกคนดิ้นรนที่จะเข้าใจในเรื่องนี้จะดูที่stackoverflow.com/a/13784820/1414639 โอ้และใน JS กับ V8 ฉันได้รับการเพิ่มประสิทธิภาพเล็กน้อยโดยใช้ตัวดำเนินการแบบบิต
Bardi Harborow

1
@JamesKolpack การดำเนินการในระดับบิตสามารถดำเนินการได้เร็วกว่าบน CPU มากกว่าโมดูโล ในความเป็นจริงเคล็ดลับการประกอบทั่วไปเพื่อให้เป็นศูนย์รีจิสเตอร์คือการ XOR ด้วยตัวมันเอง (เพราะความจริงนี้) ปัจจุบันคอมไพเลอร์อาจจะปรับโมดูโลของกำลังสองให้เหมาะสมได้ แต่ฉันไม่รู้
Kaiser Keister

คำตอบ:


70

ก่อนอื่นการพูดแบบนั้นไม่ถูกต้อง

x % 2 == x & 1

ตัวอย่างง่ายๆตัวอย่าง: x = -1. -1 % 2 == -1ในหลายภาษารวมทั้งภาษาจาวา นั่นคือ%ไม่จำเป็นต้องเป็นคำจำกัดความทางคณิตศาสตร์แบบดั้งเดิมของโมดูโล Java เรียกมันว่า "ตัวดำเนินการที่เหลือ" เช่น

เกี่ยวกับการเพิ่มประสิทธิภาพระดับบิตมีเพียงพาวเวอร์โมดูโลสองตัวเท่านั้นที่สามารถ "ทำได้อย่างง่ายดาย" ในการคำนวณทางคณิตศาสตร์ระดับบิต โดยทั่วไปมีเพียงพลังโมดูโลของฐานbเท่านั้นที่สามารถ "ทำได้อย่างง่ายดาย" ด้วยการแทนค่าฐานb

ในฐานที่ 10 ตัวอย่างเช่นสำหรับที่ไม่ใช่เชิงลบN, N mod 10^kเป็นเพียงการอย่างมีนัยสำคัญน้อยกว่าkตัวเลข

อ้างอิง


1
-1 = -1 (mod 2)ไม่แน่ใจว่าคุณได้อะไร - คุณหมายความว่ามันไม่เหมือนกับส่วนที่เหลือของ IEEE 754 ใช่หรือไม่
BlueRaja - Danny Pflughoeft

2
@BlueRaja: สารตกค้างทั่วไปสำหรับ -1 ใน mod 2 คือ 1 en.wikipedia.org/wiki/Modular_arithmetic#Remainders
polygenelubricants

@BlueRaja: หากคุณอนุญาตให้ใช้ตัวเลขติดลบสิ่งที่คุณสามารถมั่นใจได้โดยทั่วไป (โดยเฉพาะอย่างยิ่งเมื่อไม่มีการกล่าวถึงภาษาใด ๆ ) คือ(a / b) / b + a % b == aสำหรับตัวดำเนินการประเภท C จำนวนเต็ม a และ b, b ไม่ใช่ศูนย์และabs(a % b) < abs(b)ด้วยเงื่อนไขเดียวกัน
David Thornley

1
@DavidThornley - ถือว่าคุณหมายถึง*(a / b) b + a % b == a
sfjac

40

มีเพียงวิธีง่ายๆในการค้นหาตัวเลข 2 ^ i โดยใช้ bitwise

มีวิธีที่แยบยลในการไขคดีMersenne ตามลิงค์เช่น n% 3, n% 7 ... มีกรณีพิเศษสำหรับ n% 5, n% 255 และกรณีแบบผสมเช่น n% 6

สำหรับกรณี 2 ^ i, (2, 4, 8, 16 ... )

n % 2^i = n & (2^i - 1)

สิ่งที่ซับซ้อนกว่านั้นยากที่จะอธิบาย อ่านเฉพาะในกรณีที่คุณอยากรู้มาก


1
โหวต ++; ลิงค์ที่ยอดเยี่ยมขอบคุณสำหรับข้อมูลอ้างอิง ฉันแนะนำให้คนอื่นดูมันคุ้มค่าที่จะอ่านแม้ว่ามันจะซับซ้อนไปหน่อยก็ตาม
varzeak

ลิงก์เป็นส่วนที่ดีที่สุดของคำตอบ
Amit Kumar

n% 2 ^ i = n & (1 << i - 1)
Kartik Singh

18

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


2
หากคุณกำลังใช้งานสถาปัตยกรรมแบบ ternary สิ่งนั้นจะเปลี่ยนไปเล็กน้อย ... โอกาสที่จะไม่มีศูนย์
Noldorin

ฉันชอบวิธีที่คุณใช้วลี: "ที่เปลี่ยนแปลงไปเล็กน้อย "
j3141592653589793238

12

นี่เป็นกรณีพิเศษโดยเฉพาะเนื่องจากคอมพิวเตอร์เป็นตัวแทนของตัวเลขในฐาน 2 ซึ่งเป็นลักษณะทั่วไป:

(ตัวเลข) ฐาน % ฐานx

จะเทียบเท่ากับหลักสุดท้ายของ x (จำนวน) ฐาน


5

มีโมดูลอื่นที่ไม่ใช่พาวเวอร์ของ 2 ซึ่งมีอัลกอริทึมที่มีประสิทธิภาพอยู่

ตัวอย่างเช่นถ้า x เป็น 32 บิต int ที่ไม่ได้ลงนามแล้ว x% 3 = popcnt (x & 0x55555555) - popcnt (x & 0xaaaaaaaa)


4

Modulo "7" ที่ไม่มีตัวดำเนินการ "%"

int a = x % 7;

int a = (x + x / 7) & 7;

3
ใช้ไม่ได้สำหรับ 10% 2 = 0 (10 + 10/2) & 2 = 15 & 2 = 2 ในทำนองเดียวกัน 10% 6 = 4 (10 + 10/6) & 6 = 11 & 6 = 2
Sriram Murali

10
นอกจากนี้ทำไมคุณถึงต้องการแบ่งเมื่อคุณต้องการหลีกเลี่ยงการใช้โมดูโล AFAIK คำสั่งในการหารจะเหมือนกับคำสั่งเพื่อหาเศษที่เหลือ
Horse SMith

2
@SriramMurali Thats เพราะคุณใช้แม้กระทั่ง mod แน่นอนว่ามันจะใช้ไม่ได้นี่เป็นวิธีแก้ปัญหาสำหรับแปลกอย่างที่ OP กล่าว
ylun.ca

3

ไม่ใช้ bitwise-and (& ) ในไบนารีจะไม่มี ร่างหลักฐาน:

สมมติว่ามีค่าkดังกล่าวว่าx & k == x % (k + 1)แต่k = 2 ^ n - 1 แล้วถ้าx == kแสดงออกx & kดูเหมือนว่าจะ "ทำงานอย่างถูกต้อง" และผลที่ได้คือk ตอนนี้พิจารณาx == ki : ถ้ามีคนใด "0" บิตในk , มีบางฉันมากกว่า 0 ซึ่งkiอาจจะแสดงออกด้วย 1 บิตในตำแหน่งนั้น (เช่น 1011 (11) ต้องกลายเป็น 0111 (7) เมื่อลบ 100 (4) ออกจากมันในกรณีนี้ 000 บิตจะกลายเป็น 100 เมื่อi = 4 ) ถ้าบิตจากนิพจน์ของkต้องเปลี่ยนจากศูนย์ หนึ่งเพื่อแสดงถึงkiดังนั้นจึงไม่สามารถคำนวณx% (k + 1)ได้อย่างถูกต้องซึ่งในกรณีนี้ควรเป็นkiแต่ไม่มีวิธีใดสำหรับบูลีนระดับบิตและสร้างค่านั้นตามมาสก์


2

ในกรณีเฉพาะนี้ (mod 7) เรายังคงสามารถแทนที่% 7 ด้วยตัวดำเนินการแบบบิต:

// Return X%7 for X >= 0.
int mod7(int x)
{
  while (x > 7) x = (x&7) + (x>>3);
  return (x == 7)?0:x;
}

มันใช้งานได้เพราะ 8% 7 = 1 เห็นได้ชัดว่าโค้ดนี้น่าจะมีประสิทธิภาพน้อยกว่า x% 7 ธรรมดาและอ่านได้น้อยกว่า


1

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

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