บรรทัดที่ 294 ของแหล่งที่มา java.util.Randomกล่าวว่า
if ((n & -n) == n) // i.e., n is a power of 2
// rest of the code
ทำไมถึงเป็นแบบนี้?
บรรทัดที่ 294 ของแหล่งที่มา java.util.Randomกล่าวว่า
if ((n & -n) == n) // i.e., n is a power of 2
// rest of the code
ทำไมถึงเป็นแบบนี้?
(n & (n - 1)) == 0
นี้ยังใช้งานได้ (จะลบบิตลำดับต่ำสุดหากไม่มีบิตเหลืออยู่แสดงว่ามีการตั้งค่าสูงสุด 1 บิตตั้งแต่แรก)
คำตอบ:
คำอธิบายไม่ถูกต้องทั้งหมดเพราะ(0 & -0) == 0
แต่ 0 ไม่ใช่กำลังสอง วิธีที่ดีกว่าที่จะพูดคือ
((n & -n) == n)
เมื่อ n เป็นเลขยกกำลังสองหรือลบของกำลังสองหรือศูนย์
ถ้า n เป็นเลขยกกำลังสอง n ในเลขฐานสองจะเป็น 1 ตัวเดียวตามด้วยเลขศูนย์ -n ในส่วนเติมเต็มของสองคือผกผัน + 1 ดังนั้นบิตจึงเรียงตัวกัน
n 0000100...000
-n 1111100...000
n & -n 0000100...000
หากต้องการดูว่าเหตุใดจึงทำงานนี้ให้พิจารณาส่วนเติมเต็มสองส่วนเป็นผกผัน + 1 -n == ~n + 1
n 0000100...000
inverse n 1111011...111
+ 1
two's comp 1111100...000
เนื่องจากคุณพกติดตัวไปตลอดเมื่อเพิ่มหนึ่งเพื่อให้ได้ส่วนเสริมของทั้งสอง
ถ้า n เป็นสิ่งอื่นที่ไม่ใช่กำลังของสอง would ผลลัพธ์จะหายไปเล็กน้อยเพราะส่วนเติมเต็มของทั้งสองจะไม่มีบิตที่ตั้งไว้สูงสุดเนื่องจากการพกพานั้น
† - หรือศูนย์หรือลบของกำลังสอง ... ตามที่อธิบายไว้ด้านบน
(0 & -0) == 0
, คำสั่งทันที preceedingif (n <= 0) throw ...
คือ หมายความว่าตัวเลขที่อยู่ระหว่างการทดสอบจะไม่เป็น 0 (หรือลบ) ณ จุดนั้น
Random.java
ที่ฉันยังไม่ได้อ่าน
n
คืออะไร ฉันไม่ได้ตรวจสอบสมมติฐานนี้ แต่สงสัยว่าdouble
จะมีพฤติกรรมแบบเดียวกัน
n
เนื่องจากคำถามนี้มีแท็ก "java" &
ไม่ได้กำหนดไว้บนdouble
หรือfloat
ใน Java กำหนดไว้เฉพาะประเภทจำนวนเต็มและบูลีนเท่านั้น เนื่องจาก-
ไม่ได้กำหนดไว้สำหรับบูลีนเราสามารถสรุปได้อย่างปลอดภัยว่าn
เป็นอินทิกรัล
เพราะในส่วนเสริมของ 2 -n
คือ~n+1
.
ถ้าn
เป็นเลขยกกำลัง 2 ก็จะมีเพียงชุดบิตเดียว ดังนั้นจึง~n
มีการตั้งค่าบิตทั้งหมดยกเว้นบิตนั้น เพิ่ม 1 และคุณตั้งค่าบิตพิเศษอีกครั้งเพื่อให้มั่นใจว่าจะมีค่าเท่ากับn & (that thing)
n
การสนทนายังเป็นจริงเนื่องจาก 0 และจำนวนลบถูกตัดออกโดยบรรทัดก่อนหน้าในซอร์ส Java นั้น หากn
มีการตั้งค่ามากกว่าหนึ่งบิตหนึ่งในนั้นคือบิตที่สูงที่สุด บิตนี้จะไม่ถูกตั้งค่า+1
เนื่องจากมีบิตที่ชัดเจนต่ำกว่าเพื่อ "ดูดซับ":
n: 00001001000
~n: 11110110111
-n: 11110111000 // the first 0 bit "absorbed" the +1
^
|
(n & -n) fails to equal n at this bit.
คุณต้องดูค่าเป็นบิตแมปเพื่อดูว่าเหตุใดจึงเป็นจริง:
1 & 1 = 1
1 & 0 = 0
0 & 1 = 0
0 & 0 = 0
ดังนั้นถ้าทั้งสองช่องเป็น 1 จะได้ 1 ออกมา
ตอนนี้ -n เติมเต็ม 2 มันเปลี่ยนแปลงทั้งหมด0
ไป1
และจะเพิ่ม 1
7 = 00000111
-1 = NEG(7) + 1 = 11111000 + 1 = 11111001
อย่างไรก็ตาม
8 = 00001000
-8 = 11110111 + 1 = 11111000
00001000 (8)
11111000 (-8)
--------- &
00001000 = 8.
สำหรับพาวเวอร์ 2 เท่านั้นที่จะ(n & -n)
เป็น n
เนื่องจากกำลังของ 2 แสดงเป็นบิตเซตเดียวในทะเลยาวของศูนย์ การปฏิเสธจะให้ผลตรงกันข้ามคือศูนย์เดียว(ในจุดที่ 1 เคยเป็น)ในทะเล 1 การเพิ่ม 1 จะเลื่อนตัวล่างเข้าไปในช่องว่างที่ศูนย์อยู่
และบิตและ (&) จะกรอง 1 อีกครั้ง
ในการแทนค่าสองส่วนสิ่งที่ไม่ซ้ำกันเกี่ยวกับพลังของสองคือประกอบด้วย 0 บิตทั้งหมดยกเว้นบิต k โดยที่ n = 2 ^ k:
base 2 base 10
000001 = 1
000010 = 2
000100 = 4
...
ในการรับค่าลบในส่วนเสริมของสองคุณพลิกบิตทั้งหมดแล้วบวกหนึ่ง สำหรับพาวเวอร์ของสองนั่นหมายความว่าคุณจะได้ 1s จำนวนหนึ่งทางด้านซ้ายจนถึงและรวม 1 บิตที่อยู่ในค่าบวกจากนั้นจึงมี 0 จำนวนหนึ่งทางด้านขวา:
n base 2 ~n ~n+1 (-n) n&-n
1 000001 111110 111111 000001
2 000010 111101 111110 000010
4 000100 111011 111100 000100
8 001000 110111 111000 001000
คุณสามารถเห็นได้ง่ายว่าผลลัพธ์ของคอลัมน์ 2 และ 4 จะเหมือนกับคอลัมน์ 2
หากคุณดูค่าอื่น ๆ ที่ขาดหายไปจากแผนภูมินี้คุณจะเห็นว่าเหตุใดจึงไม่ถือเป็นสิ่งใดนอกจากพลังของสอง:
n base 2 ~n ~n+1 (-n) n&-n
1 000001 111110 111111 000001
2 000010 111101 111110 000010
3 000011 111100 111101 000001
4 000100 111011 111100 000100
5 000101 111010 111011 000001
6 000110 111001 111010 000010
7 000111 111000 111001 000001
8 001000 110111 111000 001000
n & -n จะ (สำหรับ n> 0) จะมีชุด 1 บิตเท่านั้นและบิตนั้นจะเป็นบิตชุดที่มีนัยสำคัญน้อยที่สุดใน n สำหรับตัวเลขทั้งหมดที่เป็นพาวเวอร์ของสองบิตชุดที่มีนัยสำคัญน้อยที่สุดคือบิตชุดเดียว สำหรับตัวเลขอื่น ๆ ทั้งหมดจะมีการตั้งค่ามากกว่าหนึ่งบิตซึ่งจะกำหนดเฉพาะค่านัยสำคัญน้อยที่สุดในผลลัพธ์
มันทรัพย์สินของอำนาจของ 2 ของพวกเขาและเติมเต็มสอง
ตัวอย่างเช่นใช้เวลา 8:
8 = 0b00001000
-8 = 0b11111000
การคำนวณส่วนเติมเต็มของทั้งสอง:
Starting: 0b00001000
Flip bits: 0b11110111 (one's complement)
Add one: 0b11111000
AND 8 : 0b00001000
สำหรับอำนาจของ 2 เพียงหนึ่งบิตจะถูกตั้งค่าเพื่อเพิ่มจะทำให้ n THบิตของ 2 nจะเป็นชุด (หนึ่งช่วยให้การดำเนินการกับ n THบิต) จากนั้นเมื่อคุณได้AND
ตัวเลขสองตัวคุณจะได้รับต้นฉบับกลับคืนมา
สำหรับตัวเลขที่ไม่ใช่พาวเวอร์ของ 2 บิตอื่น ๆ จะไม่พลิกดังนั้นจึงAND
ไม่ให้จำนวนเดิม
ถ้า n เป็นเลขยกกำลัง 2 นั่นหมายความว่าบิตเดียวถูกตั้งค่าเป็น 1 และค่าอื่น ๆ เป็น 0:
00000...00001 = 2 ^ 0
00000...00010 = 2 ^ 1
00000...00100 = 2 ^ 2
00000...01000 = 2 ^ 3
00000...10000 = 2 ^ 4
and so on ...
และเนื่องจาก-n
เป็นส่วนเติมเต็มของ 2 n
(นั่นหมายถึงบิตเดียวที่เป็น 1 ยังคงอยู่และบิตทางด้านซ้ายของบิตนั้นจะนั่งเป็น 1 ซึ่งจริงๆแล้วไม่สำคัญเนื่องจากผลลัพธ์ของตัวดำเนินการ AND &
จะเป็น 0 ถ้า หนึ่งในสองบิตเป็นศูนย์):
000000...000010000...00000 <<< n
&
111111...111110000...00000 <<< -n
--------------------------
000000...000010000...00000 <<< n
แสดงผ่านตัวอย่าง:
8 ในฐานสิบหก = 0x000008
-8 ในฐานสิบหก = 0xFFFFF8
8 & -8 = 0x000008