年关将至,数一数手上为数不多的零花钱,再想一想老家的哥哥姐姐们的众多小孩以及二胎……T-T 我忽然不想回去了~ T-T我也是个宝宝啊~
然而毕竟去年就给小孩子们许诺过说今年开始发的来着~
于是,机智的我构思了如下一套方案!兼顾娱乐性与实用性!发出水平!发出高度!而且重点是!能!省!钱!
问题描述
发红包游戏的设计:
1. 重点是在小孩聚集在一块的时候,发出若干个随机金额的红包。充分利用某某某某心理学常识,使小孩子们的满意程度最大化。
2. 细节:
- 所有红包要尽可能平均分布,例如线性分布
- 所有红包的纸币张数必须完全一致
- 计算塞红包的纸币的方案,纸币数量尽可能少
此时就轮到我们伟大的脚本出场了~
脚本解决
1.定义的一些常量
包括纸币的面值、红包最小值、红包最大值【好像暴露了什么
以及后来才发现需要一定的“误差”值~【不然计算不出解决方案……
Enum_Money_1 = 1
Enum_Money_5 = 5
Enum_Money_10 = 10
Enum_Money_20 = 20
Enum_Money_50 = 50
Enum_Money_100 = 100
Money_All = [
Enum_Money_1,
Enum_Money_5,
Enum_Money_10,
Enum_Money_20,
Enum_Money_50,
Enum_Money_100,
];
Money_All_R = Money_All[::-1]
g_pocket_min = 10
g_pocket_max = 100
# 允许的误差值
g_money_error = 3;
2. 递归的计算每个红包包多少
# 计算红包方案的递归函数
def _GetCountListRecur( money_left, cur_count_left, cur_loop_idx):
'''
@param money: 钱的总数
@param count: 张数
@return: [[], ] 解决方案列表,为空表示无解
算法:
从大钱到小钱,依次遍历其所有可能的张数(小钱的最大张数由循环内的大钱的张数决定);
如果找到一个数目恰好可以的,那么加入解决方案列表。
'''
if cur_count_left == 0 and money_left == 0:
return [[], ]
elif cur_loop_idx == len(Money_All_R)-1:
if abs(money_left - cur_count_left * Money_All_R[cur_loop_idx]) <= g_money_error:
return [[cur_count_left, ], ]
else:
return []
result_list = []
money_type = Money_All_R[cur_loop_idx]
max_count = money_left // money_type
for guess_count in range(min(max_count, cur_count_left)+1):
next_money_left = money_left - money_type * guess_count;
next_count_left = cur_count_left - guess_count;
for next_result in _GetCountListRecur(next_money_left, next_count_left, cur_loop_idx+1):
result_list.append([guess_count, ] + next_result)
return result_list;
# 封装下前边的函数(计算所有红包方案的列表)
def GetCountList( moneyTotal, countTotal ):
return _GetCountListRecur( moneyTotal, countTotal, 0)
3. 给定小孩个数,获得最小的有解的红包方案,及其所需纸币张数
# 根据小孩个数,计算每人应得的红包里的金额
def GetMoneyDistri( peopleCount ):
res = range( g_pocket_min, g_pocket_max, (g_pocket_max-g_pocket_min)/peopleCount) + [g_pocket_max,]
return [int(round(i)) for i in res]
# 对于若干个小孩子,计算最少需要的纸币张数
def GetPocketOfPeopleCount( peopleCount ):
money_distri = GetMoneyDistri(peopleCount)
moneyTotal = sum(money_distri)
guess_count = 0;
while guess_count <= moneyTotal:
guess_count += 1
pocket_list_all = []
for money in money_distri:
pocket_list = GetCountList(money, guess_count)
if len(pocket_list) == 0:
pocket_list_all = []
break
else:
pocket_list_all.append(pocket_list[0])
if len(pocket_list_all) != 0:
for money, pocket in zip(money_distri, pocket_list_all):
print money, pocket
return guess_count
4. 计算结果
结果如下~
在完全没有误差的情况下,8和9个小孩是无解的~所以我加了误差=3……
3元的误差……还是可以接受的吧~
例:
小孩个数为8时:
10 [0, 0, 0, 0, 1, 3]
21 [0, 0, 0, 0, 4, 0]
32 [0, 0, 0, 2, 2, 0]
43 [0, 0, 0, 4, 0, 0]
54 [0, 0, 2, 1, 0, 1]
65 [0, 0, 3, 0, 1]
76 [0, 1, 0, 2, 1, 0]
87 [0, 1, 1, 1, 1, 0]
98 [0, 1, 2, 0, 1, 0]
100 [0, 1, 2, 1]
git地址:
https://github.com/YgritteSnow/PythonScript/tree/master/PythonScripts/杂七杂八/如何给几个小孩发同样张数但钱数不同的红包.py