一、代码
import time
from collections import Counter
import pandas as pd
from itertools import permutations
from itertools import combinations_with_replacement
from scipy.special import comb
def op_all_combs(in_list, n):
# 13个不相同的球有放回的抽取4个
out_list = []
for j in list(combinations_with_replacement(in_list, n)):
out_list.append(j)
return out_list
def op_all_arrange(unarr_list):
arred_list = []
for k in unarr_list:
arred_list.append(set(permutations(k)))
return arred_list
def extract_list(unex_list):
exed_list = []
for i in unex_list:
for j in i:
exed_list.append(j)
return exed_list
def mix_tuple(tup_1, tup_2):
tup_mix = [tup_1[0], tup_2[0], tup_1[1], tup_2[1], tup_1[2], tup_2[2], tup_1[3]]
return tup_mix
def inst_bck_2(in_list):
# 加入一对括号,对应所有位置的情况
out_strs = []
poitions = [(0, 4), (2, 6), (4, 8), (0, 6), (2, 8)]
for poition in poitions:
str_copy = in_list[:]
str_copy.insert(poition[0], '(')
str_copy.insert(poition[1], ')')
out_strs.append(''.join(str_copy))
# 加入两对括号
in_list.insert(0, '(')
in_list.insert(4, ')')
in_list.insert(6, '(')
in_list.insert(10, ')')
out_strs.append(''.join(in_list))
return out_strs
def type_cvt(in_dict):
counter_result = sorted(list(in_dict.values()), reverse=True)
if counter_result == [4]:
return 1
elif counter_result == [3, 1]:
return 16
elif counter_result == [2, 2]:
return 36
elif counter_result == [2, 1, 1]:
return 96
elif counter_result == [1, 1, 1, 1]:
return 256
# 区分花色和数字,任意一个牌型组合都有24种可能,取决于取牌的顺序
# 只关心数字,四种类型的牌型,是不同的。故只考虑数字和花色,不考虑取牌顺序,共有1820种可能
s_time = time.time()
"""
num_min:卡牌范围的下限
num_max: 卡牌范围的上限
syms_list:可选运算符构成的列表
syms: 本次选用的运算符
"""
num_min = 1
num_max = 13
nums = [str(n) for n in range(num_min, num_max+1)]
all_sum = int(comb(num_max*4, 4))
syms_list = [['+', '-'], ['+', '-', '*'], ['+', '-', '*', '/'], ['+', '-', '*', '/', '%'], ['+', '-', '*', '/', '//'],
['+', '-', '*', '/', '%', '//']]
syms = syms_list[2]
num_combs = op_all_combs(nums, 4) # ['1', '2', '3', '4']
sym_combs = op_all_combs(syms, 3) # ['*', '*', '*']
num_arrs = op_all_arrange(num_combs) # 数字部分排列组合后的集合
sym_arrs = op_all_arrange(sym_combs) # 运算符部分排列组合后的集合
num_arrs = extract_list(num_arrs)
sym_arrs = extract_list(sym_arrs)
types = [type_cvt(Counter(comb)) for comb in num_combs]
# num_fits能够算出24的数字元组构成的列表
# eva_equs是能够算出24的数字和运算符组成的命令
num_fits = {','.join(sorted(k)): [] for k in num_combs}
eva_equs = []
for num_arr in num_arrs:
for sym_arr in sym_arrs:
add_key = list(num_arr)
add_key.sort()
add_key = ','.join(add_key)
if set(sym_arr).issubset({'+', '-'}):
# 由数字和加法运算符得到的
evas = ''.join(mix_tuple(num_arr, sym_arr))
if eval(evas) == 24:
num_fits[add_key].append(evas)
else:
evas = inst_bck_2(mix_tuple(num_arr, sym_arr))
try:
for eva in evas:
if eval(eva) == 24:
if add_key in num_fits:
num_fits[add_key].append(eva)
except ZeroDivisionError:
pass
# 打印程序运行的时间
print("Time used: {0}s.\n".format(time.time() - s_time))
out_csv = "num_combs.csv"
df = pd.DataFrame.from_dict(num_fits, orient='index')
# types记录牌组的数字类型对应的个数,aaaa型牌有1种,aaab型牌有16种...
df.insert(0, 'types', types)
# 将csv导出
df.to_csv(out_csv)
# 第一步,去除所有的none所在的行
df = df[~df[0].isin([None])]
# 第二步,计算剩下的新的df在types列的总和
row_sum = df['types'].sum()
# percent:抽到有解数字组合的概率
percent = row_sum / all_sum * 100
print('Precent is {0}%'.format(percent))
# 将计算结果写入到一个文本中
log_name = 'log.txt'
with open(log_name, 'a+') as f_obj:
f_obj.write('\n'+str(nums)+'\n')
f_obj.write(str(syms)+'\n')
f_obj.write(str(percent)+'%'+'\n')
二、结果
Excel文件:储存所有数字组合对应的解的情况
txt文件:记录各个可选运算符、数字范围情况下有解的概率
三、概率
若将有解定义为——从4种花色、数字范围为1~n共计4n张扑克牌中随机抽取4张牌,牌面上的数字可以通过运算符(加减乘除)得到24点。
则不同运算符和卡牌范围对应的有解概率为: