संख्या 7 का प्रतिनिधित्व करने वाले 8 बिट इस प्रकार हैं:
00000111
तीन बिट सेट हैं।
32-बिट पूर्णांक में सेट बिट्स की संख्या निर्धारित करने के लिए एल्गोरिदम क्या हैं?
संख्या 7 का प्रतिनिधित्व करने वाले 8 बिट इस प्रकार हैं:
00000111
तीन बिट सेट हैं।
32-बिट पूर्णांक में सेट बिट्स की संख्या निर्धारित करने के लिए एल्गोरिदम क्या हैं?
जवाबों:
इसे ' हैमिंग वेट ', 'पॉपकाउंट' या 'साइड शिवाय' के रूप में जाना जाता है ।
'सर्वश्रेष्ठ' एल्गोरिथ्म वास्तव में निर्भर करता है कि आप किस सीपीयू पर हैं और आपका उपयोग पैटर्न क्या है।
कुछ सीपीयू में ऐसा करने के लिए एक ही अंतर्निहित अनुदेश होता है और अन्य में समानांतर निर्देश होते हैं जो बिट वैक्टर पर कार्य करते हैं। समानांतर निर्देश (जैसे 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);
इसमें चर्चा किए गए किसी भी एल्गोरिदम का सबसे खराब मामला है, इसलिए आप इसे उपयोग करने वाले किसी भी उपयोग पैटर्न या मूल्यों से कुशलतापूर्वक निपटेंगे।
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)) & 0x0F0F0F0F
4x 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 के साथ सीपीयू पर स्पीडअप के लिए एक बिट पूर्णांक रजिस्टर के बजाय यह बिटवाइड-स्वार एल्गोरिदम एक ही बार में कई वेक्टर तत्वों में किया जा सकता है। (उदाहरण के लिए x86-64 कोड जिसे किसी भी सीपीयू पर चलना है, न कि केवल नेहम या बाद में।
हालांकि, पॉपकाउंट के लिए वेक्टर निर्देशों का उपयोग करने का सबसे अच्छा तरीका आमतौर पर समानांतर में प्रत्येक बाइट के समय 4 बिट्स के लिए टेबल-लुकअप करने के लिए एक चर-फेरबदल का उपयोग करके होता है। (एक वेक्टर रजिस्टर में आयोजित 4 बिट्स इंडेक्स 16 एंट्री टेबल)।
इंटेल सीपीयू पर, हार्डवेयर 64 बिट पॉपकांट अनुदेश 2 के कारक के बारे में एक एसएसएसई 3 PSHUFB
बिट-समानांतर कार्यान्वयन को बेहतर बना सकता है , लेकिन केवल तभी जब आपका कंपाइलर इसे सही तरीके से प्राप्त करता है । अन्यथा SSE काफी आगे आ सकता है। नए संकलक संस्करण इंटेल पर पॉपकंट झूठी निर्भरता समस्या से अवगत हैं ।
संदर्भ:
unsigned int
, आसानी से यह दिखाने के लिए कि यह किसी भी संकेत बिट जटिलताओं से मुक्त है। यह uint32_t
भी सुरक्षित होगा , जैसा कि, आप सभी प्लेटफार्मों पर क्या उम्मीद करते हैं?
>>
नकारात्मक मूल्यों के लिए कार्यान्वयन-परिभाषित है। तर्क को बदलने (या कास्ट) करने की आवश्यकता है unsigned
, और चूंकि कोड 32-बिट-विशिष्ट है, इसलिए संभवतः इसका उपयोग किया जाना चाहिएuint32_t
।
अपने संकलक के अंतर्निहित कार्यों पर भी विचार करें।
उदाहरण के लिए जीएनयू संकलक पर आप बस उपयोग कर सकते हैं:
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 के लिए अद्भुत नहीं है ।
std::bitset::count
। एक एकल __builtin_popcount
कॉल करने के लिए इस संकलन के बाद ।
मेरी राय में, "सबसे अच्छा" समाधान वह है जिसे किसी अन्य प्रोग्रामर (या मूल प्रोग्रामर को दो साल बाद) द्वारा बिना किसी टिप्पणी के पढ़ा जा सकता है। आप अच्छी तरह से सबसे तेज़ या चतुर समाधान चाहते हैं जो कुछ पहले ही प्रदान कर चुके हैं लेकिन मैं किसी भी समय चतुराई पर पठनीयता पसंद करता हूं।
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);
}
हालांकि ये विशिष्ट डेटा प्रकार आकारों पर निर्भर करते हैं, इसलिए वे पोर्टेबल नहीं हैं। लेकिन, चूंकि कई प्रदर्शन अनुकूलन वैसे भी पोर्टेबल नहीं हैं, इसलिए यह एक मुद्दा नहीं हो सकता है। यदि आप पोर्टेबिलिटी चाहते हैं, तो मैं पठनीय समाधान पर टिकूंगा।
if ((value & 1) == 1) { count++; }
के साथ count += value & 1
?
हैकर डिलाइट से, पी। 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 निर्देश (आर्क आश्रित) में निष्पादित, कोई शाखा नहीं।
हैकर डिलाइट प्रसन्न है ! अत्यधिक सिफारिशित।
Integer.bitCount(int)
इसी सटीक कार्यान्वयन का उपयोग करती है।
pop
इसके बजाय population_count
(या pop_cnt
यदि आपके पास एक संक्षिप्त विवरण होना चाहिए) को कॉल करने के लिए एक अच्छा किकिंग देना होगा । @MarcoBolis मैं मानता हूं कि जावा के सभी संस्करणों के बारे में सही होगा, लेकिन आधिकारिक तौर पर यह कार्यान्वयन पर निर्भर होगा :)
मुझे लगता है कि सबसे तेज़ तरीका है - लुकअप टेबल और पॉपकाउंट का उपयोग किए बिना - निम्नलिखित। यह सिर्फ 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 "सुझाएं।
popcount(int v)
और popcount(unsigned v)
। पोर्टेबिलिटी के लिए, विचार करें popcount(uint32_t v)
, आदि वास्तव में * 0x1010101 भाग की तरह।
return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
इसलिए हमें यह देखने के लिए अक्षरों को गिनने की आवश्यकता नहीं है कि आप वास्तव में क्या कर रहे हैं (क्योंकि आपने पहली बार खारिज कर दिया था 0
, मैंने गलती से सोचा था कि आपने गलत (फ़्लिप) बिट पैटर्न को मुखौटा के रूप में इस्तेमाल किया है। - जब तक मैंने नोट किया कि केवल 7 अक्षर हैं और 8 नहीं)।
मैं ऊब गया, और तीन तरीकों के एक अरब पुनरावृत्तियों को समयबद्ध किया। कंपाइलर 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 खर्च करने के लिए पर्याप्त नहीं है, तो दूसरे दृष्टिकोण का उपयोग करें। अन्यथा पठनीय (लेकिन धीमी गति से) एक-बिट-ए-टाइम दृष्टिकोण का उपयोग करें।
ऐसी स्थिति के बारे में सोचना मुश्किल है जहां आप बिट-ट्विडलिंग दृष्टिकोण का उपयोग करना चाहते हैं।
संपादित करें: यहां समान परिणाम ।
यदि आप जावा, बिल्ट-इन विधि का उपयोग कर रहे हैं Integer.bitCount
ऐसा करेगी।
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)
+-------------------------------+
यह उन सवालों में से एक है जहां यह आपकी सूक्ष्म वास्तुकला को जानने में मदद करता है। मैं सिर्फ समय के साथ दो संस्करणों को प्राप्त करता हूं। घड़ी चक्र सटीक)।
इनलाइन 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 से विभाजित?
गिनती = ० जबकि n> 0 if (n% 2) == 1 गिनती + = १ n / = 2
मैं मानता हूं कि यह सबसे तेज नहीं है, लेकिन "सबसे अच्छा" कुछ अस्पष्ट है। मेरा तर्क है कि हालांकि "सर्वश्रेष्ठ" में स्पष्टता का एक तत्व होना चाहिए
जब आप बिट पैटर्न लिखते हैं तो हैकर डिलाइट बिट-टिडलिंग बहुत स्पष्ट हो जाता है।
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;
}
पहला चरण विषम बिट्स को सम जोड़ता है, प्रत्येक दो में बिट्स का योग बनाता है। अन्य चरणों में उच्च-क्रम वाले विखंडू को कम-क्रम वाले विखंडू में जोड़ दिया जाता है, चंक के आकार को दोगुना कर दिया जाता है, जब तक कि हमारे पास अंतिम अंक पूरे इंट को नहीं लेते।
एक खुश मध्यम के लिए एक 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;
}
इसमें किया जा सकता है O(k)
, जहां k
बिट्स की संख्या निर्धारित है।
int NumberOfSetBits(int n)
{
int count = 0;
while (n){
++ count;
n = (n - 1) & n;
}
return count;
}
n &= (n-1)
रूप का इस्तेमाल किया ।
यह सबसे तेज़ या सबसे अच्छा समाधान नहीं है, लेकिन मुझे अपने तरीके से एक ही सवाल मिला, और मैंने सोचना और सोचना शुरू कर दिया। अंत में मुझे एहसास हुआ कि यह इस तरह से किया जा सकता है यदि आप गणितीय तरफ से समस्या प्राप्त करते हैं, और एक ग्राफ खींचते हैं, तो आप पाते हैं कि यह एक फ़ंक्शन है जिसका कुछ आवधिक भाग है, और फिर आपको अवधियों के बीच के अंतर का एहसास होता है ... हेयर यू गो:
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);
}
}
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))()
जिस फ़ंक्शन की आप तलाश कर रहे हैं, उसे अक्सर बाइनरी नंबर की "साइडव्यू सम" या "जनसंख्या गणना" कहा जाता है। नुथ पूर्व-फ़ासिकल 1 ए, पीपी 11-12 में इसकी चर्चा करता है (हालाँकि वॉल्यूम 2, 4.6.3- (7) में एक संक्षिप्त संदर्भ था।)
ठिकाना classicus पीटर वेग्नर के लेख "एक बाइनरी कम्प्यूटर में गिनती वाले के लिए एक तकनीक", से है एसीएम का संचार , खंड 3 (1960) संख्या 5, पेज 322 । वह वहां दो अलग-अलग एल्गोरिदम देता है, एक "विरल" होने के लिए अपेक्षित संख्याओं के लिए अनुकूलित (यानी, लोगों की एक छोटी संख्या है) और विपरीत मामले के लिए एक है।
कुछ खुले प्रश्न: -
हम नकारात्मक संख्या का समर्थन करने के लिए अहंकार को संशोधित कर सकते हैं: -
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
मुझे लगता है कि ब्रायन कर्निघन का तरीका भी उपयोगी होगा ... सेट बिट्स के रूप में यह कई पुनरावृत्तियों से गुजरता है। इसलिए यदि हमारे पास केवल उच्च बिट सेट के साथ 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 में बेक्डबाख द्वारा संपादित पुस्तक में प्रकाशित हुई।")
मैं नीचे दिए गए कोड का उपयोग करता हूं जो अधिक सहज है।
int countSetBits(int n) {
return !n ? 0 : 1 + countSetBits(n & (n-1));
}
तर्क: n & (n-1) n के अंतिम सेट बिट को रीसेट करता है।
पुनश्च: मुझे पता है कि यह ओ (1) समाधान नहीं है, हालांकि यह एक दिलचस्प समाधान है।
O(ONE-BITS)
। यह वास्तव में O (1) है क्योंकि अधिकांश 32 वन-बिट्स हैं।
"बेस्ट एल्गोरिथ्म" के साथ आपका क्या मतलब है? शॉर्ट कोड या फास्ट कोड? आपका कोड बहुत ही सुंदर लग रहा है और इसमें लगातार निष्पादन का समय है। कोड भी बहुत छोटा है।
लेकिन अगर गति प्रमुख कारक है और कोड आकार नहीं है, तो मुझे लगता है कि अनुसरण तेज हो सकता है:
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 बिट वैल्यू तेज हो सकती है।
मैंने लगभग 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-पंक्ति बूलियन टेबल के साथ चेक कर सकते हैं।
यदि आप 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
हालांकि एक अच्छा बना सकते हैं ।
मैं विशेष रूप से भाग्य फ़ाइल से इस उदाहरण के शौकीन हूँ:
#define BITCOUNT (x) (((BX_ (x) + (BX_ (x) >> 4)) और 0x0F0F0F0F)% 255) #define BX_ (x) ((x) - (((x) >> 1) & 0x7777777) - ((x) >> 2) और 0x33333333) - ((x) >> 3) और 0x11111111)
मुझे यह सबसे अच्छा लगता है क्योंकि यह बहुत सुंदर है!
जावा 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);
}
मुझे 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];
}
सेट बिट्स की गणना करने के लिए कई एल्गोरिथ्म हैं; लेकिन मुझे लगता है कि सबसे अच्छा एक तेजी से एक है! आप इस पृष्ठ पर विस्तृत देख सकते हैं:
मैं यह सुझाव देता हूं:
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 लेता है।
इनपुट आकार पर ब्रांचिंग के साथ बाइट बिट की पूर्व-गणना तालिका का उपयोग करके फास्ट सी # समाधान।
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
};
}
(0xe994 >>(k*2))&3
, मेमोरी एक्सेस के बिना ...
यहां एक पोर्टेबल मॉड्यूल (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
तुम क्या कर सकते हो
while(n){
n=n&(n-1);
count++;
}
इसके पीछे तर्क n-1 के बिट्स n के सबसे दाहिने सेट बिट से उल्टा है। यदि n = 6 यानी 110 तो 5 101 है बिट्स n के सबसे दाहिने सेट बिट से उल्टे हैं। इसलिए अगर हम और ये दो हम हर पुनरावृत्ति में सबसे कम बिट 0 बनायेंगे और हमेशा अगले सबसे दाहिने सेट बिट पर जाएँगे। सेट बिट की गिनती करते हुए। हर बिट सेट होने पर सबसे खराब समय जटिलता O (logn) होगी।