2010-01-15 17 views
14

मैं इस तरह डेटा है:रैंडम भारित पसंद

d = (
    (701, 1, 0.2), 
    (701, 2, 0.3), 
    (701, 3, 0.5), 
    (702, 1, 0.2), 
    (702, 2, 0.3), 
    (703, 3, 0.5) 
) 

कहाँ (701, 1, 0.2) = (ID1 आईडी 2, प्राथमिकता)

वहाँ आईडी 2 का चयन करने के अगर मैं जानता हूँ कि एक सुंदर तरीका है आईडी 1, प्राथमिकता का उपयोग कर?

समारोह (701) लौटना चाहिए:
    1 - 30%
    3 - - 20% मामलों
    2 में 50%

प्रतिशत पाठ्यक्रम

के किसी न किसी हो जाएगा
+3

क्या आप अब तक क्या है भार है कि आप चाहते हैं में लोड? – SilentGhost

+1

एक "सुंदर" तरीका? – marcc

+0

702 और 703 के लिए प्राथमिकताएं 1 तक नहीं जुड़ती हैं। 703 के लिए 50% समय के साथ क्या होता है जब हमें 'वापस 3 नहीं करना चाहिए? हम क्या लौटते हैं? – MAK

उत्तर

6

इस प्रकार प्रत्येक ID1 के लिए एक संचयी बंटन फ़ंक्शन जनरेट करें:

cdfs = defaultdict() 
for id1,id2,val in d: 
    prevtotal = cdfs[id1][-1][0] 
    newtotal = prevtotal + val 
    cdfs[id1].append((newtotal,id2)) 

तो तुम

cdfs = { 701 : [ (0.2,1), (0.5,2), (1.0,3) ], 
     702 : [ (0.2,1), (0.5,2) ], 
     703 : [ (0.5,3) ] } 

है तो फिर एक यादृच्छिक संख्या और खोज उत्पन्न होगा सूची में इसके लिए।

def func(id1): 
    max = cdfs[id1][-1][0] 
    rand = random.random()*max 
    for upper,id2 in cdfs[id1]: 
     if upper>rand: 
      return id2 
    return None 
+0

अंतिम दो पंक्तियां - 'अन्य: वापसी कोई नहीं' हटा दी जानी चाहिए। यह लूप के पुनरावृत्ति को तब तक रोक देगा जब तक कि रैंड वैल्यू सूची में पहले आइटम से नीचे न हो। –

+0

@ डॉउग: धन्यवाद, वापसी वापस ले ली गई कोई नहीं। –

2

पर्याप्त संख्या में random module से एक अलग वर्दी वितरण का उपयोग करें, फिर इसे विभाजित करें:

उदाहरण के लिए

, मामले 701 के लिए, 10 मूल्यों पर एक वितरण का उपयोग के लिए 2 मूल्यों एक और 3 के लिए, 1 वापसी, वापसी 2, और अन्य 5 के लिए, लौट 3.

आपके पास पर्याप्त वर्दी का उपयोग कर किसी भी वितरण का निर्माण कर सकते वितरण :)

1

यदि आपके प्रतिशत मान पूरे प्रतिशत मूल्यों की तुलना में अधिक सटीक नहीं होंगे, तो 0-99 संख्या उत्पन्न करने के लिए यादृच्छिक संख्या जनरेटर का उपयोग करें।

फिर अपने कार्य में, सही संख्या चुनने के लिए (प्रोग्रामैटिक) मामलों का उपयोग करें। उदाहरण के लिए (यह साफ):

 
if 701 
    if random_num < 20 
    return 1 
    else if random number < 50 // (20 + 30) 
    return 2 
    else if random number < 100 // (20 + 30 + 50) 
    return 3 
    else 
    // error 
+3

इस प्रविष्टि को वोट क्यों दिया गया था? – ericmjl

+0

.... क्योंकि लोग मूर्ख हैं। – Fattie

1

एक बहुत जल्दी हैक:

import random 

d = { 
    701: [(1,0.2),(2,0.3),(3,0.5)], 
    702: [(1,0.2),(2,0.3),(3,0.5)] 
} 

def func(value): 
    possible_values=d[value] 
    total=sum(p[-1] for p in possible_values) 
    random_value=random.random() 
    prob=possible_values[0][-1]/total 
    index=1 
    while index<len(possible_values) and prob<random_value: 
     prob+=possible_values[index][-1]/total 
     index+=1 
    return possible_values[index-1][0] 

if __name__=='__main__': 
    testcases=1000 
    cnt=[0,0,0] 
    for case in xrange(testcases): 
     answer=func(701) 
     cnt[answer-1]+=1 
    for i in xrange(3): 
     print "Got %d %f%% of the time"%(i+1,float(cnt[i])/testcases*100) 

यह बहुत नहीं है, लेकिन यह पहली बात यह है कि मन में आए है, और अपेक्षा के अनुरूप काम करने के लिए प्रकट होता है।

अंतराल [0,1) में यादृच्छिक मूल्य प्राप्त करने के लिए यह क्या होता है (random.random() का उपयोग करके)। यह तब उपयोग करता है कि यादृच्छिक मान अंतराल [0,0.2), [0.2,0.5) या [0.5,1) में आता है, यह पता लगाने के लिए कि कौन सा मान वापस करना है।

0

दो विचारों (मुझे, अलग विकल्प और तर्क के नाम में स्पष्टता के लिए अनुपात के साथ यह वर्णन करने के लिए अगर वे एक टपल में पैक कर रहे हैं आप "ज़िप" बचा सकता है की अनुमति देते हैं):

क) पूर्णांक अनुपात प्राप्त करने के लिए वजन को कम करें, फिर अनुपात के रूप में कई प्रतियां सूचीबद्ध करें और random.choice का उपयोग करें।

def choice_with_ratios(options, ratios): 
    tmp = sum([[v]*n for v, n in zip(options, ratios)], []) 
    return random.choice(tmp) 

ख) सामान्यीकृत वजन का उपयोग करें और संक्षेप जब तक आप एक यादृच्छिक उत्पन्न वर्दी मूल्य

def choice_with_weights(options, weights): 
    s = 0 
    r = random.random() 
    for v, w in zip(options, weights): 
     s += w 
     if s >= r: break 
    return v 

वैसे तक पहुँचने शुरू करते हैं, यदि पहले क्षेत्र एक प्रमुख के रूप में इस्तेमाल किया जाता है, तो आप इसे होना चाहिए एक शब्दकोश में, जैसे:

d = { 
    701: ((1, 0.2), (2, 0.3), (3, 0.5), 
    702: ((1, 0.3), (2, 0.2), (3, 0.5) 
} 
3

यह समझते हुए कि मेरा पहला जवाब गणित में काफी छोटी थी, मैंने एक नया विचार प्रस्तुत किया है। मेरा मानना ​​है कि एल्गोरिथ्म यहाँ अन्य उत्तर के कई के समान है, लेकिन इस कार्यान्वयन के लिए "सुंदर" अर्हता प्राप्त करने के सवाल की आवश्यकता (है कि अगर साधारण के बराबर होती है) लगता है:

def func(id): 
    rnd = random() 
    sum = 0 
    for row in d: 
     if row[0] == id: 
      sum = sum + row[2] 
      if rnd < sum: 
       return row[1] 
से उदाहरण डेटा के साथ

ओपी यह इस प्रकार है:

  • एक यादृच्छिक संख्या उठाओ 0 और 1.0
  • तो संख्या < 0.2 पहला तत्व
  • वरना अगर संख्या < 0.5 दूसरा तत्व वापसी है वापसी है के बीच
  • वरना (यदि संख्या < 1.0 है) तीसरे तत्व
0

तुम भी प्रत्येक मान के लिए एक 100 तत्व सूची बना सकते हैं वापसी, और फिर जाने random.choice एक वरीयता प्राप्त सूची जिसके सदस्य हैं से चयन करना

import random 
from collections import defaultdict 

d = ( 
    (701, 1, 0.2), 
    (701, 2, 0.3), 
    (701, 3, 0.5), 
    (702, 1, 0.2), 
    (702, 2, 0.3), 
    (702, 3, 0.5) 
) 

class WeightedLookup(object): 
    def __init__(self, valueTupleList): 
     self.valdict = defaultdict(list) 
     for key, val, prob in valueTupleList: 
      self.valdict[key] += [val]*(int)(prob*100) 

    def __getitem__(self,key): 
     return random.choice(self.valdict[key]) 


lookup = WeightedLookup(d) 

# test out our lookup distribution, sample it 100000 times 
res = { 1:0, 2:0, 3:0 } 
for i in range(100000): 
    res[lookup[701]] += 1 

# print how many times each value was returned 
for k in (1,2,3): 
    print k, res[k] 

प्रिंटों:

1 20059 
2 30084 
3 49857 
संबंधित मुद्दे