Генерация случайных чисел с заданным (числовым) распределением - PullRequest
98 голосов
/ 24 ноября 2010

У меня есть файл с некоторыми вероятностями для различных значений, например:

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

Я хотел бы сгенерировать случайные числа, используя это распределение.Существует ли существующий модуль, который обрабатывает это?Довольно просто написать код самостоятельно (создать функцию кумулятивной плотности, сгенерировать случайное значение [0,1] и выбрать соответствующее значение), но, похоже, это должно быть распространенной проблемой, и, возможно, кто-то создал функцию / модуль дляэто.

Мне это нужно, потому что я хочу сгенерировать список дней рождения (которые не следуют ни за каким распространением в стандартном модуле random).

Ответы [ 13 ]

1 голос
/ 24 ноября 2010

Возможно, вы захотите взглянуть на NumPy Случайные распределения выборки

0 голосов
/ 29 декабря 2015

Вот более эффективный способ сделать это:

Просто вызовите следующую функцию с вашим массивом 'weights' (принимая индексы в качестве соответствующих элементов) и no.образцов необходимо.Эта функция может быть легко изменена для обработки упорядоченной пары.

Возвращает индексы (или элементы), отобранные / выбранные (с заменой) с использованием их соответствующих вероятностей:

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

Краткое примечание о концепции, используемой в цикле while,Мы уменьшаем вес текущего элемента из совокупной бета-версии, которая представляет собой совокупное значение, построенное равномерно случайным образом, и увеличиваем текущий индекс, чтобы найти элемент, вес которого соответствует значению бета-версии.

0 голосов
/ 28 февраля 2013

Ни один из этих ответов не является особенно ясным или простым.

Вот ясный, простой метод, который гарантированно сработает.

аккумулировать_нормальные_проблемы принимает словарь 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
...