सकारात्मक पूर्णांक में गैर-शून्य बिट्स की गिनती का तेज़ तरीका


117

मुझे अजगर में एक पूर्णांक में बिट्स की संख्या की गणना करने के लिए एक तेज़ तरीका चाहिए। मेरा वर्तमान समाधान है

bin(n).count("1")

लेकिन मुझे आश्चर्य है कि क्या ऐसा करने का कोई तेज़ तरीका है?

पुनश्च: (मैं संख्याओं की एक सूची के रूप में एक बड़ा 2 डी बाइनरी सरणी का प्रतिनिधित्व कर रहा हूं और बिटवाइज़ संचालन कर रहा हूं, और यह समय को घंटों से मिनटों तक नीचे लाता है। अब मैं उन अतिरिक्त मिनटों से छुटकारा पाना चाहूंगा।

संपादित करें: 1. यह अजगर 2.7 या 2.6 में होना चाहिए

और छोटी संख्या के लिए अनुकूलन करना बहुत मायने नहीं रखता है क्योंकि यह एक स्पष्ट अड़चन नहीं होगी, लेकिन मेरे पास कुछ स्थानों पर 10 000 + बिट्स के साथ संख्याएं हैं

उदाहरण के लिए यह 2000 का बिट केस है:

12448057941136394342297748548545082997815840357634948550739612798732309975923280685245876950055614362283769710705811182976142803324242407017104841062064840113262840137625582646683068904149296501029754654149991842951570880471230098259905004533869130509989042199261339990315125973721454059973605358766253998615919997174542922163484086066438120268185904663422979603026066685824578356173882166747093246377302371176167843247359636030248569148734824287739046916641832890744168385253915508446422276378715722482359321205673933317512861336054835392844676749610712462818600179225635467147870208L


1
यदि आपके "पूर्णांक" मानक अजगर से अधिक लंबे हैं, तो आप किस प्रकार का प्रतिनिधित्व कर रहे हैं int? क्या इसकी गणना के लिए अपनी स्वयं की विधि नहीं है?
मार्सिन


3
इस सवाल से अलग होने के लिए stackoverflow.com/a/2654211/1959808 (यदि यह अलग होना चाहिए --- कम से कम इतना तो दिखता है) कृपया शीर्षक को फिर से विचार करने पर विचार करें "... गैर की संख्या की गिनती शून्य बिट ... ”या समान। अन्यथा int.bit_lengthउत्तर होना चाहिए, और नीचे स्वीकार नहीं किया जाना चाहिए।
Ioannis Filippidis

जवाबों:


121

मनमाने ढंग से लंबाई पूर्णांक के लिए, bin(n).count("1")सबसे तेज है जो मुझे शुद्ध पायथन में मिल सकता है।

मैंने क्रमशः 64-बिट और 32-बिट विखंडू में पूर्णांक को संसाधित करने के लिए Óscar और एडम के समाधानों को अपनाने की कोशिश की। दोनों की तुलना में कम से कम दस गुना धीमा था bin(n).count("1")(32-बिट संस्करण ने लगभग आधा फिर से उतना ही समय लिया)।

दूसरी ओर, gmpy popcount() ने लगभग 1/20 वां समय लिया bin(n).count("1")। तो अगर आप gmpy स्थापित कर सकते हैं, तो इसका उपयोग करें।

टिप्पणियों में एक प्रश्न का उत्तर देने के लिए, बाइट्स के लिए मैं एक लुकअप टेबल का उपयोग करूंगा। आप इसे रनटाइम पर उत्पन्न कर सकते हैं:

counts = bytes(bin(x).count("1") for x in range(256))  # py2: use bytearray

या बस इसे शाब्दिक रूप से परिभाषित करें:

counts = (b'\x00\x01\x01\x02\x01\x02\x02\x03\x01\x02\x02\x03\x02\x03\x03\x04'
          b'\x01\x02\x02\x03\x02\x03\x03\x04\x02\x03\x03\x04\x03\x04\x04\x05'
          b'\x01\x02\x02\x03\x02\x03\x03\x04\x02\x03\x03\x04\x03\x04\x04\x05'
          b'\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06'
          b'\x01\x02\x02\x03\x02\x03\x03\x04\x02\x03\x03\x04\x03\x04\x04\x05'
          b'\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06'
          b'\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06'
          b'\x03\x04\x04\x05\x04\x05\x05\x06\x04\x05\x05\x06\x05\x06\x06\x07'
          b'\x01\x02\x02\x03\x02\x03\x03\x04\x02\x03\x03\x04\x03\x04\x04\x05'
          b'\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06'
          b'\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06'
          b'\x03\x04\x04\x05\x04\x05\x05\x06\x04\x05\x05\x06\x05\x06\x06\x07'
          b'\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06'
          b'\x03\x04\x04\x05\x04\x05\x05\x06\x04\x05\x05\x06\x05\x06\x06\x07'
          b'\x03\x04\x04\x05\x04\x05\x05\x06\x04\x05\x05\x06\x05\x06\x06\x07'
          b'\x04\x05\x05\x06\x05\x06\x06\x07\x05\x06\x06\x07\x06\x07\x07\x08')

फिर इसे counts[x]1 बिट की संख्या प्राप्त करनी है, xजिसमें 0 ≤ x number 255 है।


7
+1! इस का bin(n).count("0")आक्षेप सटीक नहीं है, तथापि, यह कहा जाना चाहिए: '0b' उपसर्ग के कारण सटीक नहीं है। अवश्य होना चाहिए कि bin(n)[2:].count('0')उन गिनती naughts के लिए ....
भेड़िया

11
आप वास्तव में कितने बिट्स भर रहे हैं, यह जानने के बिना आप शून्य बिट्स की गणना नहीं कर सकते हैं, हालांकि, जो अजगर के पूर्णांक के साथ समस्याग्रस्त है क्योंकि यह कुछ भी हो सकता है।
किंडल

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

खस्ता हालतों के लिए मैं कुछ इस तरह से देखूंगा
kindall

1
मैंने उपयोग किया है bin(n).count("1")। हालाँकि, केवल 60% अजगर प्रस्तुत करता है। @ लेटोडे
नॉर्थट्री

29

आप निम्न एल्गोरिथ्म को अनुकूलित कर सकते हैं:

def CountBits(n):
  n = (n & 0x5555555555555555) + ((n & 0xAAAAAAAAAAAAAAAA) >> 1)
  n = (n & 0x3333333333333333) + ((n & 0xCCCCCCCCCCCCCCCC) >> 2)
  n = (n & 0x0F0F0F0F0F0F0F0F) + ((n & 0xF0F0F0F0F0F0F0F0) >> 4)
  n = (n & 0x00FF00FF00FF00FF) + ((n & 0xFF00FF00FF00FF00) >> 8)
  n = (n & 0x0000FFFF0000FFFF) + ((n & 0xFFFF0000FFFF0000) >> 16)
  n = (n & 0x00000000FFFFFFFF) + ((n & 0xFFFFFFFF00000000) >> 32) # This last & isn't strictly necessary.
  return n

यह 64-बिट पॉजिटिव नंबरों के लिए काम करता है, लेकिन यह आसानी से विस्तार योग्य है और तर्क के लॉगरिदम के साथ संचालन की संख्या बढ़ जाती है (यानी तर्क के बिट-आकार के साथ रैखिक)।

यह समझने के लिए कि यह कैसे काम करता है कल्पना करें कि आप 64 64-बिट स्ट्रिंग को 64 1-बिट बाल्टी में विभाजित करते हैं। प्रत्येक बाल्टी का मूल्य बाल्टी में सेट बिट्स की संख्या के बराबर होता है (0 यदि कोई बिट सेट नहीं है और 1 यदि एक बिट सेट है)। पहले परिवर्तन का परिणाम एक अनुरूप अवस्था में होता है, लेकिन 32 बाल्टियों के साथ प्रत्येक 2-बिट लंबा होता है। यह उचित रूप से बाल्टियों को शिफ्ट करने और उनके मूल्यों को जोड़ने के द्वारा प्राप्त किया जाता है (एक जोड़ सभी बाल्टियों की देखभाल करता है क्योंकि बाल्टियों में कोई भी कैरी नहीं हो सकती है - एन-बिट संख्या हमेशा एन को एनकोड करने के लिए पर्याप्त लंबी होती है)। जब तक हम एक 64-बिट लंबी बाल्टी तक नहीं पहुंच जाते, तब तक और अधिक परिवर्तन होने के साथ-साथ तेजी से बढ़ते आकार की बाल्टियों की संख्या में कमी आती है। यह मूल तर्क में सेट बिट्स की संख्या देता है।


मुझे गंभीरता से पता नहीं है कि यह 10 000 बिट संख्या के साथ कैसे काम करेगा, लेकिन मैं समाधान पसंद करता हूं। क्या आप मुझे संकेत दे सकते हैं कि क्या और कैसे मैं इसे बड़ी संख्या में अपीयर कर सकता हूं?
zidarsk8

मैंने उन बिट्स की संख्या नहीं देखी, जो आप यहाँ देख रहे हैं। क्या आपने अपने डेटा हैंडलिंग कोड को C जैसी निम्न-स्तरीय भाषा में लिखने पर विचार किया है? शायद आपके पायथन कोड के विस्तार के रूप में? आप निश्चित रूप से अजगर में बड़े अंकों की तुलना में सी में बड़े सरणियों का उपयोग करके प्रदर्शन में सुधार कर सकते हैं। उस ने कहा, आप CountBits()कोड के सिर्फ 8 लाइनों को जोड़कर 10k-बिट संख्या को संभालने के लिए फिर से लिख सकते हैं । लेकिन यह विशाल स्थिरांक के कारण अनिर्दिष्ट हो जाएगा।
एडम ज़ल्कमैन

2
आप स्थिरांक के अनुक्रम को उत्पन्न करने के लिए कोड लिख सकते हैं, और प्रसंस्करण के लिए एक लूप सेट कर सकते हैं।
कार्ल केनचेल

इस उत्तर का बड़ा फायदा है कि इसे बड़े सरणियों से निपटने वाले मामलों के लिए वेक्टर किया जा सकता है numpy
gerrit

17

यहाँ जनसंख्या गणना एल्गोरिथम का पायथन कार्यान्वयन है, जैसा कि इस पोस्ट में बताया गया है :

def numberOfSetBits(i):
    i = i - ((i >> 1) & 0x55555555)
    i = (i & 0x33333333) + ((i >> 2) & 0x33333333)
    return (((i + (i >> 4) & 0xF0F0F0F) * 0x1010101) & 0xffffffff) >> 24

इसके लिए काम करेंगे 0 <= i < 0x100000000


यह चालाकी है। कूल्हे से एक उत्तर की शूटिंग के बजाय इसे देखना पूरी तरह से उपयुक्त है!
Mr बजे मिगोमेज़

1
क्या आपने इसे बेंचमार्क किया? मेरी मशीन पर अजगर 2.7 का उपयोग करते हुए, मैंने पाया कि यह वास्तव में थोड़ा धीमा है bin(n).count("1")
डेविड वेल्डन

@DavidWeldon नहीं मैंने नहीं किया, क्या आप कृपया अपने बेंचमार्क पोस्ट कर सकते हैं?
सकर लोपेज

%timeit numberOfSetBits(23544235423): 1000000 loops, best of 3: 818 ns per loop; %timeit bitCountStr(23544235423): 1000000 loops, best of 3: 577 ns per loop
gerrit

7
हालाँकि, 841 µ में numberOfSetBitsमेरे 864 × 64 numpy.ndarrayको संसाधित करता है । साथ bitCountStrमैं स्पष्ट रूप से पाश के लिए है, और यह 40.7 एमएस, या लंबे समय तक लगभग 50 बार लेता है।
gerrit

8

इस पोस्ट के अनुसार , यह हेमिंग वजन का सबसे तेज़ कार्यान्वयन है (यदि आप 64KB मेमोरी का उपयोग करने में कोई आपत्ति नहीं है)।

#http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetTable
POPCOUNT_TABLE16 = [0] * 2**16
for index in range(len(POPCOUNT_TABLE16)):
    POPCOUNT_TABLE16[index] = (index & 1) + POPCOUNT_TABLE16[index >> 1]

def popcount32_table16(v):
    return (POPCOUNT_TABLE16[ v        & 0xffff] +
            POPCOUNT_TABLE16[(v >> 16) & 0xffff])

अजगर पर 2.x आप बदलना चाहिए rangeसाथ xrange

संपादित करें

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

gmpy एक सी-कोडेड पायथन एक्सटेंशन मॉड्यूल है जो जीएमपी लाइब्रेरी को लपेटता है।

>>> import gmpy
>>> gmpy.popcount(2**1024-1)
1024

मैंने अपने प्रश्न को यह स्पष्ट करने के लिए संपादित किया है कि मुझे बड़ी संख्या (10k बिट्स और अधिक) के लिए इसकी आवश्यकता है। 32 बिट पूर्णांक के लिए कुछ का अनुकूलन संभावित रूप से इतना अंतर नहीं करेगा क्योंकि गिनती की संख्या वास्तव में बड़ी होगी, इस मामले में जो धीमी गति से निष्पादित समय का कारण होगा।
zidarsk8

लेकिन जीएमपी वास्तव में बहुत बड़ी संख्याओं के लिए है, जिसमें आपके द्वारा उल्लिखित आकारों से कहीं अधिक संख्याएं शामिल हैं।
जेम्स यंगमैन

1
यदि आप उपयोग array.arrayकरते हैं तो मेमोरी का उपयोग बेहतर होगा POPCOUNT_TABLE16, क्योंकि यह पायथन intऑब्जेक्ट्स की एक गतिशील आकार सूची के बजाय पूर्णांक की एक सरणी के रूप में संग्रहीत किया जाएगा ।
gsnedders

6

मुझे वास्तव में यह तरीका पसंद है। इसका सरल और सुंदर तेज़ लेकिन बिट लंबाई में सीमित नहीं है क्योंकि अजगर में अनंत पूर्णांक होते हैं।

यह वास्तव में अधिक चालाक है जितना दिखता है, क्योंकि यह शून्य को स्कैन करने में समय बर्बाद करने से बचता है। उदाहरण के लिए सेट बिट्स को १०००००००००००००००००००००००००००००१००००००००००१ में ११११ में गिनने में उतना ही समय लगेगा।

def get_bit_count(value):
   n = 0
   while value:
      n += 1
      value &= value-1
   return n

बहुत अच्छा लग रहा है, लेकिन यह बहुत ही "विरल" पूर्णांकों के लिए अच्छा है। औसतन यह काफी धीमा है। फिर भी, यह कुछ उपयोग मामलों में वास्तव में उपयोगी दिखता है।
zidarsk8

मुझे यकीन नहीं है कि आप "औसत पर यह काफी धीमा है" से क्या मतलब है। क्या की तुलना में धीमी गति से? क्या आप कुछ अन्य अजगर कोड की तुलना में धीमी गति से मतलब है जो आप उद्धृत नहीं कर रहे हैं? यह औसत संख्या के लिए बिट द्वारा गिनती के रूप में दोगुना तेज़ है। वास्तव में मेरी मैकबुक पर यह प्रति सेकंड 12.6 मिलियन बिट्स गिना जाता है जो कि मैं उनकी गिनती कर सकता हूं। यदि आपके पास एक और सामान्य अजगर एल्गोरिथ्म है जो पूर्णांक के किसी भी लम्बाई के लिए काम करता है और इससे तेज है तो मैं इसके बारे में सुनना चाहूंगा।
रोबोटबग्स

1
मैं स्वीकार करता हूं कि यह वास्तव में ऊपर मैनुअल द्वारा उत्तर की तुलना में धीमी है।
रोबॉटबग्स

औसत साधनों पर धीमी गति से, 10000 अंकों वाले 10000 नंबरों के लिए बिट्स की गिनती 0.15s के साथ होती है, bin(n).count("1")लेकिन यह आपके फ़ंक्शन के लिए 3.8s लेता है । यदि संख्याओं में बहुत कम बिट सेट थे तो यह तेजी से काम करेगा, लेकिन यदि आप कोई यादृच्छिक संख्या लेते हैं, तो औसतन ऊपर का कार्य परिमाण धीमे के क्रम में होगा।
zidarsk8

ठीक है, मैं इसे स्वीकार करूंगा। मुझे लगता है कि मैं सिर्फ एक डिक हो रहा था क्योंकि आप थोड़े अभेद्य हैं, लेकिन आप पूरी तरह से सही हैं। मैंने अभी अपनी टिप्पणी से पहले ऊपर मैनुअल द्वारा विधि का उपयोग करके विधि का परीक्षण नहीं किया था। यह बहुत भद्दी लगती है लेकिन यह वास्तव में बहुत तेज है। अब मैं उस तरह के संस्करण का उपयोग कर रहा हूं, लेकिन शब्दकोश में 16 मानों के साथ और वह उस उद्धृत की तुलना में बहुत तेज है। लेकिन रिकॉर्ड के लिए मैं केवल कुछ बिट्स के साथ एक एप्लिकेशन में मेरा उपयोग कर रहा था जो कि 1 पर सेट थे। लेकिन पूरी तरह से यादृच्छिक बिट्स के लिए हाँ यह लंबाई के साथ घटते हुए थोड़ा विचरण के साथ लगभग 50:50 पर जा रहा है।
रोबॉटबग्स

3

आप एक पूर्णांक के बाइनरी स्ट्रिंग [1] को प्राप्त करने के लिए एल्गोरिथ्म का उपयोग कर सकते हैं, लेकिन स्ट्रिंग को सुगम बनाने के बजाय, उनकी संख्या की गणना करें:

def count_ones(a):
    s = 0
    t = {'0':0, '1':1, '2':1, '3':2, '4':1, '5':2, '6':2, '7':3}
    for c in oct(a)[1:]:
        s += t[c]
    return s

[१] https://wiki.python.org/moin/BitManipulation


यह तेजी से काम करता है। एक त्रुटि है, कम से कम p3 पर, [1:] होना चाहिए [2:] क्योंकि स्ट्रिंग से पहले ऑक्ट () '0o' देता है। यदि आप
ओक्स के

2

आपने कहा कि नम्पी बहुत धीमी थी। क्या आप व्यक्तिगत बिट्स को स्टोर करने के लिए इसका उपयोग कर रहे थे? क्यों न तो इन्टर्स का उपयोग बिट एरेज़ के रूप में किया जाए, लेकिन उन को स्टोर करने के लिए नेम्पी का उपयोग किया जाए?

ceil(n/32.)32-बिट ints की एक सरणी के रूप में n बिट्स स्टोर करें । फिर आप समान सरणी के साथ काम कर सकते हैं (अच्छी तरह से, समान पर्याप्त) जिस तरह से आप किसी अन्य सरणी को अनुक्रमित करने के लिए उनका उपयोग करते हैं, सहित।

एल्गोरिथ्म मूल रूप से गणना करने के लिए है, समानांतर में, प्रत्येक सेल में सेट बिट्स की संख्या, और वे प्रत्येक सेल के बिटकाउंट का योग करते हैं।

setup = """
import numpy as np
#Using Paolo Moretti's answer http://stackoverflow.com/a/9829855/2963903
POPCOUNT_TABLE16 = np.zeros(2**16, dtype=int) #has to be an array

for index in range(len(POPCOUNT_TABLE16)):
    POPCOUNT_TABLE16[index] = (index & 1) + POPCOUNT_TABLE16[index >> 1]

def popcount32_table16(v):
    return (POPCOUNT_TABLE16[ v        & 0xffff] +
            POPCOUNT_TABLE16[(v >> 16) & 0xffff])

def count1s(v):
    return popcount32_table16(v).sum()

v1 = np.arange(1000)*1234567                       #numpy array
v2 = sum(int(x)<<(32*i) for i, x in enumerate(v1)) #single int
"""
from timeit import timeit

timeit("count1s(v1)", setup=setup)        #49.55184188873349
timeit("bin(v2).count('1')", setup=setup) #225.1857464598633

हालांकि मुझे आश्चर्य है कि किसी ने आपको सी मॉड्यूल लिखने का सुझाव नहीं दिया।


0
#Python prg to count set bits
#Function to count set bits
def bin(n):
    count=0
    while(n>=1):
        if(n%2==0):
            n=n//2
        else:
            count+=1
            n=n//2
    print("Count of set bits:",count)
#Fetch the input from user
num=int(input("Enter number: "))
#Output
bin(num)

-2

यह पता चलता है कि आपका आरंभिक प्रतिनिधित्व उन किलों की सूची है जो या तो 1 या 0. हैं, बस उन्हें उस प्रतिनिधित्व में गिनें।


एक पूर्णांक में बिट्स की संख्या अजगर में स्थिर होती है।

हालाँकि, यदि आप सेट बिट्स की संख्या को गिनना चाहते हैं, तो सबसे तेज़ तरीका निम्नलिखित pseudocode के अनुरूप एक सूची बनाना है: [numberofsetbits(n) for n in range(MAXINT)]

आपके द्वारा सूची तैयार करने के बाद यह आपको लगातार समय प्रदान करेगा। इसके अच्छे क्रियान्वयन के लिए @ पाओलोमोरेटी का उत्तर देखें। बेशक, आपको यह सब याद में रखने की ज़रूरत नहीं है - आप किसी प्रकार के निरंतर कुंजी-मूल्य स्टोर, या यहां तक ​​कि MySql का उपयोग कर सकते हैं। (एक अन्य विकल्प आपके स्वयं के सरल डिस्क-आधारित भंडारण को लागू करना होगा)।


@StevenRumbalski यह कैसा है?
मार्सिन

जब मैंने आपके उत्तर को पढ़ा तो इसमें केवल आपका पहला वाक्य था: "एक पूर्णांक में बिट्स की संख्या अजगर में स्थिर है।"
स्टीवन रूम्बल्स्की

मेरे पास पहले से ही सभी गिनती के लिए एक बिट काउंट लुकअप टेबल है जिसे स्टोर करना संभव है, लेकिन संख्याओं की एक बड़ी सूची होने और [i] & [j] के साथ उन पर काम करने से आपका सोल्यूशन बेकार हो जाता है जब तक कि मेरे पास 10+ न हो राम की जीबी। सरणी के लिए & ^ | 10000 नंबर के ट्रिपल्स के लिए 3 * 10000 ^ 3 लुकअप टेबल साइज होगा। चूँकि मुझे नहीं पता कि मुझे किस चीज़ की आवश्यकता होगी, यह कुछ और मायने रखता है जब मुझे ज़रूरत पड़ने पर कुछ हज़ार गिनने की ज़रूरत है
zidarsk8

@ zidarsk8 या, आप कुछ प्रकार के डेटाबेस या लगातार कुंजी-मूल्य स्टोर का उपयोग कर सकते हैं।
मार्सिन

@ zidarsk8 10 + GB का राम चौंकाने वाला बड़ा नहीं है। यदि आप तेजी से संख्यात्मक अभिकलन करना चाहते हैं, तो मध्यम-बड़े लोहे का उपयोग करना अनुचित नहीं है।
मार्सिन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.