用Pyhon自带的 itertools 模块实现 组合、排列、组合(放回)、排列(放回),并计算每种情况出现的概率

引子

之所以想起来itertools,是看到了这么一个题目

题目是英文的,里面废话有点多,简单而言就是:
假设有一组只由01构成字符串,你可以随便猜3次,每次告诉你猜对了多少位。
然后问题来了,你要结合你这三次猜测结果,说说满足这个条件的字符串最多存在几个。

试着用python3写了一下解法:

"""这一段是2018年4月写的
# 载入数据
x = '6 3'
length,trytimes = [int(i) for i in x.split(' ')]
print(length,trytimes )
y = '''
000000 2
010100 4
111100 2
'''
r = [j.split(' ') for j in [i for i in y.strip().split('\n')]]
print(r)

# 比较两个字符串,返回相同位置上,字符一致的个数。
def match(a,b):
    count = 0
    if len(a)==len(b):
        for i in range(len(a)):
            if a[i]==b[i]:
                count+=1
    else:
        print('ERROR:',a,b)
    return str(count)

# 开始解题,构造字符串迭代器,遍历所有字符串,返回符合条件的字符串个数
import itertools
total = 0
z = itertools.product('01', repeat=length)
for i in z:
    g = ''.join(i)
    for j in range(trytimes):
        if match(r[j][0],g)!=r[j][1]:
            break
    else:
        total+=1

# 符合条件的
print(total)
"""


"""======================================
2021-03-04, 时隔3年再看, 重写一下
======================================"""
import numpy as np
from io import StringIO
from itertools import product

y = '''
000000 2
010100 4
111100 2
'''

narr= np.loadtxt(StringIO(y), delimiter=" ", dtype=[('sample', 'O'), ('matchNum', '<i4')])
sampleLength = len(narr[0][0])

def countMatch(x,y):
    return [char1==char2 for char1,char2 in zip(x,y)].count(True)

matchStrArr = list()
for enumvar in product('01', repeat=sampleLength):
    for sample,matchNum in narr:
        if countMatch(enumvar,sample)!=matchNum:
            break
    else:
        matchStrArr.append(''.join(enumvar))
print(len(matchStrArr),matchStrArr)

用itertools实现排列组合

因为昨天刚刚说完关于迭代器的特征,这里itertools实际上就是通过迭代器的方式,相当高效且节省内存。

将itertools中的排列组合,拿出来专门记录一下:
以“4个中抽取2个”为例(4个一样的乒乓球,分别写上ABCD,扔到不透明的箱子里。在看不到球的情况下,每次摸出1个球,摸2次),在不同的判定标准下,列出所有可能出现的结果

函数名称说明每种方式得到的结果数(数学计算公式)
combinations组合不分先后(AB==BA) C 4 2 = 4 ! 2 ! ( 4 − 2 ) ! = 4 ∗ 3 ∗ 2 ∗ 1 2 ∗ 1 ∗ 2 ∗ 1 = 6 C_4^2=\frac{4!}{2!(4-2)!}=\frac{4*3*2*1}{2*1*2*1}=6 C42=2!(42)!4!=21214321=6
permutations排列分先后(AB!=BA) A 4 2 = 4 ! ( 4 − 2 ) ! = 4 ∗ 3 ∗ 2 ∗ 1 2 ∗ 1 = 12 A_4^2=\frac{4!}{(4-2)!}=\frac{4*3*2*1}{2*1}=12 A42=(42)!4!=214321=12
combinations_with_replacement组合(放回)有放回,不分先后(AB==BA且存在AA,BB…) C 4 + 2 − 1 2 = 5 ! 2 ! ( 5 − 2 ) ! = 5 ∗ 4 ∗ 3 ∗ 2 ∗ 1 2 ∗ 1 ∗ 3 ∗ 2 ∗ 1 = 10 C_{4+2-1}^2=\frac{5!}{2!(5-2)!}=\frac{5*4*3*2*1}{2*1*3*2*1}=10 C4+212=2!(52)!5!=2132154321=10
product排列(放回)有放回,分先后(AB!=BA且存在AA,BB…) 4 2 = 16 4^2=16 42=16
# 为了输出结果好看,加了list(map(lambda x:''.join(x)……
print(list(map(lambda x:''.join(x),itertools.combinations('ABCD', 2))))
print(list(map(lambda x:''.join(x),itertools.permutations('ABCD', 2))))
print(list(map(lambda x:''.join(x),itertools.combinations_with_replacement('ABCD', 2))))
print(list(map(lambda x:''.join(x),itertools.product('ABCD', repeat=2))))
['AB', 'AC', 'AD', 'BC', 'BD', 'CD']
['AB', 'AC', 'AD', 'BA', 'BC', 'BD', 'CA', 'CB', 'CD', 'DA', 'DB', 'DC']
['AA', 'AB', 'AC', 'AD', 'BB', 'BC', 'BD', 'CC', 'CD', 'DD']
['AA', 'AB', 'AC', 'AD', 'BA', 'BB', 'BC', 'BD', 'CA', 'CB', 'CC', 'CD', 'DA', 'DB', 'DC', 'DD']

上面例子中的最后一项“排列(放回)”,其实就是笛卡尔积。所以也可以写成for循环嵌套。

for i in 'ABCD':
    for j in 'ABCD':
        print(i+j,end=' ')
AA AB AC AD BA BB BC BD CA CB CC CD DA DB DC DD 

每种结果出现的概率

2022-10-08 添加此段内容

python中存在一个random模块,用于构建随机事件,这使得我们想要粗略计算概率时,可以直接通过穷举的方法来计算每种结果出现的概率。

以上方的四种抽取方式为例:

import random
import itertools

def buildsample(method):
    '''按照指定的排列组合方式,构建出1个随机样本'''
    if method==0:
        sample = sorted(random.sample(Seed, ChoiceTimes)) # 组合(不分先后)
    elif method==1:
        sample = random.sample(Seed, ChoiceTimes) # 排列(分先后)
    elif method==2:
        sample = sorted([random.choice(Seed) for t in range(ChoiceTimes)]) # 组合(放回)(不分先后)
    elif method==3:
        sample = [random.choice(Seed) for t in range(ChoiceTimes)] # 排列(放回)(分先后)
    else:
        raise '未知方法'
    return tuple(sample)


# 随机生成样本,统计每种结果出现的频率
MethodDict = {
    0:'组合(不分先后)',
    1:'排列(分先后)',
    2:'组合(放回)(不分先后)',
    3:'排列(放回)(分先后)'
}
Seed = 'ABCD'   # 几个球
ChoiceTimes = 2 # 抽几次
RandomTimes = 1000000 # 随机次数

# 4种模式中,每个模式每种结果的概率
for method in MethodDict:
    ResultDict = dict() # 用于记录每种结果出现的次数
    for i in range(RandomTimes): # 随机生成,成千上万次,统计每种结果出现的次数
        sample = buildsample(method)
        ResultDict[sample] = ResultDict.get(sample,0)+1
    d = {''.join(k):round(v/RandomTimes, 3) for k,v in ResultDict.items()}  # 将每种结果出现的次数转为概率
    l = sorted(d.items(), key=lambda x:x[1], reverse=True) # 按概率从大到小排序
    # 输出计算结果
    print(f'{MethodDict[method]}{len(l)}种结果各自概率:') 
    [print(k,v) for k,v in l]
组合(不分先后)的6种结果各自概率:
AD 0.167
BD 0.167
CD 0.167
BC 0.166
AC 0.166
AB 0.166
排列(分先后)的12种结果各自概率:
DB 0.084
DA 0.084
BD 0.084
CA 0.084
BC 0.083
CB 0.083
AB 0.083
AC 0.083
AD 0.083
DC 0.083
BA 0.083
CD 0.083
组合(放回)(不分先后)的10种结果各自概率:
BC 0.125
AD 0.125
BD 0.125
AC 0.125
AB 0.125
CD 0.125
DD 0.063
CC 0.063
BB 0.062
AA 0.062
排列(放回)(分先后)的16种结果各自概率:
CA 0.063
CB 0.063
DD 0.063
AA 0.063
DA 0.063
DC 0.063
AB 0.063
BB 0.063
CD 0.063
AC 0.063
AD 0.062
BA 0.062
DB 0.062
CC 0.062
BD 0.062
BC 0.062

通过以上数据,我们可以近似得出“4抽2”在不同情况下,每种结果的概率:

4抽2方式存在多少种结果每种结果出现的概率
组合(不分先后)6 1 6 \frac{1}{6} 61
排列(分先后)12 1 12 \frac{1}{12} 121
组合(放回)(不分先后)10AB这种为 1 8 \frac{1}{8} 81,AA这种为 1 16 \frac{1}{16} 161
排列(放回)(分先后)16 1 16 \frac{1}{16} 161
  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值