32-बिट पूर्णांक में सेट बिट्स की संख्या की गणना कैसे करें?


868

संख्या 7 का प्रतिनिधित्व करने वाले 8 बिट इस प्रकार हैं:

00000111

तीन बिट सेट हैं।

32-बिट पूर्णांक में सेट बिट्स की संख्या निर्धारित करने के लिए एल्गोरिदम क्या हैं?


101
यह हैमिंग का वजन BTW है।
पूर्वाफैड्स

11
इसके लिए एक वास्तविक दुनिया एप्लिकेशन क्या है? (यह एक आलोचना के रूप में नहीं लिया जाना चाहिए - मैं सिर्फ उत्सुक हूं।)
20

8
समता बिट की गणना (इसे देखें), जिसका उपयोग संचार में सरल त्रुटि का पता लगाने के लिए किया गया था।
डायलेक्टिकस

8
@ डायलेक्टिकस, एक समता बिट की गणना हेमिंग वजन की गणना करने की तुलना में सस्ता है
फाइननव

15
@spookyjon मान लीजिए कि आपके पास एक आसन्न मैट्रिक्स के रूप में दर्शाया गया ग्राफ है, जो अनिवार्य रूप से एक सा सेट है। यदि आप एक शीर्ष के किनारों की संख्या की गणना करना चाहते हैं, तो यह बिट सेट में एक पंक्ति के हेमिंग वजन की गणना करने के लिए उबलता है।
फ़ूज

जवाबों:


849

इसे ' हैमिंग वेट ', 'पॉपकाउंट' या 'साइड शिवाय' के रूप में जाना जाता है ।

'सर्वश्रेष्ठ' एल्गोरिथ्म वास्तव में निर्भर करता है कि आप किस सीपीयू पर हैं और आपका उपयोग पैटर्न क्या है।

कुछ सीपीयू में ऐसा करने के लिए एक ही अंतर्निहित अनुदेश होता है और अन्य में समानांतर निर्देश होते हैं जो बिट वैक्टर पर कार्य करते हैं। समानांतर निर्देश (जैसे x86 की popcnt, सीपीयू पर जहां यह समर्थित है) लगभग निश्चित रूप से सबसे तेज होगा। कुछ अन्य आर्किटेक्चर में एक माइक्रोकोडेड लूप के साथ धीमी गति से निर्देश दिया जा सकता है जो प्रति चक्र ( आवश्यक उद्धरण ) का थोड़ा परीक्षण करता है ।

यदि आपके CPU में एक बड़ी कैश और / या आप एक तंग लूप में इन निर्देशों के बहुत सारे कर रहे हैं तो एक पूर्व-आबादी वाली टेबल लुकअप विधि बहुत तेज़ हो सकती है। हालांकि यह एक 'कैश मिस' की कीमत के कारण पीड़ित हो सकता है, जहां सीपीयू को मुख्य मेमोरी से कुछ टेबल प्राप्त करना है। (तालिका को छोटा रखने के लिए प्रत्येक बाइट को अलग से देखें।)

यदि आप जानते हैं कि आपकी बाइट्स ज्यादातर 0 या अधिकतर 1 हैं तो इन परिदृश्यों के लिए बहुत ही कुशल एल्गोरिदम हैं।

मेरा मानना ​​है कि एक बहुत अच्छा सामान्य उद्देश्य एल्गोरिथ्म निम्नलिखित है, जिसे 'समानांतर' या 'चर-सटीक SWAR एल्गोरिथ्म' के रूप में जाना जाता है। मैंने इसे C- जैसी छद्म भाषा में व्यक्त किया है, आपको इसे किसी विशेष भाषा के लिए काम करने के लिए समायोजित करने की आवश्यकता हो सकती है (जैसे जावा में C ++ और >>> के लिए uint32_t का उपयोग करना):

int numberOfSetBits(uint32_t i)
{
     // Java: use int, and use >>> instead of >>
     // C or C++: use uint32_t
     i = i - ((i >> 1) & 0x55555555);
     i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
     return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
}

जावास्क्रिप्ट के लिए: प्रदर्शन के लिए पूर्णांक के |0लिए मोटे तौर पर : पहली पंक्ति को बदल देंi = (i|0) - ((i >> 1) & 0x55555555);

इसमें चर्चा किए गए किसी भी एल्गोरिदम का सबसे खराब मामला है, इसलिए आप इसे उपयोग करने वाले किसी भी उपयोग पैटर्न या मूल्यों से कुशलतापूर्वक निपटेंगे।


यह SWAR बिथक कैसे काम करता है:

i = i - ((i >> 1) & 0x55555555);

पहला कदम विषम / सम बिट को अलग करने के लिए मास्किंग का एक अनुकूलित संस्करण है, उन्हें ऊपर और ऊपर जोड़ने के लिए स्थानांतरण। यह प्रभावी रूप से 2-बिट संचयकों में 16 अलग-अलग परिवर्धन करता है ( एक रजिस्टर के भीतर SWAR = SIMD )। की तरह (i & 0x55555555) + ((i>>1) & 0x55555555)

अगला कदम उन 16x 2-बिट संचयकों के विषम / आठ को भी लेता है और फिर से जोड़ता है, जो 8x 4-बिट रकम का उत्पादन करता है। इस बार i - ...अनुकूलन संभव नहीं है, इसलिए यह स्थानांतरण से पहले / बाद में सिर्फ मास्क करता है। शिफ्ट 0x33...करने से 0xccc...पहले एक ही निरंतर दोनों समय का उपयोग करना ISIS के लिए संकलन करते समय एक अच्छी बात है कि अलग-अलग रजिस्टरों में 32-बिट स्थिरांक का निर्माण करने की आवश्यकता होती है।

(i + (i >> 4)) & 0x0F0F0F0F4x 8-बिट संचयकों के लिए चौड़ाई का अंतिम बदलाव और ऐड-स्टेप । यह पहले के बजाय जोड़ने के बाद मास्क करता है , क्योंकि किसी भी 4-बिट संचायक में अधिकतम मूल्य है 4, यदि संबंधित इनपुट बिट्स के सभी 4 बिट्स सेट किए गए थे। 4 + 4 = 8 जो अभी भी 4 बिट्स में फिट बैठता है, इसलिए निबल तत्वों के बीच ले जाना असंभव है i + (i >> 4)

अब तक यह सिर्फ काफी सामान्य SIMD है जिसमें कुछ चतुर अनुकूलन के साथ SWAR तकनीकों का उपयोग किया गया है। 2 और चरणों के लिए एक ही पैटर्न के साथ जारी रहना 2x 16-बिट और फिर 1x 32-बिट काउंट को चौड़ा कर सकता है। लेकिन तेजी से हार्डवेयर वाले मशीनों पर अधिक कुशल तरीका है:

एक बार जब हमारे पास कुछ पर्याप्त "तत्व" होते हैं, तो एक जादू स्थिरांक के साथ एक गुणा सभी तत्वों को शीर्ष तत्व में जोड़ सकता है । इस मामले में बाइट तत्वों। गुणा-भाग बाएं-शिफ्टिंग और जोड़ द्वारा किया जाता है, इसलिए परिणामों का एक गुणा x * 0x01010101होता है x + (x<<8) + (x<<16) + (x<<24) हमारे 8-बिट तत्व पर्याप्त विस्तृत हैं (और छोटे पर्याप्त मात्रा में पकड़) है कि यह उस शीर्ष 8 बिट में ले जाने का उत्पादन नहीं करता है ।

इसका 64-बिट संस्करण 0x0101010101010101 गुणक के साथ 64-बिट पूर्णांक में 8x 8-बिट तत्व कर सकता है, और साथ उच्च बाइट को निकाल सकता है >>56। इसलिए यह कोई अतिरिक्त कदम नहीं उठाता है, बस व्यापक स्थिरांक है। __builtin_popcountllहार्डवेयर popcntनिर्देश सक्षम नहीं होने पर x86 सिस्टम पर GCC का उपयोग यही होता है । यदि आप इसके लिए बिल्डिंस या इंट्रिनिक्स का उपयोग कर सकते हैं, तो कंपाइलर को लक्ष्य-विशिष्ट अनुकूलन करने का मौका दें।


व्यापक वैक्टर के लिए पूर्ण SIMD के साथ (उदाहरण के लिए एक पूरी सरणी की गिनती)

SIMD के साथ सीपीयू पर स्पीडअप के लिए एक बिट पूर्णांक रजिस्टर के बजाय यह बिटवाइड-स्वार एल्गोरिदम एक ही बार में कई वेक्टर तत्वों में किया जा सकता है। (उदाहरण के लिए x86-64 कोड जिसे किसी भी सीपीयू पर चलना है, न कि केवल नेहम या बाद में।

हालांकि, पॉपकाउंट के लिए वेक्टर निर्देशों का उपयोग करने का सबसे अच्छा तरीका आमतौर पर समानांतर में प्रत्येक बाइट के समय 4 बिट्स के लिए टेबल-लुकअप करने के लिए एक चर-फेरबदल का उपयोग करके होता है। (एक वेक्टर रजिस्टर में आयोजित 4 बिट्स इंडेक्स 16 एंट्री टेबल)।

इंटेल सीपीयू पर, हार्डवेयर 64 बिट पॉपकांट अनुदेश 2 के कारक के बारे में एक एसएसएसई 3 PSHUFBबिट-समानांतर कार्यान्वयन को बेहतर बना सकता है , लेकिन केवल तभी जब आपका कंपाइलर इसे सही तरीके से प्राप्त करता है । अन्यथा SSE काफी आगे आ सकता है। नए संकलक संस्करण इंटेल पर पॉपकंट झूठी निर्भरता समस्या से अवगत हैं ।

संदर्भ:


87
हा! NumberOfSetBits () फ़ंक्शन से प्यार करें, लेकिन कोड समीक्षा के माध्यम से सौभाग्य प्राप्त हो रहा है। :-)
जेसन एस

37
शायद इसका उपयोग करना चाहिए unsigned int, आसानी से यह दिखाने के लिए कि यह किसी भी संकेत बिट जटिलताओं से मुक्त है। यह uint32_tभी सुरक्षित होगा , जैसा कि, आप सभी प्लेटफार्मों पर क्या उम्मीद करते हैं?
क्रेग मैकक्वीन

35
@nonnb: वास्तव में, जैसा कि लिखा गया है, कोड छोटी गाड़ी है और रखरखाव की आवश्यकता है। >>नकारात्मक मूल्यों के लिए कार्यान्वयन-परिभाषित है। तर्क को बदलने (या कास्ट) करने की आवश्यकता है unsigned, और चूंकि कोड 32-बिट-विशिष्ट है, इसलिए संभवतः इसका उपयोग किया जाना चाहिएuint32_t
आर .. गिटहब स्टॉप हेल्पिंग आईसीई

6
यह वास्तव में जादू नहीं है। यह बिट्स के सेट को जोड़ रहा है लेकिन कुछ चतुर अनुकूलन के साथ ऐसा कर रहा है। उत्तर में दी गई विकिपीडिया लिंक यह बताने का अच्छा काम करती है कि क्या हो रहा है लेकिन मैं लाइन से जाऊंगा। 1) बिट्स की हर जोड़ी में बिट्स की संख्या की गणना करें, उस गिनती को बिट्स के जोड़े में रखें (आपके पास 00, 01, या 10 होगा); "चतुर" बिट यहाँ घटाव है जो एक मुखौटा से बचा जाता है। 2) बिटमैप्स के उन जोड़ के जोड़े को उनके संगत निबल्स में जोड़ें; यहां कुछ भी चालाक नहीं है, लेकिन प्रत्येक कुतरने का मूल्य 0-4 होगा। (cont'd)
डैश-टॉम-बैंग

8
एक और ध्यान दें, यह केवल 64 और 128 बिट रजिस्टरों को विस्तारपूर्वक स्थिरांक रूप से बढ़ाता है। दिलचस्प है (मेरे लिए), उन स्थिरांक भी हैं ~ 0/3, 5, 17, और 255; पूर्व तीन जा रहा है 2 ^ n + 1। यह सब अधिक समझ में आता है जितना अधिक आप इसे घूरते हैं और शॉवर में इसके बारे में सोचते हैं। :)
डैश-टॉम-बैंग

214

अपने संकलक के अंतर्निहित कार्यों पर भी विचार करें।

उदाहरण के लिए जीएनयू संकलक पर आप बस उपयोग कर सकते हैं:

int __builtin_popcount (unsigned int x);
int __builtin_popcountll (unsigned long long x);

सबसे खराब स्थिति में कंपाइलर एक फ़ंक्शन पर कॉल उत्पन्न करेगा। सबसे अच्छा मामले में कंपाइलर समान कार्य को तेजी से करने के लिए एक सीपीयू निर्देश का उत्सर्जन करेगा।

GCC आंतरिक भी कई प्लेटफार्मों में काम करते हैं। पॉपकाउंट x86 आर्किटेक्चर में मुख्यधारा बन जाएगा, इसलिए यह अब आंतरिक उपयोग करना शुरू करने के लिए समझ में आता है। अन्य आर्किटेक्चर के पास सालों से पॉपकाउंट है।


X86 पर, आप संकलक को बता सकते हैं कि वह popcntनिर्देश के साथ समर्थन के लिए -mpopcntया मान सकता है-msse4.2 उसी पीढ़ी में जोड़े गए वेक्टर निर्देशों को भी सक्षम कर सकता है। GCC x86 विकल्प देखें । -march=nehalem(या -march=जो भी सीपीयू आप चाहते हैं कि आपका कोड मान लें और उसके लिए ट्यून करें) एक अच्छा विकल्प हो सकता है। पुराने CPU पर परिणामी बाइनरी को चलाने से अवैध-अनुदेश दोष हो जाएगा।

बायनेरिज़ को आपके द्वारा बनाई गई मशीन के लिए अनुकूलित करने के लिए, उपयोग -march=native (जीसीसी, क्लैंग या आईसीसी के साथ)।

MSVC x86 के लिए एक आंतरिक प्रदान करता है popcnt निर्देश के , लेकिन gcc के विपरीत यह वास्तव में हार्डवेयर निर्देश के लिए एक आंतरिक है और हार्डवेयर समर्थन की आवश्यकता है।


का उपयोग करते हुए std::bitset<>::count()एक अंतर्निहित के बजाय का

सिद्धांत रूप में, कोई भी संकलक जो यह जानता है कि लक्ष्य सीपीयू के लिए कुशलतापूर्वक पॉपकाउंट कैसे करना है, आईएसओ सी ++ के माध्यम से उस कार्यक्षमता को उजागर करना चाहिए std::bitset<> । व्यवहार में, आप कुछ लक्ष्य CPU के लिए कुछ मामलों में बिट-हैक और / शिफ्ट / ADD के साथ बेहतर हो सकते हैं।

लक्ष्य आर्किटेक्चर के लिए जहां हार्डवेयर पॉपकाउंट एक वैकल्पिक एक्सटेंशन है (जैसे x86), सभी कंपाइलरों में ऐसा नहीं होता है std::bitsetजो उपलब्ध होने पर इसका लाभ उठाते हैं। उदाहरण के लिए, MSVC के पास popcntसंकलन समय पर समर्थन सक्षम करने का कोई तरीका नहीं है , और हमेशा टेबल लुकअप का उपयोग करता है , यहां तक ​​कि /Ox /arch:AVX(जिसका अर्थ है SSE4.2, हालांकि तकनीकी रूप से इसके लिए एक अलग फीचर बिट है popcnt।)

लेकिन कम से कम आपको कुछ पोर्टेबल मिलता है जो हर जगह काम करता है, और सही लक्ष्य विकल्पों के साथ gcc / clang के साथ, आपको आर्किटेक्चर के लिए हार्डवेयर पॉपकाउंट मिलता है जो इसका समर्थन करते हैं।

#include <bitset>
#include <limits>
#include <type_traits>

template<typename T>
//static inline  // static if you want to compile with -mpopcnt in one compilation unit but not others
typename std::enable_if<std::is_integral<T>::value,  unsigned >::type 
popcount(T x)
{
    static_assert(std::numeric_limits<T>::radix == 2, "non-binary type");

    // sizeof(x)*CHAR_BIT
    constexpr int bitwidth = std::numeric_limits<T>::digits + std::numeric_limits<T>::is_signed;
    // std::bitset constructor was only unsigned long before C++11.  Beware if porting to C++03
    static_assert(bitwidth <= std::numeric_limits<unsigned long long>::digits, "arg too wide for std::bitset() constructor");

    typedef typename std::make_unsigned<T>::type UT;        // probably not needed, bitset width chops after sign-extension

    std::bitset<bitwidth> bs( static_cast<UT>(x) );
    return bs.count();
}

देख Godbolt कंपाइलर एक्सप्लोरर पर gcc, clang, icc और MSVC से asm

x86-64 gcc -O3 -std=gnu++11 -mpopcnt उत्सर्जन करता है:

unsigned test_short(short a) { return popcount(a); }
    movzx   eax, di      # note zero-extension, not sign-extension
    popcnt  rax, rax
    ret
unsigned test_int(int a) { return popcount(a); }
    mov     eax, edi
    popcnt  rax, rax
    ret
unsigned test_u64(unsigned long long a) { return popcount(a); }
    xor     eax, eax     # gcc avoids false dependencies for Intel CPUs
    popcnt  rax, rdi
    ret

PowerPC64 gcc -O3 -std=gnu++11उत्सर्जन (के लिए)int arg संस्करण के लिए):

    rldicl 3,3,0,32     # zero-extend from 32 to 64-bit
    popcntd 3,3         # popcount
    blr

यह स्रोत x86- विशिष्ट या GNU- विशिष्ट बिल्कुल नहीं है, लेकिन केवल g86 / clang / icc के साथ x86 के लिए अच्छी तरह से संकलित है।

यह भी ध्यान दें कि सिंगल-इंस्ट्रक्शन पॉपकॉइन के बिना आर्किटेक्चर के लिए gcc का कमबैक एक बाइट-ए-ए-टाइम टेबल लुकअप है। उदाहरण के लिए, यह ARM के लिए अद्भुत नहीं है ।


5
मैं मानता हूं कि यह सामान्य रूप से अच्छा अभ्यास है, लेकिन XCode / OSX / Intel पर मैंने पाया कि यहां पर सुझाए गए अधिकांश सुझावों की तुलना में यह धीमी कोड उत्पन्न करता है। विवरण के लिए मेरा उत्तर देखें।

5
Intel i5 / i7 में SSE4 निर्देश POPCNT है जो इसे करता है, सामान्य उद्देश्य रजिस्टरों का उपयोग करता है। मेरे सिस्टम पर जीसीसी इस आंतरिक का उपयोग करके उस अनुदेश का उत्सर्जन नहीं करता है, मुझे लगता है कि अभी तक नो-मोर्च = नेहेल्म विकल्प के कारण।
१४:३१ बजे मटका

3
@ मटजा, मेरी जीसीसी 4.4.1 पॉपकंट अनुदेश का उत्सर्जन करती है अगर मैं -msse4.2 के साथ संकलित करता हूं
निल्स पिपेनब्रिंक

74
c ++ का उपयोग करें std::bitset::count। एक एकल __builtin_popcountकॉल करने के लिए इस संकलन के बाद ।
deft_code

1
@nlucaroni खैर, हाँ। समय बदल रहा है। मैंने इसका जवाब 2008 में लिखा है। आजकल हमारे पास देशी पॉपकॉर्न हैं और आंतरिक एक एकल कोडांतरक वक्तव्य के लिए संकलित करेगा यदि प्लेटफ़ॉर्म अनुमति देता है।
निल्स पिपेनब्रिनक

183

मेरी राय में, "सबसे अच्छा" समाधान वह है जिसे किसी अन्य प्रोग्रामर (या मूल प्रोग्रामर को दो साल बाद) द्वारा बिना किसी टिप्पणी के पढ़ा जा सकता है। आप अच्छी तरह से सबसे तेज़ या चतुर समाधान चाहते हैं जो कुछ पहले ही प्रदान कर चुके हैं लेकिन मैं किसी भी समय चतुराई पर पठनीयता पसंद करता हूं।

unsigned int bitCount (unsigned int value) {
    unsigned int count = 0;
    while (value > 0) {           // until all bits are zero
        if ((value & 1) == 1)     // check lower bit
            count++;
        value >>= 1;              // shift bits, removing lower bit
    }
    return count;
}

यदि आप अधिक गति चाहते हैं (और अपने उत्तराधिकारियों की मदद करने के लिए इसे अच्छी तरह से दस्तावेज मान लेते हैं), तो आप टेबल लुकअप का उपयोग कर सकते हैं:

// Lookup table for fast calculation of bits set in 8-bit unsigned char.

static unsigned char oneBitsInUChar[] = {
//  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F (<- n)
//  =====================================================
    0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, // 0n
    1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, // 1n
    : : :
    4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, // Fn
};

// Function for fast calculation of bits set in 16-bit unsigned short.

unsigned char oneBitsInUShort (unsigned short x) {
    return oneBitsInUChar [x >>    8]
         + oneBitsInUChar [x &  0xff];
}

// Function for fast calculation of bits set in 32-bit unsigned int.

unsigned char oneBitsInUInt (unsigned int x) {
    return oneBitsInUShort (x >>     16)
         + oneBitsInUShort (x &  0xffff);
}

हालांकि ये विशिष्ट डेटा प्रकार आकारों पर निर्भर करते हैं, इसलिए वे पोर्टेबल नहीं हैं। लेकिन, चूंकि कई प्रदर्शन अनुकूलन वैसे भी पोर्टेबल नहीं हैं, इसलिए यह एक मुद्दा नहीं हो सकता है। यदि आप पोर्टेबिलिटी चाहते हैं, तो मैं पठनीय समाधान पर टिकूंगा।


21
2 से विभाजित करने और "शिफ्ट बिट्स ..." के रूप में टिप्पणी करने के बजाय, आपको बस शिफ्ट ऑपरेटर (>>) का उपयोग करना चाहिए और टिप्पणी को छोड़ देना चाहिए।
indiv

9
इसे बदलना अधिक मतलब नहीं होगा if ((value & 1) == 1) { count++; }के साथ count += value & 1?
पोंकाडूडल

21
नहीं, सबसे अच्छा समाधान इस मामले में सबसे अधिक पठनीय नहीं है। यहाँ सबसे अच्छा एल्गोरिथ्म सबसे तेज़ है।
NikiC

21
यह पूरी तरह से आपकी राय है, @ मिनिक, हालांकि आप मुझे नीचा दिखाने के लिए स्वतंत्र हैं, जाहिर है। इस सवाल में कोई उल्लेख नहीं था कि "सर्वश्रेष्ठ" को कैसे निर्धारित किया जाए, "प्रदर्शन" या "तेज" शब्द कहीं नहीं देखे जा सकते हैं। इसलिए मैंने पठनीय का विकल्प चुना।
paxdiablo

3
मैं इस उत्तर को 3 साल बाद पढ़ रहा हूं, और मुझे यह सबसे अच्छा उत्तर लगता है क्योंकि यह पठनीय है और इसमें अधिक टिप्पणियां हैं। अवधि।
वाका-वाका-वाका

98

हैकर डिलाइट से, पी। 66, चित्रा 5-2

int pop(unsigned x)
{
    x = x - ((x >> 1) & 0x55555555);
    x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
    x = (x + (x >> 4)) & 0x0F0F0F0F;
    x = x + (x >> 8);
    x = x + (x >> 16);
    return x & 0x0000003F;
}

~ 20-ish निर्देश (आर्क आश्रित) में निष्पादित, कोई शाखा नहीं।

हैकर डिलाइट प्रसन्न है ! अत्यधिक सिफारिशित।


8
जावा विधि Integer.bitCount(int)इसी सटीक कार्यान्वयन का उपयोग करती है।
मार्को बोलिस

इसके बाद थोड़ी परेशानी होना - अगर हम 32-बिट के बजाय केवल 16-बिट वैल्यू की परवाह करें तो यह कैसे बदलेगा?
जेरेमी ब्लम

हो सकता है कि हैकर्स का आनंद सुखद हो, लेकिन मैं popइसके बजाय population_count(या pop_cntयदि आपके पास एक संक्षिप्त विवरण होना चाहिए) को कॉल करने के लिए एक अच्छा किकिंग देना होगा । @MarcoBolis मैं मानता हूं कि जावा के सभी संस्करणों के बारे में सही होगा, लेकिन आधिकारिक तौर पर यह कार्यान्वयन पर निर्भर होगा :)
Maarten Bodewes

और, इसके लिए स्वीकृत उत्तर में कोड की तरह कोई गुणन की आवश्यकता नहीं है।
एलेक्स

ध्यान दें कि 64-बिट को सामान्य करने में समस्या है। मास्क के कारण परिणाम 64 नहीं हो सकता है।
अल्बर्ट वैन डेर होर्स्ट

76

मुझे लगता है कि सबसे तेज़ तरीका है - लुकअप टेबल और पॉपकाउंट का उपयोग किए बिना - निम्नलिखित। यह सिर्फ 12 ऑपरेशन के साथ सेट बिट्स को गिनता है।

int popcount(int v) {
    v = v - ((v >> 1) & 0x55555555);                // put count of each 2 bits into those 2 bits
    v = (v & 0x33333333) + ((v >> 2) & 0x33333333); // put count of each 4 bits into those 4 bits  
    return c = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;
}

यह काम करता है क्योंकि आप सेट बिट्स की कुल संख्या को दो हिस्सों में विभाजित करके, दोनों हिस्सों में सेट बिट्स की संख्या की गणना कर सकते हैं और फिर उन्हें जोड़ सकते हैं। Divide and Conquerप्रतिमान के रूप में भी जानते हैं । आइये विस्तार से ..

v = v - ((v >> 1) & 0x55555555); 

दो बिट्स में बिट्स की संख्या हो सकती है 0b00, 0b01या 0b10। 2 बिट पर यह काम करने की कोशिश करता है ..

 ---------------------------------------------
 |   v    |   (v >> 1) & 0b0101   |  v - x   |
 ---------------------------------------------
   0b00           0b00               0b00   
   0b01           0b00               0b01     
   0b10           0b01               0b01
   0b11           0b01               0b10

यह वही था जो आवश्यक था: अंतिम कॉलम हर दो बिट जोड़ी में सेट बिट्स की गिनती दिखाता है। दो बिट संख्या है, तो >= 2 (0b10)उसके बाद andपैदा करता है 0b01, और यह पैदा करता है 0b00

v = (v & 0x33333333) + ((v >> 2) & 0x33333333); 

इस कथन को समझना आसान होना चाहिए। पहले ऑपरेशन के बाद हमारे पास हर दो बिट्स में सेट बिट्स की गिनती है, अब हम उस गणना को हर 4 बिट्स में जोड़ते हैं।

v & 0b00110011         //masks out even two bits
(v >> 2) & 0b00110011  // masks out odd two bits

फिर हम उपरोक्त परिणाम को जोड़ते हैं, जिससे हमें सेट बिट्स की कुल संख्या 4 बिट्स में मिलती है। अंतिम कथन सबसे कठिन है।

c = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;

चलिए इसे आगे तोड़ते हैं ...

v + (v >> 4)

यह दूसरे कथन के समान है; हम इसके बजाय 4 के समूहों में सेट बिट्स की गिनती कर रहे हैं। हम जानते हैं- हमारे पिछले ऑपरेशनों के कारण- कि प्रत्येक कुतरने में सेट बिट्स की गिनती होती है। आइए एक उदाहरण देखें। मान लीजिए कि हमारे पास बाइट है 0b01000010। इसका मतलब है कि पहले कुतरने का अपना 4bit सेट है और दूसरे का अपना 2bit सेट है। अब हम उन निबल्स को एक साथ जोड़ते हैं।

0b01000010 + 0b01000000

यह हमें एक बाइट में सेट बिट्स की गिनती देता है, पहली निबल में 0b01100010और इसलिए हम संख्या में सभी बाइट्स के अंतिम चार बाइट्स (उन्हें त्यागते हुए) को मुखौटा करते हैं।

0b01100010 & 0xF0 = 0b01100000

अब हर बाइट में सेट बिट्स की गिनती है। हमें उन सभी को एक साथ जोड़ने की जरूरत है। चाल से परिणाम गुणा करना है0b10101010 जिसके पास एक दिलचस्प संपत्ति है। यदि हमारी संख्या में चार बाइट्स हैं, A B C Dतो इन बाइट्स के साथ एक नई संख्या होगी A+B+C+D B+C+D C+D D। एक 4 बाइट संख्या में अधिकतम 32 बिट सेट हो सकते हैं, जिन्हें इस रूप में दर्शाया जा सकता है0b00100000

अब हमें केवल पहले बाइट की आवश्यकता है, जिसमें सभी बाइट्स में सभी सेट बिट्स का योग है, और हम इसे प्राप्त करते हैं >> 24। इस एल्गोरिथ्म को 32 bitशब्दों के लिए डिज़ाइन किया गया था लेकिन इसे 64 bitशब्दों के लिए आसानी से संशोधित किया जा सकता है ।


के c = बारे में क्या है ? लगता है जैसे खत्म किया जाना चाहिए। इसके अलावा, कुछ क्लासिक चेतावनियों से बचने के लिए एक अतिरिक्त पैरेन सेट A (((v + (v >> 4))) & 0xF0F0F0F) * 0x1010101) >> 24 "सुझाएं।
chux -

4
एक महत्वपूर्ण विशेषता यह है कि यह 32-बिट रूटीन दोनों के लिए काम करता है popcount(int v)और popcount(unsigned v)। पोर्टेबिलिटी के लिए, विचार करें popcount(uint32_t v), आदि वास्तव में * 0x1010101 भाग की तरह।
chux -

चटनी ? (पुस्तक, लिंक, इनवर्टर के नाम आदि) का बहुत स्वागत किया जाएगा। क्योंकि तब हम अपने कोडबस में टिप्पणी के साथ यह कह सकते हैं कि यह कहां से आता है।
v.oddou

1
मुझे लगता है कि बेहतर स्पष्टता के लिए अंतिम पंक्ति के रूप में लिखा जाना चाहिए: return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;इसलिए हमें यह देखने के लिए अक्षरों को गिनने की आवश्यकता नहीं है कि आप वास्तव में क्या कर रहे हैं (क्योंकि आपने पहली बार खारिज कर दिया था 0, मैंने गलती से सोचा था कि आपने गलत (फ़्लिप) बिट पैटर्न को मुखौटा के रूप में इस्तेमाल किया है। - जब तक मैंने नोट किया कि केवल 7 अक्षर हैं और 8 नहीं)।
एएम

यही कारण है कि गुणा 0x01010101 द्वारा धीमी गति से हो सकता है, प्रोसेसर पर निर्भर करता है। उदाहरण के लिए, मेरे पुराने पावरबुक जी 4 में, 1 गुणन 4 परिवर्धन जितना धीमा था (विभाजन जितना बुरा नहीं था, जहाँ 1 विभाजन लगभग 23 परिवर्तियों जितना धीमा था)।
जॉर्ज कोहेलर

54

मैं ऊब गया, और तीन तरीकों के एक अरब पुनरावृत्तियों को समयबद्ध किया। कंपाइलर gcc -O3 है। सीपीयू जो कुछ भी वे 1 जीन मैकबुक प्रो में डालते हैं।

सबसे तेज़ निम्नलिखित है, 3.7 सेकंड में:

static unsigned char wordbits[65536] = { bitcounts of ints between 0 and 65535 };
static int popcount( unsigned int i )
{
    return( wordbits[i&0xFFFF] + wordbits[i>>16] );
}

दूसरी जगह एक ही कोड जाता है लेकिन 2 हाफ़ पासवर्ड के बजाय 4 बाइट्स देखना। जिसमें लगभग 5.5 सेकंड लगे।

तीसरे स्थान पर बिट-ट्विडलिंग 'साइड शिवाय' अप्रोच जाता है, जिसमें 8.6 सेकंड लगे।

चौथा स्थान शर्मनाक 11 सेकंड में जीसीसी के __builtin_popcount () में जाता है।

एक-एक-बार के दृष्टिकोण की गिनती वायाए धीमी थी, और मैं इसके पूरा होने की प्रतीक्षा में ऊब गया था।

इसलिए यदि आप अन्य सभी से ऊपर के प्रदर्शन की परवाह करते हैं तो पहले दृष्टिकोण का उपयोग करें। यदि आप परवाह करते हैं, लेकिन इस पर 64Kb RAM खर्च करने के लिए पर्याप्त नहीं है, तो दूसरे दृष्टिकोण का उपयोग करें। अन्यथा पठनीय (लेकिन धीमी गति से) एक-बिट-ए-टाइम दृष्टिकोण का उपयोग करें।

ऐसी स्थिति के बारे में सोचना मुश्किल है जहां आप बिट-ट्विडलिंग दृष्टिकोण का उपयोग करना चाहते हैं।

संपादित करें: यहां समान परिणाम ।


49
@ माइक, तालिका आधारित दृष्टिकोण अपराजेय है यदि तालिका कैश में है। यह माइक्रो-बेंचमार्क में होता है (जैसे तंग पाश में लाखों परीक्षण)। हालांकि, एक कैश मिस में लगभग 200 चक्र होते हैं, और यहां तक ​​कि सबसे भोली पॉपकाउंट भी तेज होगी। यह हमेशा आवेदन पर निर्भर करता है।
निल्स पिपेनब्रिनक

10
यदि आप इस नियम को कुछ समय के लिए तंग लूप में नहीं कह रहे हैं, तो आपके पास इसके प्रदर्शन के बारे में परवाह करने का कोई कारण नहीं है, और प्रदर्शन हानि के नगण्य होने के बाद भी भोले-भाले लेकिन पढ़ने योग्य दृष्टिकोण का उपयोग कर सकते हैं। और FWIW, 8bit LUT 10-20 कॉल के भीतर कैश-हॉट हो जाता है।

6
मुझे नहीं लगता कि यह सब उस स्थिति की कल्पना करना कठिन है जहां यह विधि से किया गया एक पत्ता है, जो आपके ऐप में भारी-भरकम लिफ्टिंग कर रहा है। इस पर निर्भर करता है कि और क्या चल रहा है (और थ्रेडिंग) छोटा संस्करण जीत सकता है। एल्गोरिदम के बहुत सारे लिखा गया है कि संदर्भ की बेहतर स्थानीयता के कारण अपने साथियों को हरा देते हैं। यह भी क्यों नहीं?
जेसन

क्लैंग के साथ यह कोशिश करो, यह बिलिनों को लागू करने में काफी समझदार है।
मैट जॉइनर

3
जब तक -msse4.2 के साथ कॉल नहीं किया जाता है, तब तक GCC पॉपकॉर्न इंस्ट्रक्शन का उत्सर्जन नहीं करेगा, जो कि 'बग़ल के अलावा' से तेज़ है।
लवेला

54

यदि आप जावा, बिल्ट-इन विधि का उपयोग कर रहे हैं Integer.bitCount ऐसा करेगी।


जब सूरज ने अलग-अलग एपीआई प्रदान किए, तो उसे पृष्ठभूमि पर कुछ तर्क का उपयोग करना चाहिए, है ना?
वल्लभ पाटड़े

2
एक साइड नोट के रूप में, जावा के कार्यान्वयन केविन लिटिल द्वारा बताए गए एक ही एल्गोरिदम का उपयोग करता है ।
मार्को बोलिस

2
एक तरफ कार्यान्वयन, यह संभवतः आपके बाद आपके कोड को बनाए रखने वाले डेवलपर्स के लिए इरादे का सबसे स्पष्ट संदेश है (या जब आप 6 महीने बाद वापस आते हैं)
divillysausages

31
unsigned int count_bit(unsigned int x)
{
  x = (x & 0x55555555) + ((x >> 1) & 0x55555555);
  x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
  x = (x & 0x0F0F0F0F) + ((x >> 4) & 0x0F0F0F0F);
  x = (x & 0x00FF00FF) + ((x >> 8) & 0x00FF00FF);
  x = (x & 0x0000FFFF) + ((x >> 16)& 0x0000FFFF);
  return x;
}

मुझे इस एल्गोरिथ्म की व्याख्या करें।

यह एल्गोरिथ्म डिवाइड और कॉनकॉर एल्गोरिदम पर आधारित है। मान लीजिए कि एक 8bit पूर्णांक 213 (बाइनरी में 11010101) है, एल्गोरिथ्म इस तरह काम करता है (प्रत्येक बार दो पड़ोसी ब्लॉकों को मिलाता है):

+-------------------------------+
| 1 | 1 | 0 | 1 | 0 | 1 | 0 | 1 |  <- x
|  1 0  |  0 1  |  0 1  |  0 1  |  <- first time merge
|    0 0 1 1    |    0 0 1 0    |  <- second time merge
|        0 0 0 0 0 1 0 1        |  <- third time ( answer = 00000101 = 5)
+-------------------------------+

7
यह एल्गोरिथ्म मैट होवल्स का संस्करण है, इस तथ्य के अनुकूल होने से पहले कि यह अपठनीय हो गया था।
लेटरिस ई

29

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

इनलाइन int pop2 (अहस्ताक्षरित x, अहस्ताक्षरित y)
{
    x = x - (x (1 >> 1) & 0x55555555);
    y = y - (y (1 >> 1) & 0x55555555);
    x = (x & 0x33333333) + ((x >> 2) और 0x33333333);
    y = (y & 0x33333333) + ((y >> 2) & 0x33333333);
    x = (x + (x >> 4)) और 0x0F0F0F0F;
    y = (y + (y >> 4)) & 0x0F0F0F0F;
    x = x + (x >> 8);
    y = y + (y >> 8);
    x = x + (x >> 16);
    y = y + (y >> 16);
    वापसी (x + y) & 0x000000FF;
}

अनमैक्ड हैकर की डिलाईट ने 12.2 गिगासायकल ले लिया। मेरा समानांतर संस्करण (दो बार कई बिट्स की गिनती) 13.0 गीगासाइकल में चलता है। एक 2.4GHz Core Duo पर एक साथ दोनों के लिए कुल 10.5s बीत गए। इस घड़ी की आवृत्ति पर 25 गीगासाइकल = केवल 10 सेकंड से अधिक, इसलिए मुझे विश्वास है कि मेरी टाइमिंग सही है।

यह अनुदेश निर्भरता श्रृंखलाओं के साथ करना है, जो इस एल्गोरिथ्म के लिए बहुत खराब हैं। मैं 64-बिट रजिस्टरों की एक जोड़ी का उपयोग करके फिर से गति को दोगुना कर सकता हूं। वास्तव में, अगर मैं चालाक था और कुछ ही समय में x + ya थोड़ा जल्दी जोड़ा तो मैं कुछ बदलाव कर सकता था। 64-बिट संस्करण कुछ छोटे ट्वीक्स के बारे में भी बाहर आ जाएगा, लेकिन दो बार फिर से कई बिट्स गिनें।

128 बिट SIMD रजिस्टरों के साथ, अभी तक दो का एक और कारक, और SSE निर्देश सेट में अक्सर चतुर शॉर्ट-कट भी होते हैं।

कोड के विशेष रूप से पारदर्शी होने का कोई कारण नहीं है। इंटरफ़ेस सरल है, एल्गोरिथ्म को कई स्थानों पर ऑन-लाइन संदर्भित किया जा सकता है, और यह व्यापक इकाई परीक्षण के लिए उत्तरदायी है। प्रोग्रामर जो इस पर ठोकर खाता है वह शायद कुछ सीख भी सकता है। ये बिट ऑपरेशन मशीन स्तर पर बेहद स्वाभाविक हैं।

ठीक है, मैंने तय 64-बिट संस्करण को बेंच दिया। इसके लिए एक आकार (लंबे समय तक अहस्ताक्षरित) == (

इनलाइन int pop2 (अहस्ताक्षरित लंबी x, अहस्ताक्षरित लंबी y)
{
    x = x - (x (1 >> 1) & 0x5555555555555555);
    y = y - (y (1 >> 1) & 0x5555555555555555);
    x = (x & 0x3333333333333333) + ((x >> 2) और 0x3333333333333333);
    y = (y & 0x3333333333333333) + ((y >> 2) & 0x3333333333333333);
    x = (x + (x >> 4)) & 0x0F0F0F0F0F0F0F0F0F;
    y = (y + (y >> 4)) & 0x0F0F0F0F0F0F0F0F0F;
    x = x + y; 
    x = x + (x >> 8);
    x = x + (x >> 16);
    x = x + (x >> 32); 
    वापसी x & 0xFF;
}

यह सही के बारे में लग रहा है (मैं ध्यान से परीक्षण नहीं कर रहा हूं, हालांकि)। अब समय 10.70 गीगासाइकल / 14.1 गिगासाइकल पर निकलता है। बाद में यह संख्या 128 बिलियन बिट्स हो गई और इस मशीन पर 5.9 की संख्या से मेल खाती है। गैर-समानांतर संस्करण एक छोटे बिट को गति देता है क्योंकि मैं 64-बिट मोड में चल रहा हूं और यह 32-बिट रजिस्टरों की तुलना में 64-बिट रजिस्टरों को थोड़ा बेहतर पसंद करता है।

चलो देखते हैं कि यहाँ होने के लिए थोड़ा और OOO पाइपलाइनिंग है या नहीं। यह थोड़ा अधिक शामिल था, इसलिए मैंने वास्तव में थोड़ा परीक्षण किया। प्रत्येक शब्द अकेले 64 तक बैठता है, सभी संयुक्त राशि 256 है।

इनलाइन इंट पॉप 4 (अहस्ताक्षरित लंबी x, अहस्ताक्षरित लंबी y, 
                अहस्ताक्षरित लंबा यू, अहस्ताक्षरित लंबा वी)
{
  enum {m1 = 0x555555555555555555, 
         एम 2 = 0x3333333333333333, 
         m3 = 0x0F0F0F0F0F0F0F0FF, 
         m4 = 0x000000FF000000FF};

    x = x - (x (1 >> 1) & m1);
    y = y - (y (1 >> 1) & m1);
    u = u - (u (1 >> 1) & m1);
    v = v - (v (1 >> 1) & m1);
    x = (x & m2) + ((x >> 2) और m2);
    y = (y & m2) + ((y >> 2) & m2);
    यू = (यू एंड एम 2) + ((यू >> 2) और एम 2);
    वी = (वी एंड एम 2) + ((वी >> 2) और एम 2);
    x = x + y; 
    u = u + v; 
    x = (x & m3) + ((x >> 4) & m3);
    u = (u & m3) + ((u >> 4) & m3);
    x = x + u; 
    x = x + (x >> 8);
    x = x + (x >> 16);
    x = x & m4; 
    x = x + (x >> 32);
    वापसी x & 0x000001FF;
}

मैं एक पल के लिए उत्साहित था, लेकिन यह पता चला है कि जीसी इनलाइन चालें खेल रहा है -O3 के साथ भले ही मैं कुछ परीक्षणों में इनलाइन कीवर्ड का उपयोग नहीं कर रहा हूं। जब मैंने gcc चालें चलने दीं, तो एक बिल पॉप 4 () में 12.56 गीगासाइकल लगती हैं, लेकिन मैंने निर्धारित किया कि यह निरंतर अभिव्यक्तियों के रूप में तर्क दे रहा था। एक अधिक यथार्थवादी संख्या एक और 30% गति-अप के लिए 19.6gc प्रतीत होती है। मेरा परीक्षण लूप अब इस तरह दिखता है, यह सुनिश्चित करते हुए कि प्रत्येक तर्क अलग है कि जीसीसी को चालें चलाने से रोकने के लिए पर्याप्त है।

   हिटाइम b4 = rdtsc (); 
   के लिए (अहस्ताक्षरित लंबे i = 10L * 1000 * 1000 * 1000; मैं <11L * 1000 * 1000 * 1000; ++ i; 
      sum + = pop4 (i, i ^ 1, ~ i, i | 1); 
   hitime e4 = rdtsc (); 

256 बिलियन बिट्स 8.17s में समाप्‍त हो गए। 16-बिट टेबल लुकअप में बेंचमार्क के रूप में 32 मिलियन बिट्स के लिए 1.02s तक काम करता है। सीधे तुलना नहीं कर सकते, क्योंकि दूसरी बेंच घड़ी की गति नहीं देती है, लेकिन ऐसा लगता है कि मैंने 64KB टेबल संस्करण से स्नॉट को थप्पड़ मारा है, जो पहली जगह में एल 1 कैश का एक दुखद उपयोग है।

अपडेट: स्पष्ट करने का फैसला किया और पॉप 6 () को चार और डुप्लिकेट लाइनों को जोड़कर बनाया। 22.8gc तक आया, 384 बिलियन बिट्स 9.5 के दशक में समाप्‍त हुआ। तो 32 बिलियन बिट्स के लिए 800ms पर अब एक और 20% है।


2
इस तरह का सबसे अच्छा नॉन-असेंबलर फॉर्म मैंने एक समय में 24 32bit शब्दों को अनियंत्रित करके देखा है। dalkescientific.com/writings/diary/popcnt.c , stackoverflow.com/questions/3693981/… , dalkescientific.com/writings/diary/archive/2008/07/05/…
मैट जॉइनर

28

क्यों नहीं 2 से विभाजित?

गिनती = ०
जबकि n> 0
  if (n% 2) == 1
    गिनती + = १
  n / = 2  

मैं मानता हूं कि यह सबसे तेज नहीं है, लेकिन "सबसे अच्छा" कुछ अस्पष्ट है। मेरा तर्क है कि हालांकि "सर्वश्रेष्ठ" में स्पष्टता का एक तत्व होना चाहिए


यह काम करेगा और समझने में आसान है, लेकिन तेज़ तरीके हैं।
मैट हॉवेल्स

2
जब तक आप इस एक कर बहुत , प्रदर्शन प्रभाव नगण्य होगा। इसलिए सभी चीजें समान हैं, मैं डेनियल से सहमत हूं कि 'सर्वश्रेष्ठ' का तात्पर्य "जिबरिश की तरह नहीं पढ़ा जाता" है।

2
मैंने जानबूझकर विभिन्न तरीकों को प्राप्त करने के लिए 'सर्वश्रेष्ठ' को परिभाषित नहीं किया है। अगर हम इस तरह के बिट-ट्विडलिंग के स्तर तक उतर गए हैं, तो हम इसका सामना कर सकते हैं, हम शायद कुछ उबेर-तेज की तलाश कर रहे हैं जो कि चिंप की तरह दिखता है।
मैट हॉवेल्स

6
बुरा कोड। एक कंपाइलर इसमें से अच्छा बना सकता है, लेकिन मेरे परीक्षणों में जीसीसी नहीं था। बदलें (n% 2) के साथ (n & 1); और MODULO की तुलना में बहुत तेज है। बदलें (एन / = 2) के साथ (एन >> = 1); विभाजन की तुलना में बहुत तेजी से बिटशफ्टिंग।
मिकी

6
@ मीकी: मेरे परीक्षणों में, जीसीसी (4.0, -ओ 3) ने स्पष्ट अनुकूलन किया।

26

जब आप बिट पैटर्न लिखते हैं तो हैकर डिलाइट बिट-टिडलिंग बहुत स्पष्ट हो जाता है।

unsigned int bitCount(unsigned int x)
{
  x = ((x >> 1) & 0b01010101010101010101010101010101)
     + (x       & 0b01010101010101010101010101010101);
  x = ((x >> 2) & 0b00110011001100110011001100110011)
     + (x       & 0b00110011001100110011001100110011); 
  x = ((x >> 4) & 0b00001111000011110000111100001111)
     + (x       & 0b00001111000011110000111100001111); 
  x = ((x >> 8) & 0b00000000111111110000000011111111)
     + (x       & 0b00000000111111110000000011111111); 
  x = ((x >> 16)& 0b00000000000000001111111111111111)
     + (x       & 0b00000000000000001111111111111111); 
  return x;
}

पहला चरण विषम बिट्स को सम जोड़ता है, प्रत्येक दो में बिट्स का योग बनाता है। अन्य चरणों में उच्च-क्रम वाले विखंडू को कम-क्रम वाले विखंडू में जोड़ दिया जाता है, चंक के आकार को दोगुना कर दिया जाता है, जब तक कि हमारे पास अंतिम अंक पूरे इंट को नहीं लेते।


3
इस समाधान में छोटी सी समस्या है, ऑपरेटर की पूर्ववर्तीता से संबंधित है। प्रत्येक पद के लिए यह कहना चाहिए: x = (((x >> 1) & 0b0101010101010101010101010101010101) + (x & 0b0101010101010101010101010101010101); (यानी अतिरिक्त परिजनों को जोड़ा गया)।
नोपिक

21

एक खुश मध्यम के लिए एक 2 32 लुकअप टेबल और प्रत्येक बिट के माध्यम से व्यक्तिगत रूप से पुनरावृति:

int bitcount(unsigned int num){
    int count = 0;
    static int nibblebits[] =
        {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};
    for(; num != 0; num >>= 4)
        count += nibblebits[num & 0x0f];
    return count;
}

से http://ctips.pbwiki.com/CountBits


पोर्टेबल नहीं है। क्या होगा अगर CPU में 9 बिट बाइट्स हैं? हाँ, वहाँ असली सीपीयू की तरह है कि वहाँ रहे हैं ...
रॉबर्ट एस। बार्न्स

15
@ रोबर्ट एस। बार्न्स, यह फ़ंक्शन अभी भी काम करेगा। यह मूल शब्द आकार के बारे में कोई धारणा नहीं बनाता है, और "बाइट्स" का कोई संदर्भ नहीं है।
finnw

19

इसमें किया जा सकता है O(k), जहां kबिट्स की संख्या निर्धारित है।

int NumberOfSetBits(int n)
{
    int count = 0;

    while (n){
        ++ count;
        n = (n - 1) & n;
    }

    return count;
}

यह अनिवार्य रूप से ब्रायन कर्निघन है (उन्हें याद रखें?) एल्गोरिथ्म, मामूली बदलाव के साथ कि उन्होंने अधिक रसीला n &= (n-1)रूप का इस्तेमाल किया ।
एड्रियन मोल

17

यह सबसे तेज़ या सबसे अच्छा समाधान नहीं है, लेकिन मुझे अपने तरीके से एक ही सवाल मिला, और मैंने सोचना और सोचना शुरू कर दिया। अंत में मुझे एहसास हुआ कि यह इस तरह से किया जा सकता है यदि आप गणितीय तरफ से समस्या प्राप्त करते हैं, और एक ग्राफ खींचते हैं, तो आप पाते हैं कि यह एक फ़ंक्शन है जिसका कुछ आवधिक भाग है, और फिर आपको अवधियों के बीच के अंतर का एहसास होता है ... हेयर यू गो:

unsigned int f(unsigned int x)
{
    switch (x) {
        case 0:
            return 0;
        case 1:
            return 1;
        case 2:
            return 1;
        case 3:
            return 2;
        default:
            return f(x/4) + f(x%4);
    }
}

4
ओह मुझे पसंद है कैसे मुक्केबाज़ी अजगर संस्करण:def f(i, d={0:lambda:0, 1:lambda:1, 2:lambda:1, 3:lambda:2}): return d.get(i, lambda: f(i//4) + f(i%4))()
underrun

10

जिस फ़ंक्शन की आप तलाश कर रहे हैं, उसे अक्सर बाइनरी नंबर की "साइडव्यू सम" या "जनसंख्या गणना" कहा जाता है। नुथ पूर्व-फ़ासिकल 1 ए, पीपी 11-12 में इसकी चर्चा करता है (हालाँकि वॉल्यूम 2, 4.6.3- (7) में एक संक्षिप्त संदर्भ था।)

ठिकाना classicus पीटर वेग्नर के लेख "एक बाइनरी कम्प्यूटर में गिनती वाले के लिए एक तकनीक", से है एसीएम का संचार , खंड 3 (1960) संख्या 5, पेज 322 । वह वहां दो अलग-अलग एल्गोरिदम देता है, एक "विरल" होने के लिए अपेक्षित संख्याओं के लिए अनुकूलित (यानी, लोगों की एक छोटी संख्या है) और विपरीत मामले के लिए एक है।


10
  private int get_bits_set(int v)
    {
      int c; // c accumulates the total bits set in v
        for (c = 0; v>0; c++)
        {
            v &= v - 1; // clear the least significant bit set
        }
        return c;
    }

9

कुछ खुले प्रश्न: -

  1. यदि संख्या नकारात्मक है तो?
  2. यदि संख्या 1024 है, तो "पुनरावृति 2 से विभाजित करें" विधि 10 बार पुनरावृति करेगी।

हम नकारात्मक संख्या का समर्थन करने के लिए अहंकार को संशोधित कर सकते हैं: -

count = 0
while n != 0
if ((n % 2) == 1 || (n % 2) == -1
    count += 1
  n /= 2  
return count

अब दूसरी समस्या को दूर करने के लिए हम इस प्रकार लिख सकते हैं:

int bit_count(int num)
{
    int count=0;
    while(num)
    {
        num=(num)&(num-1);
        count++;
    }
    return count;
}

पूर्ण संदर्भ के लिए देखें:

http://goursaha.freeoda.com/Miscellaneous/IntegerBitCount.html


9

मुझे लगता है कि ब्रायन कर्निघन का तरीका भी उपयोगी होगा ... सेट बिट्स के रूप में यह कई पुनरावृत्तियों से गुजरता है। इसलिए यदि हमारे पास केवल उच्च बिट सेट के साथ 32-बिट शब्द है, तो यह केवल एक बार लूप के माध्यम से जाएगा।

int countSetBits(unsigned int n) { 
    unsigned int n; // count the number of bits set in n
    unsigned int c; // c accumulates the total bits set in n
    for (c=0;n>0;n=n&(n-1)) c++; 
    return c; 
}

1988 में प्रकाशित, सी प्रोग्रामिंग लैंग्वेज 2 एड। (ब्रायन डब्ल्यू। कर्निघन और डेनिस एम। रिची द्वारा) व्यायाम 2-9 में इसका उल्लेख है। 19 अप्रैल, 2006 को डॉन नुथ ने मुझे बताया कि यह विधि "पहली बार पीटर वेगनर ने सीएसीएम 3 (1960), 322 में प्रकाशित की थी। (डेरिक लेहमर द्वारा स्वतंत्र रूप से खोजी गई और 1964 में बेक्डबाख द्वारा संपादित पुस्तक में प्रकाशित हुई।")


8

मैं नीचे दिए गए कोड का उपयोग करता हूं जो अधिक सहज है।

int countSetBits(int n) {
    return !n ? 0 : 1 + countSetBits(n & (n-1));
}

तर्क: n & (n-1) n के अंतिम सेट बिट को रीसेट करता है।

पुनश्च: मुझे पता है कि यह ओ (1) समाधान नहीं है, हालांकि यह एक दिलचस्प समाधान है।


यह कम संख्या में बिट्स के साथ "विरल" संख्याओं के लिए अच्छा है, जैसा कि यह है O(ONE-BITS)। यह वास्तव में O (1) है क्योंकि अधिकांश 32 वन-बिट्स हैं।
इलफोंसो

7

"बेस्ट एल्गोरिथ्म" के साथ आपका क्या मतलब है? शॉर्ट कोड या फास्ट कोड? आपका कोड बहुत ही सुंदर लग रहा है और इसमें लगातार निष्पादन का समय है। कोड भी बहुत छोटा है।

लेकिन अगर गति प्रमुख कारक है और कोड आकार नहीं है, तो मुझे लगता है कि अनुसरण तेज हो सकता है:

       static final int[] BIT_COUNT = { 0, 1, 1, ... 256 values with a bitsize of a byte ... };
        static int bitCountOfByte( int value ){
            return BIT_COUNT[ value & 0xFF ];
        }

        static int bitCountOfInt( int value ){
            return bitCountOfByte( value ) 
                 + bitCountOfByte( value >> 8 ) 
                 + bitCountOfByte( value >> 16 ) 
                 + bitCountOfByte( value >> 24 );
        }

मुझे लगता है कि यह 64 बिट वैल्यू के लिए अधिक तेज नहीं होगा, लेकिन 32 बिट वैल्यू तेज हो सकती है।


मेरे कोड में 10 ऑपरेशन हैं। आपके कोड में 12 ऑपरेशन हैं। आपका लिंक छोटे सरणियों के साथ काम करता है (5)। मैं 256 तत्वों का उपयोग करता हूं। कैशिंग के साथ एक समस्या हो सकती है। लेकिन अगर आप इसका बहुत बार उपयोग करते हैं तो यह कोई समस्या नहीं है।
होरक्रक्स

यह दृष्टिकोण औसत रूप से बिट-ट्विडलिंग दृष्टिकोण की तुलना में काफी तेज है, क्योंकि यह निकलता है। अधिक मेमोरी का उपयोग करने के लिए, यह कम कोड को संकलित करता है और हर बार जब आप फ़ंक्शन को इनलाइन करते हैं तो यह लाभ दोहराया जाता है। तो यह आसानी से एक शुद्ध जीत हो सकती है।

7

मैंने लगभग 1990 में RISC मशीनों के लिए एक तेज बिटकॉइन मैक्रो लिखा। यह उन्नत अंकगणित (गुणा, भाग,%), मेमोरी लाने के तरीके (बहुत धीमी गति से), शाखाएं (बहुत धीमी गति से) का उपयोग नहीं करता है, लेकिन यह सीपीयू है मान 32-बिट बैरल शिफ्टर (दूसरे शब्दों में, >> 1 और 32) समान मात्रा में चक्र लेते हैं।) यह मानता है कि छोटे स्थिरांक (जैसे 6, 12, 24) को रजिस्टर में लोड करने के लिए कुछ भी खर्च नहीं होता है, या संग्रहीत किया जाता है। में और फिर से इस्तेमाल में लाते हैं।

इन मान्यताओं के साथ, यह लगभग 16 चक्रों में 32 बिट्स गिनता है / अधिकांश आरआईएससी मशीनों पर निर्देश। ध्यान दें कि 15 निर्देश / चक्र, चक्र या निर्देशों की संख्या से कम बाध्य है, क्योंकि ऐसा लगता है कि आधे में जोड़ की संख्या में कटौती करने के लिए कम से कम 3 निर्देश (मास्क, शिफ्ट, ऑपरेटर) लगते हैं, इसलिए log_2 (32) = 5, 5 x 3 = 15 निर्देश एक अर्ध-निम्नतर है।

#define BitCount(X,Y)           \
                Y = X - ((X >> 1) & 033333333333) - ((X >> 2) & 011111111111); \
                Y = ((Y + (Y >> 3)) & 030707070707); \
                Y =  (Y + (Y >> 6)); \
                Y = (Y + (Y >> 12) + (Y >> 24)) & 077;

यहाँ पहले और सबसे जटिल कदम के लिए एक रहस्य है:

input output
AB    CD             Note
00    00             = AB
01    01             = AB
10    01             = AB - (A >> 1) & 0x1
11    10             = AB - (A >> 1) & 0x1

इसलिए यदि मैं ऊपर 1 कॉलम (ए) लेता हूं, तो इसे सही 1 बिट में बदल दें, और इसे एबी से घटाएं, मुझे आउटपुट (सीडी) मिलता है। 3 बिट्स का विस्तार समान है; यदि आप चाहें तो इसे ऊपर की तरह एक 8-पंक्ति बूलियन टेबल के साथ चेक कर सकते हैं।

  • डॉन गिल्लीज

7

यदि आप C ++ का उपयोग कर रहे हैं तो एक अन्य विकल्प टेम्पलेट मेटाप्रोग्रामिंग का उपयोग करना है:

// recursive template to sum bits in an int
template <int BITS>
int countBits(int val) {
        // return the least significant bit plus the result of calling ourselves with
        // .. the shifted value
        return (val & 0x1) + countBits<BITS-1>(val >> 1);
}

// template specialisation to terminate the recursion when there's only one bit left
template<>
int countBits<1>(int val) {
        return val & 0x1;
}

उपयोग होगा:

// to count bits in a byte/char (this returns 8)
countBits<8>( 255 )

// another byte (this returns 7)
countBits<8>( 254 )

// counting bits in a word/short (this returns 1)
countBits<16>( 256 )

आप निश्चित रूप से विभिन्न प्रकारों (यहां तक ​​कि ऑटो-डिटेक्टिंग बिट आकार) का उपयोग करने के लिए इस टेम्पलेट का विस्तार कर सकते हैं, लेकिन मैंने इसे स्पष्टता के लिए सरल रखा है।

संपादित करें: यह उल्लेख करना भूल गया अच्छा है क्योंकि यह है चाहिए किसी भी C ++ कंपाइलर में काम और यह मूल रूप से आपके लूप को आपके लिए अनियंत्रित करता है यदि बिट काउंट (दूसरे शब्दों में, निरंतर मान का उपयोग किया जाता है , तो मुझे पूरा यकीन है कि यह सबसे तेज़ सामान्य विधि है तुम्हे पता चलेगा)


दुर्भाग्य से, बिट काउंटिंग समानांतर में नहीं की गई है, इसलिए यह शायद धीमी है। constexprहालांकि एक अच्छा बना सकते हैं ।
imallett

सहमत - यह C ++ टेम्पलेट पुनरावृत्ति में एक मजेदार अभ्यास था, लेकिन निश्चित रूप से एक सुंदर भोला समाधान था।
पैंटाफोब

6

मैं विशेष रूप से भाग्य फ़ाइल से इस उदाहरण के शौकीन हूँ:

#define BITCOUNT (x) (((BX_ (x) + (BX_ (x) >> 4)) और 0x0F0F0F0F)% 255)
#define BX_ (x) ((x) - (((x) >> 1) & 0x7777777)
                             - ((x) >> 2) और 0x33333333)
                             - ((x) >> 3) और 0x11111111)

मुझे यह सबसे अच्छा लगता है क्योंकि यह बहुत सुंदर है!


1
अन्य सुझावों की तुलना में यह कैसा प्रदर्शन करता है?
asdf

6

जावा JDK1.5

Integer.bitCount (एन);

जहाँ n वह संख्या है जिसके 1 को गिना जाना है।

यह भी देखें,

Integer.highestOneBit(n);
Integer.lowestOneBit(n);
Integer.numberOfLeadingZeros(n);
Integer.numberOfTrailingZeros(n);

//Beginning with the value 1, rotate left 16 times
     n = 1;
         for (int i = 0; i < 16; i++) {
            n = Integer.rotateLeft(n, 1);
            System.out.println(n);
         }

वास्तव में एक एल्गोरिथ्म नहीं, यह सिर्फ एक पुस्तकालय कॉल है। जावा के लिए उपयोगी, बाकी सब के लिए इतना नहीं।
बेंजोडा

2
@benzado सही है लेकिन +1 वैसे भी, क्योंकि कुछ जावा डेवलपर्स को इस विधि के बारे में जानकारी नहीं हो सकती है
'27

@finnw, मैं उन डेवलपर्स में से एक हूं। :)
neevek

6

मुझे SIMD अनुदेश (SSSE3 और AVX2) के उपयोग से एक सरणी में बिट काउंटिंग का कार्यान्वयन मिला। यदि यह __popcnt64 आंतरिक कार्य का उपयोग करेगा तो इसकी तुलना में 2-2.5 गुना बेहतर प्रदर्शन होता है।

SSSE3 संस्करण:

#include <smmintrin.h>
#include <stdint.h>

const __m128i Z = _mm_set1_epi8(0x0);
const __m128i F = _mm_set1_epi8(0xF);
//Vector with pre-calculated bit count:
const __m128i T = _mm_setr_epi8(0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4);

uint64_t BitCount(const uint8_t * src, size_t size)
{
    __m128i _sum =  _mm128_setzero_si128();
    for (size_t i = 0; i < size; i += 16)
    {
        //load 16-byte vector
        __m128i _src = _mm_loadu_si128((__m128i*)(src + i));
        //get low 4 bit for every byte in vector
        __m128i lo = _mm_and_si128(_src, F);
        //sum precalculated value from T
        _sum = _mm_add_epi64(_sum, _mm_sad_epu8(Z, _mm_shuffle_epi8(T, lo)));
        //get high 4 bit for every byte in vector
        __m128i hi = _mm_and_si128(_mm_srli_epi16(_src, 4), F);
        //sum precalculated value from T
        _sum = _mm_add_epi64(_sum, _mm_sad_epu8(Z, _mm_shuffle_epi8(T, hi)));
    }
    uint64_t sum[2];
    _mm_storeu_si128((__m128i*)sum, _sum);
    return sum[0] + sum[1];
}

AVX2 संस्करण:

#include <immintrin.h>
#include <stdint.h>

const __m256i Z = _mm256_set1_epi8(0x0);
const __m256i F = _mm256_set1_epi8(0xF);
//Vector with pre-calculated bit count:
const __m256i T = _mm256_setr_epi8(0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 
                                   0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4);

uint64_t BitCount(const uint8_t * src, size_t size)
{
    __m256i _sum =  _mm256_setzero_si256();
    for (size_t i = 0; i < size; i += 32)
    {
        //load 32-byte vector
        __m256i _src = _mm256_loadu_si256((__m256i*)(src + i));
        //get low 4 bit for every byte in vector
        __m256i lo = _mm256_and_si256(_src, F);
        //sum precalculated value from T
        _sum = _mm256_add_epi64(_sum, _mm256_sad_epu8(Z, _mm256_shuffle_epi8(T, lo)));
        //get high 4 bit for every byte in vector
        __m256i hi = _mm256_and_si256(_mm256_srli_epi16(_src, 4), F);
        //sum precalculated value from T
        _sum = _mm256_add_epi64(_sum, _mm256_sad_epu8(Z, _mm256_shuffle_epi8(T, hi)));
    }
    uint64_t sum[4];
    _mm256_storeu_si256((__m256i*)sum, _sum);
    return sum[0] + sum[1] + sum[2] + sum[3];
}

6

मैं हमेशा कॉम्पिटिटिव प्रोग्रामिंग में इसका उपयोग करता हूं और इसे लिखना और कुशल होना आसान है:

#include <bits/stdc++.h>

using namespace std;

int countOnes(int n) {
    bitset<32> b(n);
    return b.count();
}

5

सेट बिट्स की गणना करने के लिए कई एल्गोरिथ्म हैं; लेकिन मुझे लगता है कि सबसे अच्छा एक तेजी से एक है! आप इस पृष्ठ पर विस्तृत देख सकते हैं:

बिट ट्विडलिंग हैक्स

मैं यह सुझाव देता हूं:

64-बिट निर्देशों का उपयोग करके 14, 24 या 32-बिट शब्दों में सेट बिट्स की गिनती

unsigned int v; // count the number of bits set in v
unsigned int c; // c accumulates the total bits set in v

// option 1, for at most 14-bit values in v:
c = (v * 0x200040008001ULL & 0x111111111111111ULL) % 0xf;

// option 2, for at most 24-bit values in v:
c =  ((v & 0xfff) * 0x1001001001001ULL & 0x84210842108421ULL) % 0x1f;
c += (((v & 0xfff000) >> 12) * 0x1001001001001ULL & 0x84210842108421ULL) 
     % 0x1f;

// option 3, for at most 32-bit values in v:
c =  ((v & 0xfff) * 0x1001001001001ULL & 0x84210842108421ULL) % 0x1f;
c += (((v & 0xfff000) >> 12) * 0x1001001001001ULL & 0x84210842108421ULL) % 
     0x1f;
c += ((v >> 24) * 0x1001001001001ULL & 0x84210842108421ULL) % 0x1f;

इस विधि के लिए 64-बिट सीपीयू की आवश्यकता होती है जिसमें तेज मापांक विभाजन कुशल हो। पहला विकल्प केवल 3 संचालन लेता है; दूसरा विकल्प 10 लेता है; और तीसरा विकल्प 15 लेता है।


5

इनपुट आकार पर ब्रांचिंग के साथ बाइट बिट की पूर्व-गणना तालिका का उपयोग करके फास्ट सी # समाधान।

public static class BitCount
{
    public static uint GetSetBitsCount(uint n)
    {
        var counts = BYTE_BIT_COUNTS;
        return n <= 0xff ? counts[n]
             : n <= 0xffff ? counts[n & 0xff] + counts[n >> 8]
             : n <= 0xffffff ? counts[n & 0xff] + counts[(n >> 8) & 0xff] + counts[(n >> 16) & 0xff]
             : counts[n & 0xff] + counts[(n >> 8) & 0xff] + counts[(n >> 16) & 0xff] + counts[(n >> 24) & 0xff];
    }

    public static readonly uint[] BYTE_BIT_COUNTS = 
    {
        0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
        4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
    };
}

विडंबना यह है कि इस तालिका को इस थ्रेड में पोस्ट किए गए किसी भी एल्गोरिदम द्वारा बनाया जा सकता है! फिर भी, इस तरह तालिकाओं का उपयोग करने का अर्थ है निरंतर-समय प्रदर्शन। एक कदम आगे बढ़कर 64K अनुवाद तालिका बनाना इसलिए AND, SHIFT और ADD संचालन को आवश्यक बना देगा। बिट मैनिपुलेटर्स के लिए एक दिलचस्प विषय!
user924272

कैश समस्याओं के कारण बड़ी तालिकाएँ धीमी हो सकती हैं (और स्थिर-समय नहीं)। आप एक बार में 3 बिट्स देख सकते हैं (0xe994 >>(k*2))&3, मेमोरी एक्सेस के बिना ...
Greggo

5

यहां एक पोर्टेबल मॉड्यूल (ANSI-C) है जो आपके प्रत्येक एल्गोरिदम को किसी भी आर्किटेक्चर पर बेंचमार्क कर सकता है।

आपके CPU में 9 बिट बाइट्स हैं? कोई समस्या नहीं है :-) फिलहाल यह 2 एल्गोरिदम, के एंड आर एल्गोरिथ्म और एक बाइट वार लुकअप टेबल को लागू करता है। लुकअप तालिका K & R एल्गोरिथ्म की तुलना में औसत 3 गुना तेज है। अगर कोई "हैकर डिलाइट" एल्गोरिथ्म को पोर्टेबल बनाने के लिए इसे जोड़ने के लिए स्वतंत्र महसूस करने का एक तरीका समझ सकता है।

#ifndef _BITCOUNT_H_
#define _BITCOUNT_H_

/* Return the Hamming Wieght of val, i.e. the number of 'on' bits. */
int bitcount( unsigned int );

/* List of available bitcount algorithms.  
 * onTheFly:    Calculate the bitcount on demand.
 *
 * lookupTalbe: Uses a small lookup table to determine the bitcount.  This
 * method is on average 3 times as fast as onTheFly, but incurs a small
 * upfront cost to initialize the lookup table on the first call.
 *
 * strategyCount is just a placeholder. 
 */
enum strategy { onTheFly, lookupTable, strategyCount };

/* String represenations of the algorithm names */
extern const char *strategyNames[];

/* Choose which bitcount algorithm to use. */
void setStrategy( enum strategy );

#endif

#include <limits.h>

#include "bitcount.h"

/* The number of entries needed in the table is equal to the number of unique
 * values a char can represent which is always UCHAR_MAX + 1*/
static unsigned char _bitCountTable[UCHAR_MAX + 1];
static unsigned int _lookupTableInitialized = 0;

static int _defaultBitCount( unsigned int val ) {
    int count;

    /* Starting with:
     * 1100 - 1 == 1011,  1100 & 1011 == 1000
     * 1000 - 1 == 0111,  1000 & 0111 == 0000
     */
    for ( count = 0; val; ++count )
        val &= val - 1;

    return count;
}

/* Looks up each byte of the integer in a lookup table.
 *
 * The first time the function is called it initializes the lookup table.
 */
static int _tableBitCount( unsigned int val ) {
    int bCount = 0;

    if ( !_lookupTableInitialized ) {
        unsigned int i;
        for ( i = 0; i != UCHAR_MAX + 1; ++i )
            _bitCountTable[i] =
                ( unsigned char )_defaultBitCount( i );

        _lookupTableInitialized = 1;
    }

    for ( ; val; val >>= CHAR_BIT )
        bCount += _bitCountTable[val & UCHAR_MAX];

    return bCount;
}

static int ( *_bitcount ) ( unsigned int ) = _defaultBitCount;

const char *strategyNames[] = { "onTheFly", "lookupTable" };

void setStrategy( enum strategy s ) {
    switch ( s ) {
    case onTheFly:
        _bitcount = _defaultBitCount;
        break;
    case lookupTable:
        _bitcount = _tableBitCount;
        break;
    case strategyCount:
        break;
    }
}

/* Just a forwarding function which will call whichever version of the
 * algorithm has been selected by the client 
 */
int bitcount( unsigned int val ) {
    return _bitcount( val );
}

#ifdef _BITCOUNT_EXE_

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

/* Use the same sequence of pseudo random numbers to benmark each Hamming
 * Weight algorithm.
 */
void benchmark( int reps ) {
    clock_t start, stop;
    int i, j;
    static const int iterations = 1000000;

    for ( j = 0; j != strategyCount; ++j ) {
        setStrategy( j );

        srand( 257 );

        start = clock(  );

        for ( i = 0; i != reps * iterations; ++i )
            bitcount( rand(  ) );

        stop = clock(  );

        printf
            ( "\n\t%d psudoe-random integers using %s: %f seconds\n\n",
              reps * iterations, strategyNames[j],
              ( double )( stop - start ) / CLOCKS_PER_SEC );
    }
}

int main( void ) {
    int option;

    while ( 1 ) {
        printf( "Menu Options\n"
            "\t1.\tPrint the Hamming Weight of an Integer\n"
            "\t2.\tBenchmark Hamming Weight implementations\n"
            "\t3.\tExit ( or cntl-d )\n\n\t" );

        if ( scanf( "%d", &option ) == EOF )
            break;

        switch ( option ) {
        case 1:
            printf( "Please enter the integer: " );
            if ( scanf( "%d", &option ) != EOF )
                printf
                    ( "The Hamming Weight of %d ( 0x%X ) is %d\n\n",
                      option, option, bitcount( option ) );
            break;
        case 2:
            printf
                ( "Please select number of reps ( in millions ): " );
            if ( scanf( "%d", &option ) != EOF )
                benchmark( option );
            break;
        case 3:
            goto EXIT;
            break;
        default:
            printf( "Invalid option\n" );
        }

    }

 EXIT:
    printf( "\n" );

    return 0;
}

#endif

1
मुझे आपके प्लग-इन, बहुरूपी दृष्टिकोण, साथ ही पुन: प्रयोज्य पुस्तकालय या स्टैंड-अलोन, परीक्षण निष्पादन योग्य के रूप में निर्माण करने के लिए स्विच बहुत पसंद है। बहुत अच्छी तरह से सोचा =)

5

तुम क्या कर सकते हो

while(n){
    n=n&(n-1);
    count++;
}

इसके पीछे तर्क n-1 के बिट्स n के सबसे दाहिने सेट बिट से उल्टा है। यदि n = 6 यानी 110 तो 5 101 है बिट्स n के सबसे दाहिने सेट बिट से उल्टे हैं। इसलिए अगर हम और ये दो हम हर पुनरावृत्ति में सबसे कम बिट 0 बनायेंगे और हमेशा अगले सबसे दाहिने सेट बिट पर जाएँगे। सेट बिट की गिनती करते हुए। हर बिट सेट होने पर सबसे खराब समय जटिलता O (logn) होगी।

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