क्या एक पूर्णांक में 1-बिट्स के लिए परीक्षण करने के लिए एक सुरुचिपूर्ण और तेज़ तरीका है एक सन्निहित क्षेत्र में होना चाहिए?


85

मुझे यह जांचने की आवश्यकता है कि क्या बिट्स मान 1 के साथ (32 बिट पूर्णांक के लिए 0 से 31) एक सन्निहित क्षेत्र बनाते हैं। उदाहरण के लिए:

00111111000000000000000000000000      is contiguous
00111111000000000000000011000000      is not contiguous

मैं चाहता हूं कि यह परीक्षण, अर्थात कुछ कार्य has_contiguous_one_bits(int), पोर्टेबल हो।

एक स्पष्ट तरीका पहले सेट बिट को खोजने के लिए पदों पर लूप करना है, फिर पहला गैर-सेट बिट और किसी भी अधिक सेट बिट्स के लिए जांचना है।

मुझे आश्चर्य है कि क्या कोई तेज़ तरीका मौजूद है? यदि उच्चतम और निम्नतम सेट बिट्स को खोजने के लिए तेज़ तरीके हैं (लेकिन इस प्रश्न से यह प्रतीत होता है कि कोई पोर्टेबल नहीं हैं), तो एक संभावित कार्यान्वयन है

bool has_contiguous_one_bits(int val)
{
    auto h = highest_set_bit(val);
    auto l = lowest_set_bit(val);
    return val == (((1 << (h-l+1))-1)<<l);
}

बस मज़े के लिए, यहाँ सन्निहित बिट्स के साथ पहले 100 पूर्णांक हैं:

0 1 2 3 4 6 7 8 12 14 15 16 24 28 30 31 32 48 56 60 62 63 64 96 112 120 124 126 127 128 192 224 240 248 252 254 255 256 384 448 480 496 504 508 510 511 512 768 896 960 992 1008 1016 1020 1022 1023 1024 1536 1792 1920 1984 2016 2032 2040 2044 2046 2047 2048 3072 3584 3840 3968 4032 4064 4080 4088 4092 4094 4095 4096 6144 7168 7680 7936 8064 8128 8160 8176 8184 8188 8190 8191 8192 12288 14336 15360 15872 16128 16256 16320

वे (बेशक) (1<<m)*(1<<n-1)गैर-नकारात्मक के साथ फॉर्म के हैं mऔर n


4
@aafulei हां, 0x0कॉम्पैक्ट है। इसके विपरीत को परिभाषित करना आसान है (कॉम्पैक्ट नहीं): यदि वू सेट बिट्स हैं जैसे कि उनके बीच कम से कम एक परेशान बिट है।
वाल्टर

1
@KamilCuk h>=l(निहित) की कार्यक्षमता द्वारा highest_set_bit()औरlowest_set_bit()
वाल्टर


6
उस OEIS लिंक का कहना है कि बाइनरी में इन संख्याओं में गैर-वृद्धि होती है। उन्हें संदर्भित करने का एक और तरीका यह कहना होगा कि वे सन्निहित हैं (या शायद जुड़े हुए हैं)। इस गणितज्ञ के लिए, "कॉम्पैक्ट" का अर्थ बहुत अलग है।
टेपेइमम

1
@ मुझे लगता है कि एक कारण यह प्रश्न गर्म नेटवर्क प्रश्नों पर समाप्त हो गया है क्योंकि यह कॉम्पैक्ट शब्द के दुरुपयोग के कारण है, यह निश्चित रूप से मैंने इस पर क्यों क्लिक किया: मैं बहुत सोच नहीं रहा था और सोच रहा था कि कॉम्पैक्टनेस को परिभाषित करने के लिए यह कैसे समझ सकता है उस तरफ। जाहिर है इसका कोई मतलब नहीं है।
कोई

जवाबों:


147
static _Bool IsCompact(unsigned x)
{
    return (x & x + (x & -x)) == 0;
}

संक्षेप में:

x & -xसबसे कम बिट सेट x(या शून्य यदि xशून्य है) में देता है।

x + (x & -x) लगातार 1s के सबसे कम स्ट्रिंग को 1 (या शून्य तक लपेटता है) में कनवर्ट करता है।

x & x + (x & -x) उन 1 बिट्स को साफ करता है।

(x & x + (x & -x)) == 0 परीक्षण करता है कि क्या कोई अन्य 1 बिट रहता है।

लंबे समय तक:

-xबराबरी ~x+1, दो के पूरक का उपयोग करते हुए, जिसे हम मानते हैं। बिट्स के फ़्लिप होने के बाद ~x, 1 कैर्रीज़ को जोड़ना ताकि यह कम 1 बिट्स को वापस अंदर ले जाए ~xऔर पहले 0 बिट पर फिर रुक जाए। इस प्रकार, -xइसके पहले 1 तक के कम बिट्स और निम्न बिट्स समान हैं x, लेकिन सभी उच्च बिट्स फ़्लिप हैं। (उदाहरण: ~10011100देता है 01100011, और 1 को जोड़ता है 01100100, इसलिए निम्न 100समान हैं, लेकिन उच्च 10011को फ़्लिप किया जाता है 01100।) फिर x & -xहमें केवल एक बिट देता है जो दोनों में 1 है, जो कि सबसे कम 1 बिट ( 00000100) है। (यदि xशून्य है, x & -xशून्य है।)

इसे xसभी 1s के माध्यम से कैरी के कारण जोड़कर , उन्हें 0 में बदल दिया जाता है। यह अगले उच्च 0 पर एक 1 को छोड़ेगा (या उच्च अंत के माध्यम से ले जाएगा, शून्य के एक लिपटे कुल को छोड़कर) ( 10100000)।

जब यह एंडेड होता है x, तो 0s उन जगहों पर होते हैं जहां 1s को 0s में बदल दिया गया था (और जहां कैरी 0 से 1 में बदल गया था)। तो परिणाम केवल शून्य नहीं है अगर एक और 1 बिट ऊपर है।


23
कम से कम किसी को हैकर डिलाइट की किताब पता है। कृपया उत्तर के लिए अध्याय 2-1 देखें। लेकिन यह भी एसओ पर यहाँ कई बार उत्तर दिया गया है। वैसे भी: +1
आर्मिन मोंटेगुन

33
मुझे आशा है कि यदि आप कभी भी उत्पादन में इस तरह के कोड लिखते हैं, तो आप टिप्पणियों में स्पष्टीकरण शामिल करते हैं;)
पॉलिग्नोम

14
यह x86 BMI1 से x & -xएक ही blsiनिर्देश में करने के लिए अच्छी तरह से लाभ देता है, जो Intel पर 1 uop, AMD Zen पर 2 uops है। godbolt.org/z/5zBx-A । लेकिन बीएमआई 1 के बिना, @ केविनज़ का संस्करण और भी अधिक कुशल है।
पीटर कॉर्ड्स

3
@ टॉमीएंडर्सन: _Boolएक मानक कीवर्ड है, प्रति C 2018 6.4.1 1.
एरिक पोस्टपिसिल

1
@ दलाल: हम्म? यह कोड उपयोग करता है unsigned। यदि आप हस्ताक्षर किए गए दो के पूरक के लिए परीक्षण करना चाहते हैं int, तो सबसे आसान तरीका यह है कि इसे इस उत्तर में दिनचर्या में intबदल दें, जिससे इसे परिवर्तित किया जा सके unsigned। वह वांछित परिणाम देगा। intअतिप्रवाह / ले जाने के मुद्दों के कारण सीधे एक हस्ताक्षर किए गए ऑपरेशन को लागू करना समस्याग्रस्त हो सकता है। (यदि आप किसी के पूरक या साइन-एंड-परिमाण का परीक्षण करना चाहते हैं int, तो यह एक और मामला है, मोटे तौर पर केवल इन दिनों सैद्धांतिक हित में।)
एरिक पोस्टपिसिल

29

वास्तव में किसी भी आंतरिक उपयोग करने की आवश्यकता नहीं है।

पहले 1 से पहले सभी 0 को फ्लिप करें। फिर परीक्षण करें कि क्या नया मान एक मैर्सन नंबर है। इस अहंकार में, शून्य को सच करने के लिए मैप किया जाता है।

bool has_compact_bits( unsigned const x )
{
    // fill up the low order zeroes
    unsigned const y = x | ( x - 1 );
    // test if the 1's is one solid block
    return not ( y & ( y + 1 ) );
}

बेशक, यदि आप आंतरिक का उपयोग करना चाहते हैं, तो यहां पॉपकाउंट विधि है:

bool has_compact_bits( unsigned const x )
{
    size_t const num_bits = CHAR_BIT * sizeof(unsigned);
    size_t const sum = __builtin_ctz(x) + __builtin_popcount(x) + __builtin_clz(z);
    return sum == num_bits;
}

2
पहला संस्करण केवल 4 निर्देशों तक कम हो जाता है यदि उनका संकलन -mtbm, शोषण blsfill/ blcfillनिर्देश किया जाता है। यह अब तक प्रस्तावित सबसे छोटा संस्करण होगा। दुर्भाग्य से, लगभग कोई भी प्रोसेसर समर्थन नहीं करता है जो अनुदेश सेट का विस्तार करता है
जियोवानी सेरेटानी

19

वास्तव में आपको अग्रणी शून्य गिनने की आवश्यकता नहीं है। जैसा कि टिप्पणियों में pmg द्वारा सुझाया गया है, इस तथ्य का फायदा उठाते हुए कि आप जिन संख्याओं की तलाश कर रहे हैं, वे अनुक्रम OEIS A023758 हैं , अर्थात फॉर्म 2 की संख्याएं - i - 2 ^ j के साथ i> = j , आप बस ट्रेक्टर शून्य की गणना कर सकते हैं ( यानी j - 1 ), मूल मूल्य में उन बिट्स को टॉगल करें ( 2 ^ j - 1 जोड़ने के बराबर ), और फिर जांचें कि क्या मान 2 ^ i - 1 के फॉर्म का है । जीसीसी / क्लैंग इंट्रिनिक्स के साथ,

bool has_compact_bits(int val) {
    if (val == 0) return true; // __builtin_ctz undefined if argument is zero
    int j = __builtin_ctz(val) + 1;
    val |= (1 << j) - 1; // add 2^j - 1
    val &= (val + 1); // val set to zero if of the form (2^i - 1)
    return val == 0;
}

यह संस्करण थोड़ा तेज है, फिर आपका और कामिलुक द्वारा प्रस्तावित और यूरी फेल्डमैन द्वारा केवल पॉपकाउंट के साथ।

आप सी ++ 20 का उपयोग कर रहे हैं, तो आप को बदल कर एक पोर्टेबल समारोह मिल सकता है __builtin_ctzके साथ std::countr_zero:

#include <bit>

bool has_compact_bits(int val) {
    int j = std::countr_zero(static_cast<unsigned>(val)) + 1; // ugly cast
    val |= (1 << j) - 1; // add 2^j - 1
    val &= (val + 1); // val set to zero if of the form (2^i - 1)
    return val == 0;
}

कलाकार बदसूरत है, लेकिन यह आपको चेतावनी दे रहा है कि बिट्स में हेरफेर करने पर अहस्ताक्षरित प्रकार के साथ काम करना बेहतर होता है। प्री-सी ++ 20 विकल्प हैं boost::multiprecision::lsb

संपादित करें:

स्ट्राइकथ्रू लिंक पर बेंचमार्क इस तथ्य से सीमित था कि यूरी फेल्डमैन संस्करण के लिए कोई पॉपकाउंट निर्देश नहीं छोड़ा गया था। के साथ अपने पीसी पर उन्हें संकलित करने की कोशिश कर -march=westmereरहा है, मैंने निम्नलिखित समय को 1 बिलियन पुनरावृत्तियों के लिए समान अनुक्रमों से मापा है std::mt19937:

  • आपका संस्करण: 5.7 एस
  • कामिलुक का दूसरा संस्करण: 4.7 एस
  • मेरा संस्करण: 4.7 s
  • एरिक पोस्टपिसिल का पहला संस्करण: 4.3 एस
  • यूरी फेल्डमैन का संस्करण (स्पष्ट रूप से उपयोग करते हुए __builtin_popcount): 4.1 एस

तो, कम से कम मेरी वास्तुकला पर, सबसे तेज़ पॉपकाउंट वाला लगता है।

2 संपादित करें:

मैंने नए एरिक पोस्टपिसिल के संस्करण के साथ अपना बेंचमार्क अपडेट किया है। टिप्पणियों में अनुरोध के अनुसार, मेरे परीक्षण का कोड यहां पाया जा सकता है । मैंने PRNG द्वारा आवश्यक समय का अनुमान लगाने के लिए एक नो-ऑप लूप जोड़ा है। मैंने केविनजेड द्वारा दो संस्करण भी जोड़े हैं। कोड -O3 -msse4 -mbmiप्राप्त करने popcntऔर blsiनिर्देश (पीटर कॉर्ड्स के लिए धन्यवाद) के साथ क्लैंग पर संकलित किया गया है ।

परिणाम: कम से कम मेरे आर्किटेक्चर पर, एरिक पोस्टपिसिल का संस्करण यूरी फेल्डमैन के एक के समान तेज़ है, और अब तक प्रस्तावित किसी भी अन्य संस्करण की तुलना में कम से कम दो बार तेज है।


मैंने एक ऑपरेशन निकाल दिया return (x & x + (x & -x)) == 0;:।
एरिक पोस्टपिसिल

3
यह @ एरिक के संस्करण के पुराने संस्करण को बेंचमार्क कर रहा है, है ना? वर्तमान संस्करण के साथ, एरिक को कुछ निर्देशों के रूप में gcc -O3 -march=nehalem(पॉपकान उपलब्ध कराने के लिए) या बीएमआई 1 के blsiलिए उपलब्ध है x & -x: godbolt.org/z/zuyj_f । और निर्देश सभी सरल एकल-यूओपी हैं, popcntयूरी के संस्करण को छोड़कर, जिसमें 3 चक्र विलंबता है। (लेकिन मुझे लगता है कि आप थ्रूपुट पर दबाव डाल रहे थे।) मेरा यह भी मानना ​​है कि आपने and valयूरी से हटा दिया होगा या यह धीमा होगा।
पीटर कॉर्ड्स

2
इसके अलावा, आपने किस हार्डवेयर पर बेंचमार्क किया? अपने पूरे बेंचमार्क कोड को गॉडबोल्ट या किसी अन्य चीज़ से जोड़ना एक अच्छा विचार होगा, इसलिए भविष्य के पाठक आसानी से अपने C ++ कार्यान्वयन पर परीक्षण कर सकते हैं।
पीटर कॉर्ड्स

2
आपको @ केविनज के संस्करण का परीक्षण भी करना चाहिए; यह बीएमआई 1 के बिना भी कम निर्देशों का अनुपालन करता है (कम से कम क्लैंग के साथ; जीसीसी का गैर-इन-वर्जन संस्करण एक बेकार है movऔर इसका लाभ उठाने में विफल रहता है lea): godbolt.org/z/5jeQLQ । BMI1 के साथ , एरिक का संस्करण अभी भी x86-64 पर बेहतर है, कम से कम इंटेल पर जहां blsiएक एकल यूओपी है, लेकिन यह एएमडी पर 2 यूओपी है।
पीटर कॉर्ड्स

15

उपवास के बारे में निश्चित नहीं है, लेकिन यह सत्यापित करके एक-लाइनर कर सकते हैं कि val^(val>>1)कम से कम 2 बिट्स हैं।

यह केवल अहस्ताक्षरित प्रकारों के साथ काम करता है: 0शीर्ष पर (तार्किक शिफ्ट) में स्थानांतरण आवश्यक है, न कि अंकगणित दाएं शिफ्ट जो साइन बिट की एक कॉपी में शिफ्ट होता है।

#include <bitset>
bool has_compact_bits(unsigned val)
{
    return std::bitset<8*sizeof(val)>((val ^ (val>>1))).count() <= 2;
}

अस्वीकार करने के लिए 0(अर्थात केवल उन इनपुटों को स्वीकार करें जिनके पास 1 सन्निहित बिट-समूह है), तार्किक-और valगैर-शून्य होने के साथ। इस सवाल पर अन्य जवाब 0कॉम्पैक्ट के रूप में स्वीकार करते हैं ।

bool has_compact_bits(unsigned val)
{
    return std::bitset<8*sizeof(val)>((val ^ (val>>1))).count() <= 2 and val;
}

C ++ पोस्‍केन्‍ट के माध्‍यम से std::bitset::count()या C ++ 20 के माध्‍यमstd::popcount से पॉपकॉर्न को उजागर करता है । C के पास अभी भी एक पोर्टेबल तरीका नहीं है जो मज़बूती से पॉपकंट या ऐसे ही निर्देशों को संकलित करता है जहाँ लक्ष्य उपलब्ध हो।


2
साथ ही सबसे तेज, अब तक।
गियोवन्नी सेरेटानी

2
मुझे लगता है कि आपको यह सुनिश्चित करने के लिए कि आप साइन बिट की प्रतियां नहीं, शून्य में शिफ्ट करने के लिए एक अहस्ताक्षरित प्रकार का उपयोग करने की आवश्यकता है। विचार करें 11011111। अंकगणित सही स्थानांतरित हो गया, यह बन गया 11101111, और XOR है 00110000। तार्किक सही बदलाव ( 0शीर्ष पर शिफ्टिंग ) के साथ, आप 10110000कई बिट समूहों का पता लगाते हैं और सही ढंग से पता लगाते हैं। जिसे ठीक करने के लिए संपादन किया जा रहा है।
पीटर कॉर्ड्स

3
यह वास्तव में चतुर है। जितना मैं शैली को नापसंद करता हूं (आईएमओ सिर्फ उपयोग करता है __builtin_popcount(), आजकल हर कंपाइलर में एक आदिम है), यह अब तक का सबसे तेज (आधुनिक सीपीयू पर) है। वास्तव में, मैं यह तर्क देने जा रहा हूं कि यह प्रस्तुति गंभीर रूप से मायने रखती है, क्योंकि एक ऐसे सीपीयू पर, जिसके पास एक निर्देश के रूप में पीओपीसीएनटी नहीं है, मेरे कार्यान्वयन को यह हरा सकता है। इसलिए, यदि आप इस कार्यान्वयन का उपयोग करने जा रहे हैं, तो आपको केवल आंतरिक उपयोग करना चाहिए। std::bitsetएक भयानक इंटरफ़ेस है।
केविनजेड

9

सीपीयू के पास इसके लिए समर्पित निर्देश हैं, बहुत तेजी से। पीसी पर वे बीएसआर / बीएसएफ (1985 में 80386 में पेश किए गए) हैं, एआरएम पर वे सीएलजेड / सीटीजेड हैं

कम से कम महत्वपूर्ण सेट बिट के सूचकांक को खोजने के लिए एक का उपयोग करें, पूर्णांक को उस राशि से दाईं ओर स्थानांतरित करें। सबसे महत्वपूर्ण सेट बिट के एक सूचकांक को खोजने के लिए दूसरे का उपयोग करें, अपने पूर्णांक की तुलना करें (1u << (bsr + 1) - 1 के साथ।

दुर्भाग्य से, हार्डवेयर से मिलान करने के लिए C ++ भाषा को अपडेट करने के लिए 35 वर्ष पर्याप्त नहीं थे। C ++ के इन निर्देशों का उपयोग करने के लिए आपको आंतरिक की आवश्यकता होगी, ये पोर्टेबल नहीं हैं, और परिणाम थोड़ा भिन्न स्वरूप में हैं। #ifdefसंकलक का पता लगाने के लिए प्रीप्रोसेसर, आदि का उपयोग करें, और फिर उपयुक्त आंतरिक का उपयोग करें। MSVC में वे कर रहे हैं _BitScanForward, _BitScanForward64, _BitScanReverse, _BitScanReverse64। जीसीसी और क्लैंग में वे हैं __builtin_clzऔर __builtin_ctz


2
AMD64 के लिए संकलन करते समय @ e2-e4 विज़ुअल स्टूडियो इनलाइन असेंबली का समर्थन नहीं करता है। इसलिए मैं आंतरिक सलाह देता हूं।
सूत

5
C ++ 20 के बाद से हैं std::countr_zeroऔर std::countl_zero। यदि आप बूस्ट का उपयोग कर रहे हैं, तो इसमें पोर्टेबल रैपर boost::multiprecision::lsbऔर कहा जाता है boost::multiprecision::msb
जियोवानी सेरेटानी

8
यह मेरे प्रश्न का बिल्कुल भी जवाब नहीं देता है - मुझे आश्चर्य है कि इसे कोई अपवित्र क्यों मिला
वाल्टर

3
@Walter आपका क्या मतलब है "जवाब नहीं"? मैंने ठीक उत्तर दिया है कि आपको क्या करना चाहिए, प्रीप्रोसेसर और फिर इंट्रिनिक्स का उपयोग करें।
सूट्स

2
जाहिर तौर पर C ++ 20 अंत में बिट-स्कैन, पॉपकाउंट और रोटेट के साथ #include <bit> en.cppreference.com/w/cpp/header/bit जोड़ रहा है । यह दयनीय है कि बिट-स्कैन को आंशिक रूप से उजागर करने में यह लंबा समय लगा, लेकिन अब पहले से बेहतर है। (पोर्टेबल popcnt उपलब्ध के माध्यम से किया गया है std::bitset::count()।) सी ++ 20 अभी भी कुछ चीजें हैं जो जंग प्रदान करता है (याद आ रही है doc.rust-lang.org/std/primitive.i32.html ), उदाहरण के लिए थोड़ा-रिवर्स और endian जो कुछ सीपीयू कुशलता प्रदान लेकिन सब नहीं। एक ऑपरेशन के लिए एक पोर्टेबल बिल्टइन जो किसी भी सीपीयू के पास कुछ समझ में आता है, हालांकि उपयोगकर्ताओं को यह जानने की आवश्यकता है कि क्या तेज है।
पीटर कॉर्ड्स

7

लोगों के बजाय शून्य के साथ तुलना करने से कुछ ऑपरेशन बचेंगे:

bool has_compact_bits2(int val) {
    if (val == 0) return true;
    int h = __builtin_clz(val);
    // Clear bits to the left
    val = (unsigned)val << h;
    int l = __builtin_ctz(val);
    // Invert
    // >>l - Clear bits to the right
    return (~(unsigned)val)>>l == 0;
}

निम्न निर्देशों में एक परिणाम कम तो gcc10 -O3x86_64 पर ऊपर और साइन एक्सटेंशन पर उपयोग करता है:

bool has_compact_bits3(int val) {
    if (val == 0) return true;
    int h = __builtin_clz(val);
    val <<= h;
    int l = __builtin_ctz(val);
    return ~(val>>l) == 0;
}

गॉडबॉल्ट पर परीक्षण किया गया


दुर्भाग्य से, यह कोई पोर्टेबल नहीं है। मुझे हमेशा डर लगता है कि मुझे ऑपरेटर की गलतियाँ उन शिफ्ट ऑपरेटरों के साथ गलत लगीं - क्या आपको यकीन ~val<<h>>h>>l == 0है कि आपको लगता है कि यह क्या करता है?
वाल्टर

4
हां, मुझे यकीन है, संपादित और जोड़ा ब्रेसिज़ वैसे भी। Och, तो आप एक पोर्टेबल समाधान में रुचि रखते हैं? क्योंकि मैंने there exists a faster way?कुछ भी देखा और मान लिया।
कामिलुक

5

आप आवश्यकता को दोहरा सकते हैं:

  • उन बी की संख्या सेट करें जो पिछले वाले से अलग हैं (बिट्स के माध्यम से पुनरावृत्ति करके)
  • यदि N = 2 और पहला या अंतिम बिट 0 है तो उत्तर हां है
  • यदि N = 1 है तो उत्तर हां है (क्योंकि सभी 1s एक तरफ हैं)
  • यदि N = 0 तब और कोई भी बिट 0 है तो आपके पास कोई 1s नहीं है, यदि आप उत्तर को हां या नहीं मानते हैं
  • कुछ और: जवाब नहीं है

सभी बिट्स के माध्यम से जाने से ऐसा लग सकता है:

unsigned int count_bit_changes (uint32_t value) {
  unsigned int bit;
  unsigned int changes = 0;
  uint32_t last_bit = value & 1;
  for (bit = 1; bit < 32; bit++) {
    value = value >> 1;
    if (value & 1 != last_bit  {
      changes++;
      last_bit = value & 1;
    }
  }
  return changes;
}

लेकिन यह निश्चित रूप से अनुकूलित किया जा सकता है (उदाहरण के लिए forलूप को रोककर जब valueतक पहुंच जाता है, 0जिसका अर्थ है कि मूल्य 1 के साथ और अधिक महत्वपूर्ण बिट्स मौजूद नहीं हैं)।


3

आप गणना का यह क्रम कर सकते हैं ( valइनपुट के रूप में मानते हुए ):

uint32_t x = val;
x |= x >>  1;
x |= x >>  2;
x |= x >>  4;
x |= x >>  8;
x |= x >> 16;

उन सभी शून्य के साथ एक संख्या प्राप्त करने के लिए जो सबसे महत्वपूर्ण 1हैं।

आप y = val & -valकम से कम 1 बिट में val(उदाहरण के लिए, 7 & -7 == 1और 12 & -12 == 4) को छोड़कर सभी पट्टी करने के लिए भी गणना कर सकते हैं ।
चेतावनी: यह विफल हो जाएगा val == INT_MIN, इसलिए आपको इस मामले को अलग से संभालना होगा, लेकिन यह तत्काल है।

फिर yएक स्थान पर राइट-शिफ्ट करके, वास्तविक LSB से थोड़ा नीचे आने के लिए val, और उसी तरह की दिनचर्या करें x:

uint32_t y = (val & -val) >> 1;
y |= y >>  1;
y |= y >>  2;
y |= y >>  4;
y |= y >>  8;
y |= y >> 16;

तो फिर x - yया x & ~yया x ^ y'कॉम्पैक्ट' बिट मुखौटा की पूरी लंबाई में फैले पैदा करता है val। बस यह valदेखने के लिए तुलना करें कि valक्या 'कॉम्पैक्ट' है।


2

हम जाँच करने के लिए gcc बिलिन निर्देशों का उपयोग कर सकते हैं :

सेट बिट्स की गिनती

int __builtin_popcount (अहस्ताक्षरित int x) x
में 1-बिट्स की संख्या लौटाता है।

के बराबर है (- ए):

a : उच्चतम सेट बिट (32 - CTZ) का सूचकांक (32 क्योंकि अहस्ताक्षरित पूर्णांक में 32 बिट्स)।

int __builtin_clz (अहस्ताक्षरित int x)
अग्रणी 0-बिट्स की संख्या को x में लौटाता है, जो सबसे महत्वपूर्ण बिट स्थिति पर शुरू होता है। यदि x 0 है, तो परिणाम अपरिभाषित है।

बी : सबसे कम सेट बिट (सीएलजेड) का सूचकांक:

int __builtin_clz (अहस्ताक्षरित int x)
अग्रणी 0-बिट्स की संख्या को x में लौटाता है, जो सबसे महत्वपूर्ण बिट स्थिति पर शुरू होता है। यदि x 0 है, तो परिणाम अपरिभाषित है।

उदाहरण के लिए यदि n = 0b0001100110; हम popcount के साथ 4 प्राप्त करेंगे लेकिन सूचकांक अंतर (a - b) 6 वापस आ जाएगा।

bool has_contiguous_one_bits(unsigned n) {
    return (32 - __builtin_clz(n) - __builtin_ctz(n)) == __builtin_popcount(n);
}

जिसे इस प्रकार भी लिखा जा सकता है:

bool has_contiguous_one_bits(unsigned n) {
    return (__builtin_popcount(n) + __builtin_clz(n) + __builtin_ctz(n)) == 32;
}

मुझे नहीं लगता कि यह वर्तमान उत्थान वाले उत्तर की तुलना में अधिक सुरुचिपूर्ण या कुशल है:

return (x & x + (x & -x)) == 0;

निम्नलिखित विधानसभा के साथ:

mov     eax, edi
neg     eax
and     eax, edi
add     eax, edi
test    eax, edi
sete    al

लेकिन यह समझना आसान है।


1

ठीक है, यहाँ एक संस्करण है जो बिट्स पर लूप करता है

template<typename Integer>
inline constexpr bool has_compact_bits(Integer val) noexcept
{
    Integer test = 1;
    while(!(test & val) && test) test<<=1; // skip unset bits to find first set bit
    while( (test & val) && test) test<<=1; // skip set bits to find next unset bit
    while(!(test & val) && test) test<<=1; // skip unset bits to find an offending set bit
    return !test;
}

पहले दो छोरों को पहला कॉम्पैक्ट क्षेत्र मिला। अंतिम लूप यह जांचता है कि उस क्षेत्र से परे कोई अन्य सेट बिट है या नहीं।

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