21

それぞれがありそうもない変数を持つキーがたくさんあります。これらのキーのいずれかをランダムに選択したいのですが、可能性が低い (可能性が高い) オブジェクトよりも、可能性が低い (キー、値) が選択される可能性が低いことを望んでいます。何か提案があれば、できれば私が使用できる既存の python モジュールがあるかどうか疑問に思っています。それ以外の場合は、自分で作成する必要があります。

random モジュールをチェックアウトしました。これを提供していないようです。

それぞれが 2,455 個のオブジェクトを含む 1000 個の異なるオブジェクト セットに対して、このような選択を何百万回も行う必要があります。各セットは相互にオブジェクトを交換するため、ランダム チューザーは動的である必要があります。2,433 オブジェクトの 1000 セット、つまり 2,433 百万オブジェクト。低メモリ消費は非常に重要です。そして、これらの選択はアルゴリズムの大部分ではないため、このプロセスを非常に高速にする必要があります。CPU 時間は限られています。

どうも

アップデート:

わかりました、私はあなたの提案を賢く検討しようとしましたが、時間がとても限られています...

二分探索木のアプローチを調べましたが、リスクが高すぎるようです (複雑で複雑です)。他の提案はすべて ActiveState レシピに似ています。私はそれを取り、より効率的にすることを期待して少し修正しました:

def windex(dict, sum, max):
    '''an attempt to make a random.choose() function that makes
    weighted choices accepts a dictionary with the item_key and
    certainty_value as a pair like:
    >>> x = [('one', 20), ('two', 2), ('three', 50)], the
    maximum certainty value (max) and the sum of all certainties.'''
    n = random.uniform(0, 1)
    sum = max*len(list)-sum 
    for key, certainty in dict.iteritems():
        weight = float(max-certainty)/sum
        if n < weight:
            break
        n = n - weight
    return key

確実性の合計と最大の確実性を動的に維持することで、効率が向上することを期待しています。さらなる提案は大歓迎です。皆さんのおかげで、時間と労力を大幅に節約でき、効果を高めています。信じられないほどです。どうも!どうも!どうも!

更新 2:

一度により多くの選択肢を選択できるようにすることで、より効率的にすることにしました。これは、本質的に動的であるため、私のアルゴでは精度が許容範囲内に失われます。とにかく、ここに私が今持っているものがあります:

def weightedChoices(dict, sum, max, choices=10):
    '''an attempt to make a random.choose() function that makes
    weighted choices accepts a dictionary with the item_key and
    certainty_value as a pair like:
    >>> x = [('one', 20), ('two', 2), ('three', 50)], the
    maximum certainty value (max) and the sum of all certainties.'''
    list = [random.uniform(0, 1) for i in range(choices)]
    (n, list) = relavate(list.sort())
    keys = []
    sum = max*len(list)-sum 
    for key, certainty in dict.iteritems():
        weight = float(max-certainty)/sum
        if n < weight:
            keys.append(key)
            if list: (n, list) = relavate(list)
            else: break
        n = n - weight
    return keys
def relavate(list):
    min = list[0]
    new = [l - min for l in list[1:]]
    return (min, new)

まだ試していません。ご意見/ご提案がございましたら、お気軽にお寄せください。どうも!

Update3:

私は一日中、Rex Logan の回答のタスクに合わせたバージョンに取り組んできました。オブジェクトと重みの 2 つの配列ではなく、実際には特別なディクショナリ クラスです。Rex のコードはランダムなインデックスを生成するので、これは非常に複雑です... 私はまた、私のアルゴで起こることに似たようなテスト ケースをコーディングしました (しかし、実際に試してみるまではわかりません!)。基本的な原則は次のとおりです。キーが頻繁にランダムに生成されるほど、再度生成される可能性は低くなります。

import random, time
import psyco
psyco.full()

class ProbDict():
    """
    Modified version of Rex Logans RandomObject class. The more a key is randomly
    chosen, the more unlikely it will further be randomly chosen. 
    """
    def __init__(self,keys_weights_values={}):
        self._kw=keys_weights_values
        self._keys=self._kw.keys()
        self._len=len(self._keys)
        self._findSeniors()
        self._effort = 0.15
        self._fails = 0
    def __iter__(self):
        return self.next()
    def __getitem__(self, key):
        return self._kw[key]
    def __setitem__(self, key, value):
        self.append(key, value)
    def __len__(self):
        return self._len
    def next(self):
        key=self._key()
        while key:
            yield key
            key = self._key()
    def __contains__(self, key):
        return key in self._kw
    def items(self):
        return self._kw.items()
    def pop(self, key):  
        try:
            (w, value) = self._kw.pop(key)
            self._len -=1
            if w == self._seniorW:
                self._seniors -= 1
                if not self._seniors:
                    #costly but unlikely:
                    self._findSeniors()
            return [w, value]
        except KeyError:
            return None
    def popitem(self):
        return self.pop(self._key())
    def values(self):
        values = []
        for key in self._keys:
            try:
                values.append(self._kw[key][1])
            except KeyError:
                pass
        return values
    def weights(self):
        weights = []
        for key in self._keys:
            try:
                weights.append(self._kw[key][0])
            except KeyError:
                pass
        return weights
    def keys(self, imperfect=False):
        if imperfect: return self._keys
        return self._kw.keys()
    def append(self, key, value=None):
        if key not in self._kw:
            self._len +=1
            self._kw[key] = [0, value]
            self._keys.append(key)
        else:
            self._kw[key][1]=value
    def _key(self):
        for i in range(int(self._effort*self._len)):
            ri=random.randint(0,self._len-1) #choose a random object
            rx=random.uniform(0,self._seniorW)
            rkey = self._keys[ri]
            try:
                w = self._kw[rkey][0]
                if rx >= w: # test to see if that is the value we want
                    w += 1
                    self._warnSeniors(w)
                    self._kw[rkey][0] = w
                    return rkey
            except KeyError:
                self._keys.pop(ri)
        # if you do not find one after 100 tries then just get a random one
        self._fails += 1 #for confirming effectiveness only
        for key in self._keys:
            if key in self._kw:
                w = self._kw[key][0] + 1
                self._warnSeniors(w)
                self._kw[key][0] = w
                return key
        return None
    def _findSeniors(self):
        '''this function finds the seniors, counts them and assess their age. It
        is costly but unlikely.'''
        seniorW = 0
        seniors = 0
        for w in self._kw.itervalues():
            if w >= seniorW:
                if w == seniorW:
                    seniors += 1
                else:
                    seniorsW = w
                    seniors = 1
        self._seniors = seniors
        self._seniorW = seniorW
    def _warnSeniors(self, w):
        #a weight can only be incremented...good
        if w >= self._seniorW:
            if w == self._seniorW:
                self._seniors+=1
            else:
                self._seniors = 1
                self._seniorW = w
def test():
    #test code
    iterations = 200000
    size = 2500
    nextkey = size 


    pd = ProbDict(dict([(i,[0,i]) for i in xrange(size)]))
    start = time.clock()
    for i in xrange(iterations):
        key=pd._key()
        w=pd[key][0]
        if random.randint(0,1+pd._seniorW-w):
            #the heavier the object, the more unlikely it will be removed
            pd.pop(key)
        probAppend = float(500+(size-len(pd)))/1000
        if random.uniform(0,1) < probAppend:
            nextkey+=1
            pd.append(nextkey)
    print (time.clock()-start)*1000/iterations, "msecs / iteration with", pd._fails, "failures /", iterations, "iterations"
    weights = pd.weights()
    weights.sort()
    print "avg weight:", float(sum(weights))/pd._len, max(weights), pd._seniorW, pd._seniors, len(pd), len(weights)
    print weights
test()

どんなコメントでも大歓迎です。@Darius: あなたの二分木は複雑すぎて私には複雑です。そして、その葉を効率的に取り除くことができるとは思いません... Thx all

4

12 に答える 12

26

このアクティブステート レシピは、わかりやすいアプローチを提供します。具体的には、重みを事前に正規化する必要のないコメントのバージョンです。

import random

def weighted_choice(items):
    """items is a list of tuples in the form (item, weight)"""
    weight_total = sum((item[1] for item in items))
    n = random.uniform(0, weight_total)
    for item, weight in items:
        if n < weight:
            return item
        n = n - weight
    return item

アイテムのリストが大きい場合、これは遅くなります。その場合、おそらく二分探索の方が良いでしょう...しかし、サンプルサイズが小さい場合は、ほとんど利益が得られず、書くのがより複雑になります。 そのルートをたどりたい場合は、Python でのバイナリ検索アプローチの例を次に示します。

(データセットで両方の方法の簡単なパフォーマンス テストを行うことをお勧めします。この種のアルゴリズムに対するさまざまなアプローチのパフォーマンスは、多くの場合、少し直感的ではありません。)


編集:私は興味があったので、私は自分のアドバイスを受けて、いくつかのテストを行いました.

4 つのアプローチを比較しました。

上記の weighted_choice 関数。

次のような二分探索選択関数:

def weighted_choice_bisect(items):
    added_weights = []
    last_sum = 0

    for item, weight in items:
        last_sum += weight
        added_weights.append(last_sum)

    return items[bisect.bisect(added_weights, random.random() * last_sum)][0]

1 のコンパイル バージョン:

def weighted_choice_compile(items):
    """returns a function that fetches a random item from items

    items is a list of tuples in the form (item, weight)"""
    weight_total = sum((item[1] for item in items))
    def choice(uniform = random.uniform):
        n = uniform(0, weight_total)
        for item, weight in items:
            if n < weight:
                return item
            n = n - weight
        return item
    return choice

2 のコンパイル バージョン:

def weighted_choice_bisect_compile(items):
    """Returns a function that makes a weighted random choice from items."""
    added_weights = []
    last_sum = 0

    for item, weight in items:
        last_sum += weight
        added_weights.append(last_sum)

    def choice(rnd=random.random, bis=bisect.bisect):
        return items[bis(added_weights, rnd() * last_sum)][0]
    return choice

次に、次のような選択肢の大きなリストを作成しました。

choices = [(random.choice("abcdefg"), random.uniform(0,50)) for i in xrange(2500)]

そして、過度に単純なプロファイリング関数:

def profiler(f, n, *args, **kwargs):
    start = time.time()
    for i in xrange(n):
        f(*args, **kwargs)
    return time.time() - start

結果:

(関数への 1,000 回の呼び出しにかかった秒数。)

  • 単純な未コンパイル: 0.918624162674
  • コンパイルされていないバイナリ: 1.01497793198
  • シンプルなコンパイル: 0.287325024605
  • コンパイルされたバイナリ: 0.00327413797379

「コンパイルされた」結果には、choice 関数を 1 回コンパイルするのにかかった平均時間が含まれます。(私は 1,000 回のコンパイルの時間を測定し、その時間を 1,000 で割り、結果を選択関数の時間に追加しました。)

したがって、めったに変更されない項目と重みのリストがある場合は、バイナリ コンパイルされた方法が断然最速です。

于 2009-02-08T20:26:28.310 に答える
6

元の投稿へのコメントで、Nicholas Leonardは、交換とサンプリングの両方を高速にする必要があることを示唆しています。その場合のアイデアは次のとおりです。私はそれを試していません。

サンプリングのみを高速にする必要がある場合は、値の配列とその確率の現在の合計を使用して、現在の合計に対してバイナリ検索を実行できます(キーは均一な乱数です)--O(log( n))操作。ただし、交換では、エントリが交換された後に表示されるすべてのランニングサム値を更新する必要があります(O(n)操作)。(リストの終わり近くにあるアイテムのみを交換することを選択できますか?私はそうしないと思います。)

それでは、両方の操作でO(log(n))を目指しましょう。配列の代わりに、サンプリングするセットごとに2分木を保持します。リーフは、サンプル値とその(正規化されていない)確率を保持します。ブランチノードは、その子の合計確率を保持します。

xサンプリングするには、0からルートの合計確率までの均一な乱数を生成し、ツリーを下降します。各ブランチで、左の子に合計確率がある場合は、左の子を選択します<= x。それ以外の場合は、左の子供の確率を減算してx右に進みます。到達したリーフ値を返します。

交換するには、ツリーから葉を削除し、それにつながるブランチを調整します(合計確率を下げ、単一の子のブランチノードを切り取ります)。リーフを宛先ツリーに挿入します。リーフを配置する場所を選択できるため、バランスを保ちます。各レベルでランダムな子を選ぶことはおそらく十分です-それが私が始めるところです。各親ノードの確率を上げて、ルートに戻ります。

現在、サンプリングと交換の両方が平均してO(log(n))です。(バランスを保証する必要がある場合、簡単な方法は、サブツリー全体の葉の数を保持するブランチノードに別のフィールドを追加することです。葉を追加するときは、各レベルで葉の少ない子を選択します。これにより、ツリーは削除だけで不均衡になります。セット間にトラフィックが適度に均等にある場合、これは問題にはなりませんが、そうである場合は、トラバーサルの各ノードのリーフカウント情報を使用して削除中にロ​​ーテーションを選択します。)

更新:リクエストに応じて、基本的な実装を示します。まったく調整していません。使用法:

>>> t1 = build_tree([('one', 20), ('two', 2), ('three', 50)])
>>> t1
Branch(Leaf(20, 'one'), Branch(Leaf(2, 'two'), Leaf(50, 'three')))
>>> t1.sample()
Leaf(50, 'three')
>>> t1.sample()
Leaf(20, 'one')
>>> t2 = build_tree([('four', 10), ('five', 30)])
>>> t1a, t2a = transfer(t1, t2)
>>> t1a
Branch(Leaf(20, 'one'), Leaf(2, 'two'))
>>> t2a
Branch(Leaf(10, 'four'), Branch(Leaf(30, 'five'), Leaf(50, 'three')))

コード:

import random

def build_tree(pairs):
    tree = Empty()
    for value, weight in pairs:
        tree = tree.add(Leaf(weight, value))
    return tree

def transfer(from_tree, to_tree):
    """Given a nonempty tree and a target, move a leaf from the former to
    the latter. Return the two updated trees."""
    leaf, from_tree1 = from_tree.extract()
    return from_tree1, to_tree.add(leaf)

class Tree:
    def add(self, leaf):
        "Return a new tree holding my leaves plus the given leaf."
        abstract
    def sample(self):
        "Pick one of my leaves at random in proportion to its weight."
        return self.sampling(random.uniform(0, self.weight))
    def extract(self):
        """Pick one of my leaves and return it along with a new tree
        holding my leaves minus that one leaf."""
        return self.extracting(random.uniform(0, self.weight))        

class Empty(Tree):
    weight = 0
    def __repr__(self):
        return 'Empty()'
    def add(self, leaf):
        return leaf
    def sampling(self, weight):
        raise Exception("You can't sample an empty tree")
    def extracting(self, weight):
        raise Exception("You can't extract from an empty tree")

class Leaf(Tree):
    def __init__(self, weight, value):
        self.weight = weight
        self.value = value
    def __repr__(self):
        return 'Leaf(%r, %r)' % (self.weight, self.value)
    def add(self, leaf):
        return Branch(self, leaf)
    def sampling(self, weight):
        return self
    def extracting(self, weight):
        return self, Empty()

def combine(left, right):
    if isinstance(left, Empty): return right
    if isinstance(right, Empty): return left
    return Branch(left, right)

class Branch(Tree):
    def __init__(self, left, right):
        self.weight = left.weight + right.weight
        self.left = left
        self.right = right
    def __repr__(self):
        return 'Branch(%r, %r)' % (self.left, self.right)
    def add(self, leaf):
        # Adding to a random branch as a clumsy way to keep an
        # approximately balanced tree.
        if random.random() < 0.5:
            return combine(self.left.add(leaf), self.right)
        return combine(self.left, self.right.add(leaf))
    def sampling(self, weight):
        if weight < self.left.weight:
            return self.left.sampling(weight)
        return self.right.sampling(weight - self.left.weight)
    def extracting(self, weight):
        if weight < self.left.weight:
            leaf, left1 = self.left.extracting(weight)
            return leaf, combine(left1, self.right)
        leaf, right1 = self.right.extracting(weight - self.left.weight)
        return leaf, combine(self.left, right1)

更新2:別の問題に答える際に、Jason Orendorffは、古典的なヒープ構造のように二分木を配列で表すことにより、二分木を完全にバランスを保つことができると指摘します。(これにより、ポインターに費やされるスペースも節約されます。)この問題に彼のコードを適応させる方法については、その回答に対する私のコメントを参照してください。

于 2009-02-09T01:12:33.923 に答える
2

それから約3年…

numpy を使用する場合、おそらく最も簡単なオプションは を使用することnp.random.choiceです。これは、可能な値のリストと、各値に関連付けられた確率のオプションのシーケンスを取ります。

import numpy as np

values = ('A', 'B', 'C', 'D')
weights = (0.5, 0.1, 0.2, 0.2)

print ''.join(np.random.choice(values, size=60, replace=True, p=weights))
# ACCADAACCDACDBACCADCAAAAAAADACCDCAADDDADAAACCAAACBAAADCADABA
于 2014-01-19T00:25:27.990 に答える
2

加重ランダムのこの PHP 実装を Pythonに移植することをお勧めします。特に、バイナリ検索ベースの 2 番目のアルゴリズムは、速度に関する懸念に対処するのに役立ちます。

于 2009-02-08T20:20:31.083 に答える
2

これは疑似コードで行う古典的な方法で、random.random() は 0 から 1 までのランダムな浮動小数点数を返します。

let z = sum of all the convictions
let choice = random.random() * z 
iterate through your objects:
    choice = choice - the current object's conviction
    if choice <= 0, return this object
return the last object

choice例: 2 つのオブジェクトがあり、1 つは重みが 2 で、もう 1 つは重みが 4 であるとします。0から 6 までの数値を生成します。2 が引かれ、最初のオブジェクトが選択されます。選択が 2 から 6 の間である場合 (4/6 = 2/3 の確率で発生)、最初の減算では依然として選択が > 0 であり、2 番目の減算では 2 番目のオブジェクトが選択されます。

于 2009-02-08T20:30:14.570 に答える
2

各オブジェクトに重みを付けたいとします。重量が大きいほど、発生する可能性が高くなります。より正確には、probx = weight/sum_all_weights です。

次に、0 から sum_all_weights の範囲で乱数を生成し、それを各オブジェクトにマップします。

このコードを使用すると、ランダム インデックスを生成できます。これは、高速化のためにオブジェクトが作成されるときにマップされます。オブジェクトのすべてのセットが同じ分布を持っている場合は、1 つの RandomIndex オブジェクトだけで十分です。

import random

class RandomIndex:
    def __init__(self, wlist):
        self._wi=[]
        self._rsize=sum(wlist)-1
        self._m={}
        i=0
        s=wlist[i]
        for n in range(self._rsize+1):
            if n == s:
                i+=1
                s+=wlist[i]
            self._m[n]=i    

    def i(self):
        rn=random.randint(0,self._rsize)
        return self._m[rn]


sx=[1,2,3,4]


wx=[1,10,100,1000] #weight list
ri=RandomIndex(wx)

cnt=[0,0,0,0]

for i in range(1000):
    cnt[ri.i()] +=1  #keep track of number of times each index was generated

print(cnt)  
于 2009-02-08T20:08:31.440 に答える
2

私はこのレシピを使用します。オブジェクトに重みを追加する必要がありますが、これは単純な比率であり、それらをタプルのリスト (オブジェクト、信念/(信念の合計)) に入れます。これは、リスト内包表記を使用して簡単に行うことができます。

于 2009-02-08T20:16:43.853 に答える
1

最も簡単な方法は、random.choice (一様分布を使用) を使用して、ソース コレクション内のオブジェクトの発生頻度を変えることです。

>>> random.choice([1, 2, 3, 4])
4

... vs:

>>> random.choice([1, 1, 1, 1, 2, 2, 2, 3, 3, 4])
2

したがって、オブジェクトには基本発生率 (n) があり、1 から n 個のオブジェクトが確定率の関数としてソース コレクションに追加されます。この方法は非常に簡単です。ただし、個別のオブジェクトの数が多い場合や、判定率を非常に細かくする必要がある場合は、かなりのオーバーヘッドが発生する可能性があります。

あるいは、一様分布を使用して複数の乱数を生成し、それらを合計すると、平均近くに発生する数は、極値近くに発生する可能性が高くなります (2 つのサイコロを振って、7 対 12 または 2 が出る確率を考えてみてください)。次に、オブジェクトを有罪率で並べ替え、複数のダイスロールを使用して数値を生成します。これを使用して計算し、オブジェクトにインデックスを付けることができます。平均に近い数値を使用して確信度の低いオブジェクトにインデックスを付け、極値に近い数値を使用して確信度の高いアイテムにインデックスを付けます。「面の数」と「サイコロ」の数を変更することで、特定のオブジェクトが選択される正確な確率を変えることができます (オブジェクトをバケツに入れ、面の数が少ないダイスを使用する方が簡単な場合があります)。各オブジェクトを特定の結果に関連付けようとしています):

>>> die = lambda sides : random.randint(1, sides)
>>> die(6)
3
>>> die(6) + die(6) + die(6)
10
于 2009-02-08T20:01:37.323 に答える
1

(1 年後) 確率の異なるランダム オブジェクトに対する Walker のエイリアス メソッドは、非常に高速で非常にシンプルです。

于 2010-01-12T17:58:54.287 に答える
1

これは、特別な確率分布に対するより良い答えです.Rex Loganの答えが対象になっているようです. 分布は次のようになります。各オブジェクトには 0 から 100 までの整数の重みがあり、その確率はその重みに比例します。それが現在受け入れられている答えなので、これは考える価値があると思います。

したがって、101 個のビンの配列を保持します。各ビンには、特定の重みを持つすべてのオブジェクトのリストが保持されます。各ビンは、そのすべてのオブジェクトの重量も認識しています。

サンプリングするには:総重量に比例してランダムにビンを選びます。(これには、標準的なレシピの 1 つを使用します。線形検索またはバイナリ検索です。) 次に、ビンからオブジェクトを一様に無作為に選びます。

オブジェクトを転送するには: オブジェクトをビンから取り出し、ターゲットのビンに入れ、両方のビンの重みを更新します。(サンプリングに二分探索を使用している場合は、使用する実行中の合計も更新する必要があります。多くのビンがないため、これはまだかなり高速です。)

于 2009-02-09T20:31:49.490 に答える
1

これを行うための非常に簡単で単純な方法は、各値に重みを設定することであり、多くのメモリは必要ありません。

これを行うには、おそらくハッシュ/辞書を使用できます。

やりたいことは、乱数xを、選択したいもののセット全体に掛けて合計し、その結果をセット内のオブジェクトの数で割ることです。

擬似コード:

objectSet = [(object1, weight1), ..., (objectN, weightN)]
sum = 0
rand = random()
for obj, weight in objectSet
    sum = sum+weight*rand
choice = objectSet[floor(sum/objectSet.size())]

編集:非常に大きなセット(O(n))でコードがどれほど遅くなるかを考えました。次の疑似コードは O(log(n)) で、基本的に二分探索を使用しています。

objectSet = [(object1, weight1), ..., (objectN, weightN)]
sort objectSet from less to greater according to weights
choice = random() * N # where N is the number of objects in objectSet
do a binary search until you have just one answer

Python でのバイナリ検索の実装はネット全体にあるため、ここで繰り返す必要はありません。

于 2009-02-08T20:35:52.467 に答える
0

あまり大きな数ではないため、より高速な関数で必要でした。Visual C++ では次のようになります。

#undef _DEBUG // disable linking with python25_d.dll
#include <Python.h>
#include <malloc.h>
#include <stdlib.h>

static PyObject* dieroll(PyObject *, PyObject *args)
{
    PyObject *list;
    if (!PyArg_ParseTuple(args, "O:decompress", &list))
        return NULL;

    if (!PyList_Check(list)) 
        return PyErr_Format(PyExc_TypeError, "list of numbers expected ('%s' given)", list->ob_type->tp_name), NULL;

    int size = PyList_Size(list);

    if (size < 1)
        return PyErr_Format(PyExc_TypeError, "got empty list"), NULL;

    long *array = (long*)alloca(size*sizeof(long));

    long sum = 0;
    for (int i = 0; i < size; i++) {
        PyObject *o = PyList_GetItem(list, i);

        if (!PyInt_Check(o))
            return PyErr_Format(PyExc_TypeError, "list of ints expected ('%s' found)", o->ob_type->tp_name), NULL;
        long n = PyInt_AsLong(o);
        if (n == -1 && PyErr_Occurred())
            return NULL;
        if (n < 0)
            return PyErr_Format(PyExc_TypeError, "list of positive ints expected (negative found)"), NULL;

        sum += n; //NOTE: integer overflow
        array[i] = sum;
    }

    if (sum <= 0)
        return PyErr_Format(PyExc_TypeError, "sum of numbers is not positive"), NULL;

    int r = rand() * (sum-1) / RAND_MAX; //NOTE: rand() may be too small (0x7fff).    rand() * sum may result in integer overlow.

    assert(array[size-1] == sum);
    assert(r < sum && r < array[size-1]);
    for (int i = 0; i < size; ++i)
    {
        if (r < array[i])
            return PyInt_FromLong(i);
    }
    return PyErr_Format(PyExc_TypeError, "internal error."), NULL;
}

static PyMethodDef module_methods[] = 
{
    {"dieroll", (PyCFunction)dieroll, METH_VARARGS, "random index, beased on weights" },
    {NULL}  /* Sentinel */
};

PyMODINIT_FUNC initdieroll(void) 
{
    PyObject *module = Py_InitModule3("dieroll", module_methods, "dieroll");
    if (module == NULL)
        return;
}
于 2009-06-27T21:12:33.557 に答える