《生活中的腳本》- 過年回家如何省錢地給孩子們發紅包~

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章