如何执行选择两个项而不进行替换的乘法规则?

时间:2019-01-14 14:48:49

标签: sympy

我正在尝试使用sympy来重现一个盒子包含三个弹珠的示例:

  • 红色
  • 白色
  • 蓝色

将随机绘制两颗弹珠,而无需替换。

问:先绘制红色大理石然后再绘制白色大理石的机会是什么?

我已经能够使用乘法规则通过 hard-coding P()实例来计算此值,这些实例包装在选择第一个大理石之前的初始分布,然后在选择第二个大理石之前的分布:

from sympy.stats import DiscreteUniform, density, P
from sympy import symbols, Eq

# Coloured marbles
R, W, B = symbols('R W B') 

# Select first marble without replacement
PFirstSelection  = P(Eq(DiscreteUniform('FirstSeletion',   (R, W, B)), R))

# Select second marble - Red is not longer available because it was selected without replacement
PSecondSelection = P(Eq(DiscreteUniform('SecondSelection', (W, B)   ), W)) 

print(PFirstSelection)
# 1/3

print(PSecondSelection)
# 1/2

# Multiplication rule
print(PFirstSelection * PSecondSelection)
# 1/6

有没有更好的方法可以通过sympy实现这一目标?

2 个答案:

答案 0 :(得分:1)

在这种情况下,您最好使用组合功能。

DiscreteUniform似乎不适合在创建后更改元素。

from sympy.functions.combinatorial.numbers import nC, nP

print(1 / nP(3, 2)) # 1/6

如果您不关心订单,

print(nP(2, 2) / nP(3, 2)) # 1/3

已编辑。 (并且还针对python3进行了修改)

对于M个事物中的N个,您只需执行以下操作即可

from sympy.functions.combinatorial.numbers import nC, nP

def pickProb(candidates, picks, ordered=False):
    picks_num = len(picks)
    numerator = nP(picks_num, picks_num) if ordered else 1
    denominator = nP(len(candidates), picks_num)
    return numerator / denominator

print(pickProb('RWB', 'RW')) # 1/6
print(pickProb('RWBrwba', 'Ra')) # 1/42
print(pickProb('RWBrwba', 'RWa')) # 1/210
print(pickProb('RWBrwba', 'RWa', ordered=True)) # 1/35

并且组合功能还可以处理重复项,例如'R','R','W','B'。

from operator import mul
from sympy.functions.combinatorial.numbers import nC, nP

def pickProb(candidates, picks):
    picks_num = len(picks)
    c_counts = {}
    for c in candidates:
        c_counts[c] = c_counts[c] + 1 if c in c_counts else 1
    p_counts = {}
    for p in picks:
        p_counts[p] = p_counts[p] + 1 if p in p_counts else 1
    combinations = reduce(mul, [nP(c_counts[x], p_counts[x]) for x in p_counts.keys()], 1)
    denominator = nP(len(candidates), picks_num) / combinations
    return 1 / denominator

print(pickProb('RWBra', 'RWa')) # 1/60
print(pickProb('RRRWa', 'RWa')) # 1/20
print(pickProb('RRRWa', 'RRa')) # 1/10

但是DiscreteUniform不能,因为这种情况不是“统一的”。

from sympy.stats import DiscreteUniform, density, P, Hypergeometric
from sympy import Symbol, Eq

deck = DiscreteUniform('M', 'RRWB')
print(density(deck).dict) # {W: 1/4, R: 1/4, B: 1/4}
print(P(Eq(deck, Symbol('R')))) # 1/4

答案 1 :(得分:1)

我认为您使用的是正确的sympy,但是您可以改善使用python的方式(例如,更通用,更实用,更通用,没有硬编码)。

例如:

from sympy.stats import DiscreteUniform, density, P
from sympy import symbols, Eq
from itertools import accumulate


def ToSet(value):
    return set(value.split(' '))


def ProbaOfPick(pickSet, fromSet, operationTag):
    return P(Eq(DiscreteUniform(operationTag, symbols(fromSet)), symbols(pickSet)))


def PickWithoutReplacement(allset, picklist, probaFunc):
    currentSet = allset
    probaSeq = []
    operationSeq = []
    for pick in picklist:
        operationTag = "picking: " + pick
        newP = probaFunc(pick, currentSet, operationTag)
        operationSeq.append(operationTag + " from " + str(currentSet))
        probaSeq.append(newP)
        currentSet -= set(pick)
    return (operationSeq, probaSeq)


allset = ToSet('R W B Y Ma G1 G2')
picks = 'R', 'W', 'G2'
operationSeq, probaSeq = PickWithoutReplacement(allset, picks, ProbaOfPick)

probas = list(accumulate(probaSeq, lambda a, b: a*b))

for op in operationSeq:
    print(op)
print(probas)

您还可以将均匀分布更改为任何非均匀分布。

编辑:添加了依赖项注入(ProbaOfPick-> probaFunc)。

此代码只是入门。

结果:

picking: R from {'G2', 'Ma', 'Y', 'B', 'R', 'G1', 'W'}
picking: W from {'G2', 'Ma', 'Y', 'B', 'G1', 'W'}
picking: G2 from {'G2', 'Ma', 'Y', 'B', 'G1'}
[1/7, 1/42, 1/210]

下一步:允许每个步骤选择1个以上,允许非均匀概率分布等