年關將至,數一數手上爲數不多的零花錢,再想一想老家的哥哥姐姐們的衆多小孩以及二胎……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