किसी दिए गए (संख्यात्मक) वितरण के साथ यादृच्छिक संख्या उत्पन्न करें


132

मेरे पास विभिन्न मूल्यों के लिए कुछ संभावनाओं के साथ एक फ़ाइल है जैसे:

1 0.1
2 0.05
3 0.05
4 0.2
5 0.4
6 0.2

मैं इस वितरण का उपयोग कर यादृच्छिक संख्या उत्पन्न करना चाहूंगा। क्या कोई मौजूदा मॉड्यूल जो इसे संभालता है, वह मौजूद है? अपने दम पर कोड करना काफी आसान है (संचयी घनत्व फ़ंक्शन का निर्माण, एक यादृच्छिक मान उत्पन्न करें [0,1] और संबंधित मान चुनें) लेकिन ऐसा लगता है कि यह एक आम समस्या होनी चाहिए और शायद किसी ने इसके लिए फ़ंक्शन / मॉड्यूल बनाया हो यह।

मुझे इसकी आवश्यकता है क्योंकि मैं जन्मदिन की सूची बनाना चाहता हूं (जो मानक randomमॉड्यूल में किसी भी वितरण का पालन नहीं करते हैं )।


2
के अलावा random.choice()? आप उचित सूची के साथ मास्टर सूची बनाते हैं और एक को चुनते हैं। यह एक डुप्लिकेट प्रश्न है, निश्चित रूप से।
एस.लॉट


2
@ S.Lott वितरण में बड़े अंतर के लिए बहुत स्मृति गहन नहीं है?
लुकास मॉस्कोप्स 11

2
@ S.Lott: आपकी पसंद विधि संभवतः छोटी संख्या में होने वाली घटनाओं के लिए ठीक होगी, लेकिन जब यह आवश्यक नहीं है तो मैं बड़ी सूची बनाने से बचूंगा।
पफस्कु

5
@ S.Lott: ठीक है, लगभग 10000 * 365 = 3650000 = 3.6 मिलियन तत्व। मैं पायथन में मेमोरी के उपयोग के बारे में निश्चित नहीं हूं, लेकिन यह कम से कम 3.6M * 4B = 14.4MB है। एक बड़ी राशि नहीं है, लेकिन कुछ भी नहीं जिसे आपको या तो नजरअंदाज करना चाहिए जब एक समान सरल विधि होती है जिसे अतिरिक्त मेमोरी की आवश्यकता नहीं होती है।
पफुकु

जवाबों:


118

scipy.stats.rv_discreteहो सकता है कि आप क्या चाहते हैं। आप valuesपैरामीटर के माध्यम से अपनी संभावनाओं की आपूर्ति कर सकते हैं । आप rvs()यादृच्छिक संख्या उत्पन्न करने के लिए वितरण ऑब्जेक्ट की विधि का उपयोग कर सकते हैं ।

जैसा कि टिप्पणियों में यूजीन पखोमोव ने कहा है, आप उदाहरण के pलिए एक कीवर्ड पैरामीटर भी पास कर सकते हैंnumpy.random.choice()

numpy.random.choice(numpy.arange(1, 7), p=[0.1, 0.05, 0.05, 0.2, 0.4, 0.2])

यदि आप 3.6 या इसके बाद के संस्करण का उपयोग कर रहे हैं, तो आप random.choices()मानक पुस्तकालय से उपयोग कर सकते हैं - मार्क डिकिन्सन द्वारा उत्तर देखें ।


9
मेरी मशीन numpy.random.choice()पर लगभग 20 गुना तेज है।
यूजीन पखोमोव

9
यह मूल प्रश्न के बिल्कुल समान है। जैसे:numpy.random.choice(numpy.arange(1, 7), p=[0.1, 0.05, 0.05, 0.2, 0.4, 0.2])
यूजीन पखोमोव

1
@EugenePakhomov यह अच्छा है, मुझे नहीं पता था कि। मैं देख सकता हूँ कि आगे इस बात का उल्लेख है, लेकिन इसमें कोई उदाहरण कोड नहीं है और बहुत सारे upvotes नहीं हैं। मैं बेहतर दृश्यता के लिए इस उत्तर के लिए एक टिप्पणी जोड़ूंगा।
स्वेन मार्नाच

2
आश्चर्यजनक रूप से, rv_discrete.rvs () O (len (p) * size) समय और मेमोरी में काम करता है! जबकि पसंद () इष्टतम ओ (लेन (पी) + लॉग (लेन (पी)) * आकार) समय में चलने लगती है।
एलैक्सी

3
यदि आप Python 3.6 का उपयोग कर रहे हैं या नया है तो एक और उत्तर है जिसमें किसी भी अतिरिक्त पैकेज की आवश्यकता नहीं है।
मार्क रैनसम

113

पायथन 3.6 के बाद से, पायथन के मानक पुस्तकालय में इसके लिए एक समाधान है, अर्थात् random.choices

उदाहरण का उपयोग: चलो ओपी के प्रश्न में उन लोगों से मेल खाते हुए एक आबादी और वजन सेट करें:

>>> from random import choices
>>> population = [1, 2, 3, 4, 5, 6]
>>> weights = [0.1, 0.05, 0.05, 0.2, 0.4, 0.2]

अब choices(population, weights)एक एकल नमूना बनाता है:

>>> choices(population, weights)
4

वैकल्पिक कीवर्ड-ओनली तर्क kएक बार में एक से अधिक नमूने का अनुरोध करने की अनुमति देता है। यह मूल्यवान है क्योंकि random.choicesकिसी भी नमूने को तैयार करने से पहले कुछ तैयारी का काम करना पड़ता है जिसे हर बार कॉल करना पड़ता है; एक ही बार में कई नमूने तैयार करके, हमें केवल एक बार उस प्रारंभिक कार्य को करना होगा। यहां हम एक लाख नमूने तैयार करते हैं, और collections.Counterयह जांचने के लिए उपयोग करते हैं कि वितरण हमें मोटे तौर पर दिए गए वजन से मेल खाता है।

>>> million_samples = choices(population, weights, k=10**6)
>>> from collections import Counter
>>> Counter(million_samples)
Counter({5: 399616, 6: 200387, 4: 200117, 1: 99636, 3: 50219, 2: 50025})

क्या इसमें पायथन 2.7 संस्करण है?
abbas786

1
@ abbas786: इसमें निर्मित नहीं है, लेकिन इस प्रश्न के अन्य उत्तर सभी को पायथन 2.7 पर काम करना चाहिए। आप यादृच्छिक के लिए पायथन 3 स्रोत भी देख सकते हैं। यदि आप चाहें तो इसे कॉपी कर सकते हैं।
मार्क डिकिन्सन

27

सीडीएफ का उपयोग करके सूची तैयार करने का एक फायदा यह है कि आप द्विआधारी खोज का उपयोग कर सकते हैं। जबकि आपको प्रीप्रोसेसिंग के लिए O (n) समय और स्थान की आवश्यकता है, आप O (k log n) में k नंबर प्राप्त कर सकते हैं। चूंकि सामान्य पायथन सूची अक्षम हैं, इसलिए आप arrayमॉड्यूल का उपयोग कर सकते हैं ।

यदि आप निरंतर स्थान पर जोर देते हैं, तो आप निम्न कार्य कर सकते हैं; O (n) समय, O (1) स्थान।

def random_distr(l):
    r = random.uniform(0, 1)
    s = 0
    for item, prob in l:
        s += prob
        if s >= r:
            return item
    return item  # Might occur because of floating point inaccuracies

सूची में (आइटम, प्रोब) जोड़े का क्रम आपके कार्यान्वयन में मायने रखता है, है ना?
stackoverflowuser2010

1
@ stackoverflowuser2010: यह बात नहीं होनी चाहिए (फ्लोटिंग पॉइंट में
मोडुलो एरर

अच्छा लगा। मैंने इसे scipy.stats.rv_discrete की तुलना में 30% अधिक तेज़ पाया।
एस्पेन

1
काफी बार यह फ़ंक्शन अंतिम पंक्ति के कारण KeyError को फेंक देगा।
20

@DrunkenMaster: मुझे समझ नहीं आया। क्या आप जानते l[-1]हैं कि सूची का अंतिम तत्व क्या है?
sdcvvc

15

शायद यह देर से की तरह है। लेकिन आप उपयोग कर सकते हैं numpy.random.choice()गुजर, pपैरामीटर:

val = numpy.random.choice(numpy.arange(1, 7), p=[0.1, 0.05, 0.05, 0.2, 0.4, 0.2])

1
ओपी उपयोग नहीं करना चाहता है random.choice()- टिप्पणियों को देखें।
पोरबेलिंक

5
numpy.random.choice()random.choice()संभावना वितरण से पूरी तरह से अलग है और समर्थन करता है।
यूजीन पखोमोव

14

(ठीक है, मुझे पता है कि आप हटना-लपेटने के लिए कह रहे हैं, लेकिन हो सकता है कि उन घरेलू समाधानों का उपयोग आपके लिए पर्याप्त नहीं था। :-)

pdf = [(1, 0.1), (2, 0.05), (3, 0.05), (4, 0.2), (5, 0.4), (6, 0.2)]
cdf = [(i, sum(p for j,p in pdf if j < i)) for i,_ in pdf]
R = max(i for r in [random.random()] for i,c in cdf if c <= r)

मैंने छद्म पुष्टि की कि यह इस अभिव्यक्ति के आउटपुट को नेत्रहीन करके काम करता है:

sorted(max(i for r in [random.random()] for i,c in cdf if c <= r)
       for _ in range(1000))

यह प्रभावशाली दिखता है। केवल चीजों को संदर्भ में रखने के लिए, उपरोक्त कोड के लगातार 3 निष्पादन से परिणाम यहां दिए गए हैं: [: संभावित के साथ १ की गणना: ०.१ है: ११३ ’,: संभावना के साथ २ की गणना: ०.०५ है: ५५’, of की गिनती प्रोब के साथ ३: ०.०५ है: ५० ’, 0.2 प्रो के साथ ४ की गिनती: ०.२ है: २०१’,:: के साथ ५ की संभावना: ०.४ है: ३ '' ’,: प्रो के साथ ६ की गिनती: ०.२ है: १ ९ ३’]। ............. ['प्रोब के साथ 1 की गिनती: 0.1 है: 77', '2 की गिनती प्रोब के साथ: 0.05 है: 60', 'प्रो के साथ 3 की गणना: 0.05 है: 51 ',' प्रोब के साथ 4 की गिनती: 0.2 है: 193 ',' 5 की गिनती प्रोब के साथ: 0.4 है: 438 ',' प्रो के साथ 6 की गणना: 0.2 है: 181 '] ........ ..... और
वैभव

['प्रोब के साथ १ की गिनती: ०.१ है:'४ ’,' की संख्या २ के साथ प्रो: ०.०५ है: ५२’, 'की संख्या के साथ ३ की संभावना: ०.०५ है: ५३ ’,:: के साथ ४ की गिनती: ०.२ है: 210 ',' प्रोब के साथ 5 की गिनती: 0.4 है: 405 ',' प्रोब के साथ 6 की गिनती: 0.2 है: 196 ']
वैभव

एक प्रश्न, मैं अधिकतम कैसे लौटूं (i ..., अगर 'i' एक वस्तु है?
वैभव

@ वैभव iकोई वस्तु नहीं है।
मार्सेलो कैंटोस

6

मैंने एक कस्टम निरंतर वितरण से यादृच्छिक नमूनों को खींचने के लिए एक समाधान लिखा ।

मुझे आपके लिए एक समान उपयोग-मामले के लिए इसकी आवश्यकता थी (यानी किसी दिए गए प्रायिकता वितरण के साथ यादृच्छिक तिथियां उत्पन्न करना)।

आपको बस फ़नक्शन random_custDistऔर लाइन की आवश्यकता है samples=random_custDist(x0,x1,custDist=custDist,size=1000)। बाकी सजावट ^ ^ है।

import numpy as np

#funtion
def random_custDist(x0,x1,custDist,size=None, nControl=10**6):
    #genearte a list of size random samples, obeying the distribution custDist
    #suggests random samples between x0 and x1 and accepts the suggestion with probability custDist(x)
    #custDist noes not need to be normalized. Add this condition to increase performance. 
    #Best performance for max_{x in [x0,x1]} custDist(x) = 1
    samples=[]
    nLoop=0
    while len(samples)<size and nLoop<nControl:
        x=np.random.uniform(low=x0,high=x1)
        prop=custDist(x)
        assert prop>=0 and prop<=1
        if np.random.uniform(low=0,high=1) <=prop:
            samples += [x]
        nLoop+=1
    return samples

#call
x0=2007
x1=2019
def custDist(x):
    if x<2010:
        return .3
    else:
        return (np.exp(x-2008)-1)/(np.exp(2019-2007)-1)
samples=random_custDist(x0,x1,custDist=custDist,size=1000)
print(samples)

#plot
import matplotlib.pyplot as plt
#hist
bins=np.linspace(x0,x1,int(x1-x0+1))
hist=np.histogram(samples, bins )[0]
hist=hist/np.sum(hist)
plt.bar( (bins[:-1]+bins[1:])/2, hist, width=.96, label='sample distribution')
#dist
grid=np.linspace(x0,x1,100)
discCustDist=np.array([custDist(x) for x in grid]) #distrete version
discCustDist*=1/(grid[1]-grid[0])/np.sum(discCustDist)
plt.plot(grid,discCustDist,label='custom distribustion (custDist)', color='C1', linewidth=4)
#decoration
plt.legend(loc=3,bbox_to_anchor=(1,0))
plt.show()

सतत कस्टम वितरण और असतत नमूना वितरण

इस समाधान का प्रदर्शन सुनिश्चित करने के लिए कामचलाऊ है, लेकिन मैं पठनीयता पसंद करता हूं।


1

उनके आधार पर वस्तुओं की एक सूची बनाएं weights:

items = [1, 2, 3, 4, 5, 6]
probabilities= [0.1, 0.05, 0.05, 0.2, 0.4, 0.2]
# if the list of probs is normalized (sum(probs) == 1), omit this part
prob = sum(probabilities) # find sum of probs, to normalize them
c = (1.0)/prob # a multiplier to make a list of normalized probs
probabilities = map(lambda x: c*x, probabilities)
print probabilities

ml = max(probabilities, key=lambda x: len(str(x)) - str(x).find('.'))
ml = len(str(ml)) - str(ml).find('.') -1
amounts = [ int(x*(10**ml)) for x in probabilities]
itemsList = list()
for i in range(0, len(items)): # iterate through original items
  itemsList += items[i:i+1]*amounts[i]

# choose from itemsList randomly
print itemsList

लक्ष्य सूची को छोटा बनाने के लिए एक अनुकूलन सबसे बड़ी सामान्य विभाजक द्वारा राशियों को सामान्य करने के लिए हो सकता है।

इसके अलावा, यह दिलचस्प हो सकता है।


यदि आइटमों की सूची बड़ी है, तो यह अतिरिक्त मेमोरी का उपयोग कर सकता है।
पफस्कु

@ पफकु सहमत। बस एक समाधान, दूसरा जो मेरे दिमाग में आया (पहला था "वजन संभावना अजगर" :) जैसी किसी चीज़ की खोज करना।
खटिक

1

एक और जवाब, शायद तेज :)

distribution = [(1, 0.2), (2, 0.3), (3, 0.5)]  
# init distribution  
dlist = []  
sumchance = 0  
for value, chance in distribution:  
    sumchance += chance  
    dlist.append((value, sumchance))  
assert sumchance == 1.0 # not good assert because of float equality  

# get random value  
r = random.random()  
# for small distributions use lineair search  
if len(distribution) < 64: # don't know exact speed limit  
    for value, sumchance in dlist:  
        if r < sumchance:  
            return value  
else:  
    # else (not implemented) binary search algorithm  

1
from __future__ import division
import random
from collections import Counter


def num_gen(num_probs):
    # calculate minimum probability to normalize
    min_prob = min(prob for num, prob in num_probs)
    lst = []
    for num, prob in num_probs:
        # keep appending num to lst, proportional to its probability in the distribution
        for _ in range(int(prob/min_prob)):
            lst.append(num)
    # all elems in lst occur proportional to their distribution probablities
    while True:
        # pick a random index from lst
        ind = random.randint(0, len(lst)-1)
        yield lst[ind]

सत्यापन:

gen = num_gen([(1, 0.1),
               (2, 0.05),
               (3, 0.05),
               (4, 0.2),
               (5, 0.4),
               (6, 0.2)])
lst = []
times = 10000
for _ in range(times):
    lst.append(next(gen))
# Verify the created distribution:
for item, count in Counter(lst).iteritems():
    print '%d has %f probability' % (item, count/times)

1 has 0.099737 probability
2 has 0.050022 probability
3 has 0.049996 probability 
4 has 0.200154 probability
5 has 0.399791 probability
6 has 0.200300 probability

1

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

यह एक सरल उदाहरण है (मैंने यहाँ पूर्णांकों का उपयोग किया है)

l=[(20, 'foo'), (60, 'banana'), (10, 'monkey'), (10, 'monkey2')]
def get_cdf(l):
    ret=[]
    c=0
    for i in l: c+=i[0]; ret.append((c, i[1]))
    return ret

def get_random_item(cdf):
    return cdf[bisect.bisect_left(cdf, (random.randint(0, cdf[-1][0]),))][1]

cdf=get_cdf(l)
for i in range(100): print get_random_item(cdf),

get_cdfसमारोह में यह 20, 60, 10, 10 से बदल जाएगा 20 में, 20 + 60, 20 + 60 + 10, 20 + 60 + 10 + 10

अब हम 20 + 60 + 10 + 10 तक यादृच्छिक संख्या का उपयोग करते हैं random.randintतब हम वास्तविक मूल्य को तेजी से प्राप्त करने के लिए बाइसेक्ट का उपयोग करते हैं


0

आप NumPy रैंडम सैंपलिंग वितरण पर एक नज़र रखना चाहते हैं


3
संख्यात्मक कार्य भी केवल अपने स्वयं के निर्दिष्ट करने के लिए बिना किसी समर्थन के सीमित संख्या में वितरण का समर्थन करते हैं।
पफुकु


0

इनमें से कोई भी उत्तर विशेष रूप से स्पष्ट या सरल नहीं है।

यहां एक स्पष्ट, सरल विधि है जो काम करने की गारंटी है।

accumulate_normalize_probabilities एक डिक्शनरी लेती है pजो प्रतीकों को संभावनाओं या फ्रिक्वेंसी पर मैप करती है । यह ट्यूपल्स की प्रयोग करने योग्य सूची का चयन करता है जिसमें से चयन करना है।

def accumulate_normalize_values(p):
        pi = p.items() if isinstance(p,dict) else p
        accum_pi = []
        accum = 0
        for i in pi:
                accum_pi.append((i[0],i[1]+accum))
                accum += i[1]
        if accum == 0:
                raise Exception( "You are about to explode the universe. Continue ? Y/N " )
        normed_a = []
        for a in accum_pi:
                normed_a.append((a[0],a[1]*1.0/accum))
        return normed_a

पैदावार:

>>> accumulate_normalize_values( { 'a': 100, 'b' : 300, 'c' : 400, 'd' : 200  } )
[('a', 0.1), ('c', 0.5), ('b', 0.8), ('d', 1.0)]

यह काम क्यों करता है

संचय कदम (पहले प्रतीक के मामले में या 0) ही है और पिछले प्रतीकों संभावना या आवृत्ति के बीच एक अंतराल में प्रत्येक प्रतीक बदल जाता है। इन अंतरालों को (और इस प्रकार प्रदान किए गए वितरण को नमूना करने के लिए) का उपयोग केवल सूची के माध्यम से सूची में कदम रखकर किया जा सकता है जब तक कि अंतराल में यादृच्छिक संख्या 0.0 -> 1.0 (पहले से तैयार) वर्तमान प्रतीक के अंतराल अंत-बिंदु के बराबर या उससे कम नहीं हो।

मानकीकरण कुछ मूल्य के लिए सुनिश्चित सब कुछ रकम बनाने के लिए जरूरत से हमें मुक्त करता है। सामान्यीकरण के बाद प्रायिकताओं के "वेक्टर" 1.0 तक डूब जाते हैं।

कोड के बाकी चयन और वितरण से एक मनमाने ढंग से लंबे नमूना पैदा करने के लिए नीचे है:

def select(symbol_intervals,random):
        print symbol_intervals,random
        i = 0
        while random > symbol_intervals[i][1]:
                i += 1
                if i >= len(symbol_intervals):
                        raise Exception( "What did you DO to that poor list?" )
        return symbol_intervals[i][0]


def gen_random(alphabet,length,probabilities=None):
        from random import random
        from itertools import repeat
        if probabilities is None:
                probabilities = dict(zip(alphabet,repeat(1.0)))
        elif len(probabilities) > 0 and isinstance(probabilities[0],(int,long,float)):
                probabilities = dict(zip(alphabet,probabilities)) #ordered
        usable_probabilities = accumulate_normalize_values(probabilities)
        gen = []
        while len(gen) < length:
                gen.append(select(usable_probabilities,random()))
        return gen

उपयोग:

>>> gen_random (['a','b','c','d'],10,[100,300,400,200])
['d', 'b', 'b', 'a', 'c', 'c', 'b', 'c', 'c', 'c']   #<--- some of the time

-1

यहाँ यह करने का एक और प्रभावी तरीका है:

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

अपनी संबंधित संभावनाओं का उपयोग करके अनुक्रमित (या आइटम) नमूना / उठाया (प्रतिस्थापन के साथ) देता है:

def resample(weights, n):
    beta = 0

    # Caveat: Assign max weight to max*2 for best results
    max_w = max(weights)*2

    # Pick an item uniformly at random, to start with
    current_item = random.randint(0,n-1)
    result = []

    for i in range(n):
        beta += random.uniform(0,max_w)

        while weights[current_item] < beta:
            beta -= weights[current_item]
            current_item = (current_item + 1) % n   # cyclic
        else:
            result.append(current_item)
    return result

लूप में प्रयुक्त अवधारणा पर एक संक्षिप्त टिप्पणी। हम संचयी बीटा से वर्तमान आइटम के वजन को कम करते हैं, जो कि यादृच्छिक रूप से समान रूप से निर्मित एक संचयी मूल्य है, और आइटम को खोजने के लिए वर्धमान वर्तमान सूचकांक होता है, जिसका वजन बीटा के मूल्य से मेल खाता है।

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