2012-01-31 21 views
11

मैं एक कार्यक्रम है जहाँ मैं विभिन्न चीजों की सफलता का ट्रैक रखने कर रहा हूँcollections.Counter का उपयोग कर - एक बात से प्रत्येक सफलता वेतन वृद्धि इसी काउंटर: भविष्य परीक्षण के लिएमैं पाइथन काउंटर क्लास से भारित यादृच्छिक पिक कैसे प्राप्त कर सकता हूं?

import collections 
scoreboard = collections.Counter() 

if test(thing): 
    scoreboard[thing]+ = 1 

फिर, मैं चाहता हूँ चीजों की ओर बढ़ने के लिए जिसने सबसे अधिक सफलता उत्पन्न की है। Counter.elements() इसके लिए आदर्श लग रहा था, क्योंकि यह तत्वों (मनमाने ढंग से क्रम में) को वापस देता है, गिनती के बराबर कई बार दोहराया जाता है। तो मैं सोचा मैं सिर्फ कर सकता है:

import random 
nextthing=random.choice(scoreboard.elements()) 

लेकिन नहीं, कि लेखन त्रुटि को जन्म देती है: प्रकार की वस्तु 'itertools.chain' कोई लेन() है। ठीक है, तो random.choice can't work with iterators। लेकिन, इस मामले में, लंबाई ज्ञात (या जानकार) है - यह sum(scoreboard.values()) है।

मुझे अज्ञात लंबाई की सूची और यादृच्छिक रूप से एक तत्व चुनने के लिए मूल एल्गोरिदम पता है, लेकिन मुझे संदेह है कि कुछ और सुरुचिपूर्ण है। मुझे यहाँ क्या करना चाहिए?

+0

कैसे सिर्फ मोड़ के बारे में एक सूची में 'scoreboard.elements()'? – delnan

+0

@ डेलनान - नीचे [लार्क्स के उत्तर] (http://stackoverflow.com/a/9084700/479426) पर टिप्पणी देखें। – mattdm

उत्तर

8

आप जितनी बार लौटा दी जाएगी है है itertools.islice का उपयोग करके इसे आसानी से कर सकते हैं ताकि एक पुनरावृत्ति के एनएच आइटम प्राप्त हो सकें:

>>> import random 
>>> import itertools 
>>> import collections 
>>> c = collections.Counter({'a': 2, 'b': 1}) 
>>> i = random.randrange(sum(c.values())) 
>>> next(itertools.islice(c.elements(), i, None)) 
'a' 
+0

क्या i-1' तत्वों के माध्यम से पुनरावृत्ति करने के बजाय आइटम की गणना करने का कोई तरीका है? यदि सी में छोटे मान हैं, तो यह कोई समस्या नहीं है, लेकिन यदि एक या अधिक चाबियों की बहुत अधिक गिनती है, तो इसे फिर से शुरू करने में लंबा समय लगेगा। –

4

आप list() में इटरेटर लपेट सकता है एक सूची में random.choice() के लिए यह कन्वर्ट करने के लिए:

nextthing = random.choice(list(scoreboard.elements())) 

नकारात्मक पक्ष यह है कि यहाँ इस स्मृति में सूची का विस्तार, बल्कि आइटम-दर-आइटम होगा के रूप में यह तक पहुँचने से है आमतौर पर एक इटरेटर के साथ मिलता है।

यदि आप इसे हल करना चाहते हैं, तो this algorithm शायद एक अच्छा विकल्प है।

+2

आदर्श रूप में, मैं गिनती को एक विशाल सूची में विस्फोट से बचना चाहता हूं। ऐसा करने से पहले किसी बड़े कंटेनर में सब कुछ पिल करने के बजाय 'काउंटर' का उपयोग करने का लाभ अस्वीकार होता है। – mattdm

3

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

import random 

def get_random_item_weighted(scoreboard):  
    total_scoreboard_value = sum(scoreboard.values()) 

    item_loc = random.random() * total_scoreboard_value 
    current_loc = 0 
    for item, score in scoreboard.items(): 
     current_loc += score 
     if current_loc > item_loc: 
      return item 
उदाहरण के लिए

, अगर वहाँ 2 आइटम हैं:

ITEM1 स्कोर 5
ITEM2 स्कोर 10

ITEM2 दो बार के रूप ITEM1

1

यात्रा के साथ एक और प्रकार:

import collections 
from collections import Counter 
import random 


class CounterElementsRandomAccess(collections.Sequence): 
    def __init__(self, counter): 
     self._counter = counter 

    def __len__(self): 
     return sum(self._counter.values()) 

    def __getitem__(self, item): 
     for i, el in enumerate(self._counter.elements()): 
      if i == item: 
       return el 

scoreboard = Counter('AAAASDFQWERQWEQWREAAAAABBBBCCDDVBSDF') 
score_elements = CounterElementsRandomAccess(scoreboard) 
for i in range(10): 
    print random.choice(score_elements) 
1

एक और प्रकार, सेटअप थोड़ा बोझिल है, लेकिन देखने लघुगणक जटिलता में (उपयुक्त है जब कई लुकअप की जरूरत है) है:

import itertools 
import random 
from collections import Counter 
from bisect import bisect 

counter = Counter({"a": 5, "b": 1, "c": 1}) 

#setup 
most_common = counter.most_common() 
accumulated = list(itertools.accumulate([x[1] for x in most_common])) # i.e. [5, 6, 7] 
total_size = accumulated[-1] 

# lookup 
i = random.randrange(total_size) 
print(most_common[bisect(accumulated, i)])