Сгенерировать несколько случайных чисел, чтобы равное значение в python

Итак, вот сделка: я хочу (например) генерировать 4 псевдослучайных числа, которые при объединении будут равны 40. Как это может быть купол в python? Я мог бы генерировать случайное число 1-40, затем генерировать другое число между 1 и остальным и т.д., Но тогда у первого числа будет больше шансов "захватить" больше.

Ответ 1

b = random.randint(2, 38)
a = random.randint(1, b - 1)
c = random.randint(b + 1, 39)
return [a, b - a, c - b, 40 - c]

(Я предполагаю, что вам нужны целые числа, так как вы сказали "1-40", но это можно было бы легко обобщить для float.)

Вот как это работает:

  • вырезать общий диапазон в два раза, b. Нечетный диапазон - это то, что они будут по крайней мере на 2 ниже середины и не менее 2 выше. (Это происходит от вашего 1 минимального значения по каждому значению).
  • вырезать каждый из этих диапазонов двумя случайными способами. Опять же, оценки должны учитывать 1 минимум.
  • вернуть размер каждого фрагмента. Они добавят до 40.

Ответ 2

Здесь стандартное решение. Это похоже на ответ Лоуренса Гонсалвеса, но имеет два преимущества перед этим ответом.

  1. Он одинаков: каждая комбинация из 4-х положительных целых чисел, составляющих до 40, с равной вероятностью может придумать эту схему.

а также

  1. его легко адаптировать к другим суммам (7 номеров, суммируя до 100 и т.д.)
import random

def constrained_sum_sample_pos(n, total):
    """Return a randomly chosen list of n positive integers summing to total.
    Each such list is equally likely to occur."""

    dividers = sorted(random.sample(range(1, total), n - 1))
    return [a - b for a, b in zip(dividers + [total], [0] + dividers)]

Пример выходов:

>>> constrained_sum_sample_pos(4, 40)
[4, 4, 25, 7]
>>> constrained_sum_sample_pos(4, 40)
[9, 6, 5, 20]
>>> constrained_sum_sample_pos(4, 40)
[11, 2, 15, 12]
>>> constrained_sum_sample_pos(4, 40)
[24, 8, 3, 5]

Объяснение: существует взаимно-однозначное соответствие между (1) 4-мя кортежами (a, b, c, d) натуральных чисел, так что a + b + c + d == 40, и (2) тройками целых чисел (e, f, g) с 0 < e < f < g < 40, и последнее легко получить, используя random.sample. Соответствие задается как (e, f, g) = (a, a + b, a + b + c) в одном направлении, и (a, b, c, d) = (e, f - e, g - f, 40 - g) в обратном направлении.

Если вы хотите, чтобы неотрицательные целые числа (т.е. допускали 0) вместо положительных, то есть простое преобразование: если (a, b, c, d) - неотрицательные целые числа, суммирующие до 40 то (a+1, b+1, c+1, d+1) являются положительными целыми числами, суммирующимися в 44, и наоборот. Используя эту идею, мы имеем:

def constrained_sum_sample_nonneg(n, total):
    """Return a randomly chosen list of n nonnegative integers summing to total.
    Each such list is equally likely to occur."""

    return [x - 1 for x in constrained_sum_sample_pos(n, total + n)]

Графическая иллюстрация constrained_sum_sample_pos(4, 10), благодаря @FM. (Отредактировано немного.)

0 1 2 3 4 5 6 7 8 9 10  # The universe.
|                    |  # Place fixed dividers at 0, 10.
|   |     |       |  |  # Add 4 - 1 randomly chosen dividers in [1, 9]
  a    b      c    d    # Compute the 4 differences: 2 3 4 1

Ответ 3

Сгенерируйте 4 случайных числа, вычислите их сумму, разделите каждую на сумму и умножьте на 40.

Если вам нужны целые числа, это потребует небольшой неслучайности.

Ответ 4

Использовать полиномиальное распределение

from numpy.random import multinomial
multinomial(40, [1/4.] * 4)

Ответ 5

В диапазоне [1,37] (с разрешенными повторами) существует только 37 ^ 4 = 1,874,161 единиц из четырех целых чисел. Перечислите их, сохраните и подсчитайте перестановки, которые составляют до 40. (Это будет намного меньшее число, N).

Нарисуйте равномерно распределенные случайные целые числа K в интервале [0, N-1] и вернем K-ю перестановку. Это легко увидеть, чтобы гарантировать равномерное распределение по пространству возможных исходов, причем каждая позиция последовательности одинаково распределена. (Многие ответы, которые я вижу, будут иметь окончательный выбор, предвзятый ниже, чем первые три!)

Ответ 6

Предполагая, что вы хотите, чтобы они были равномерно распределены, и предполагая, что вы не хотите повторений

addends = []
picks = range(1, 34)
while sum(addends) != 40:
    addends = random.sample(picks, 3)
    if sum(addends) > 39:
        continue
    addends.append(40 - sum(addends))