《生活中的脚本》- 过年回家如何省钱地给孩子们发红包~

年关将至,数一数手上为数不多的零花钱,再想一想老家的哥哥姐姐们的众多小孩以及二胎……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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值