क्यों अगर (n & -n) == n तो n 2 की शक्ति है?


84

Java.util की रेखा 294. आयामी स्रोत कहते हैं

if ((n & -n) == n) // i.e., n is a power of 2
    // rest of the code

ऐसा क्यों है?


2
नया टैग एक संकेत होना चाहिए। :)
bzlm


2
बिट्स का पालन करें। संयोग से, यह दो की शक्ति के रूप में शून्य को भी गिनता है। सूत्र (n & (n - 1)) == 0भी काम करता है (यह सबसे कम क्रम बिट को हटाता है, यदि बिट्स नहीं बचे हैं तो पहले स्थान पर अधिकतम 1 बिट सेट था)।
हैरोल्ड

3
हां, मैं ऐसे कोड का उपयोग करने के लिए दोषी हूं। इस तरह की कई ट्रिक्स हैं, जिन्हें आप निभा सकते हैं, इसलिए जब तक आप जानते हैं कि आप 2 के पूरक अंकगणित से निपट रहे हैं और विभिन्न रूपांतरण और अतिप्रवाह नुकसान के संज्ञान में रहते हैं। अतिरिक्त क्रेडिट फिगर के लिए दो की अगली उच्च शक्ति तक चक्कर लगाना, या शायद दो की शक्ति - 1 - ऐसी चीजें जो कुछ तिमाहियों में आश्चर्यजनक आवृत्ति के साथ किए जाने की आवश्यकता है।
हॉट लक्स

1
रुकिए, क्या हर कोई आजकल java.util. आयामी स्रोत से पढ़ रहा है? (मैंने पढ़ा है कि कुछ महीने पहले, और मुझे इसके बाद से एसओ पर इसके बारे में कुछ सवाल याद हैं।)
मतीन उल्हाक

जवाबों:


48

विवरण पूरी तरह से सटीक नहीं है क्योंकि (0 & -0) == 00 दो की शक्ति नहीं है। इसे कहने का एक बेहतर तरीका है

((n & -n) == n) जब n दो की शक्ति है, या दो की शक्ति का नकारात्मक है, या शून्य है।

यदि n दो की शक्ति है, तो बाइनरी में 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 दो were की शक्ति के अलावा कुछ भी नहीं थे, तो परिणाम थोड़ा याद आ जाएगा क्योंकि दोनों के पूरक में उस ले जाने के कारण उच्चतम बिट सेट नहीं होगा।

† - या शून्य या दो की शक्ति का ऋणात्मक ... जैसा कि शीर्ष पर समझाया गया है।


और कम से कम 1 बिट को अलग करने के लिए वहाँ एक चाल है।
हॉट लीक्स

2
के रूप में (0 & -0) == 0, तुरंत पूर्व कथन है if (n <= 0) throw ...। मतलब यह कि परीक्षण के तहत संख्या उस बिंदु पर कभी 0 (या नकारात्मक) नहीं होगी।
उपयोगकर्ता

1
@ मिचेल, काफी सही। मैं शीर्षक में उस प्रश्न का उत्तर दे रहा था, Random.javaजिसकी आलोचना मैंने नहीं की है।
माइक शमूएल

1
@ माय, मुझे एहसास है कि; हालाँकि, मुझे नहीं लगता कि कोड में टिप्पणी (जो कि सवाल में शामिल है और शीर्षक में प्रश्न का आधार है) काफी पहले से स्थापित पूर्वापेक्षाओं के संदर्भ में नहीं देखे जाने पर अपने आप ही खड़ी हो जाती है। कोड में यह करने के लिए। यदि आप केवल यहां पोस्ट किए गए प्रश्न को देखते हैं, तो हम इस बारे में कुछ भी नहीं जानते हैं कि प्रकार क्या nहै; मैंने इस धारणा की जाँच नहीं की है, लेकिन किसी तरह संदेह है कि एक doubleही तरह से व्यवहार करेगा।
यूजर

3
@ मिचेल, हम इस प्रकार के n"जावा" टैग के प्रकार पर बहुत अच्छे सीमा लगा सकते हैं । या जावा में &परिभाषित नहीं है । यह केवल पूर्णांक प्रकार और बूलियन पर परिभाषित किया गया है। चूंकि बूलियंस के लिए परिभाषित नहीं किया गया है, हम सुरक्षित रूप से अनुमान लगा सकते हैं कि यह अभिन्न है। doublefloat-n
माइक शमूएल

95

क्योंकि 2 के पूरक में, -nहै~n+1

यदि n2 की शक्ति है, तो इसका केवल एक बिट सेट है। तो ~nउस एक को छोड़कर सभी बिट्स सेट है। 1 जोड़ें, और आप फिर से विशेष बिट सेट करते हैं, यह सुनिश्चित करता n & (that thing)है कि इसके बराबर हैn

अनुलग्‍नक भी सत्‍य है क्‍योंकि उस जावा स्रोत में 0 और ऋणात्मक संख्‍याओं को पिछली पंक्ति द्वारा खारिज किया गया था। यदि nएक से अधिक बिट सेट हैं, तो उनमें से एक बिट सबसे अधिक है। यह बिट सेट नहीं किया जाएगा +1क्योंकि इसे "अवशोषित" करने के लिए कम स्पष्ट बिट है:

 n: 00001001000
~n: 11110110111
-n: 11110111000  // the first 0 bit "absorbed" the +1
        ^
        |
        (n & -n) fails to equal n at this bit.

13

आपको बिटमैप के रूप में मान देखने की आवश्यकता है कि यह क्यों सच है:

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 को फ़िल्टर करेगा।


8

दो के पूरक प्रतिनिधित्व में, दो की शक्तियों के बारे में अनोखी बात यह है कि वे सभी 0 बिट्स से मिलकर बने हैं, केवल kth बिट को छोड़कर, जहां n = 2 ^ k:

base 2    base 10
000001 =  1 
000010 =  2
000100 =  4
     ...

दो के पूरक में एक नकारात्मक मूल्य प्राप्त करने के लिए, आप सभी बिट्स को फ्लिप करते हैं और एक को जोड़ते हैं। दो की शक्तियों के लिए, इसका मतलब है कि आपको बाईं ओर 1 एस का एक गुच्छा मिलता है और सकारात्मक मूल्य में 1 बिट शामिल है, और फिर दाईं ओर 0s का एक गुच्छा है:

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 में सबसे कम महत्वपूर्ण सेट बिट होगा। सभी संख्याओं के लिए जो दो की शक्तियां हैं, सबसे कम महत्वपूर्ण सेट बिट एकमात्र सेट बिट है। अन्य सभी नंबरों के लिए, एक से अधिक बिट सेट हैं, जिनमें से केवल सबसे कम परिणाम में सेट किया जाएगा।


4

यह 2 की शक्तियों और उनके दो के पूरक की संपत्ति है ।

उदाहरण के लिए, 8 लें:

8  = 0b00001000

-8 = 0b11111000

दो के पूरक की गणना:

Starting:  0b00001000
Flip bits: 0b11110111  (one's complement)
Add one:   0b11111000  

AND 8    : 0b00001000

2 की शक्तियों के लिए, केवल एक बिट का गठन किया जाएगा ताकि जोड़ने n कारण होगा वें 2 की बिट n स्थापित किया जाना (एक n करने के लिए ले जा रहा रखता वें बिट)। फिर जब आप ANDदो नंबर लेते हैं, तो आपको मूल वापस मिल जाता है।

उन संख्याओं के लिए जो 2 की शक्तियां नहीं हैं, अन्य बिट्स फ़्लिप नहीं होंगे, इसलिए ANDमूल संख्या प्राप्त नहीं होगी ।


4

बस, यदि 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

0

उदाहरण के माध्यम से दिखाया:

8 में हेक्स = 0x000008

-8 में हेक्स = 0xFFFFF8

8 और -8 = 0x000008

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.