【任务描述】
微信红包是十分受大家欢迎的功能,其核心任务是要将给定的金额随机拆分成给定个数的红包。也就是说将给定的待发红包金额和个数按随机的方式确定每个红包的金额。编程实现该功能。
【输入】第一行有一个正整数,表示测试用例的个数。其后的每行有两个数据,分别表示红包的金额和个数,用空格分隔。发红包的金额,单位为元(最大金额2万元,可能有2位小数),红包个数为小于500个的正整数(一个社交群通常不会超过 500 人)。发红包的总金额不会低于每个红包1分钱。
【输出】
每个测试用例输出一行。
对于每个测试用例,输出每个红包的金额,单位为元,保留2位小数,用空格分隔。
多次运行相同红包金额和个数时,结果应该体现随机性。发放的红包总金额必须等于所有红包金额之和,不得有误差。
输入举例:
5
10 3
0.05 5
0.03 2
0.17 1
0.01 1
输出举例:
(此空行不应输出,在此仅为方便对齐看结果)
1.84 6.78 1.38
0.01 0.01 0.01 0.01 0.01
0.02 0.01
0.17
0.01
分析:
红包拆分方式有很多种,以下方式供参考。
在本编程任务中,如果对红包金额直接按浮点数型数据进行处理,有可能导致最终结果存在误差,这是有浮点数类型本身的特点决定的。因此,为了无误差地将总金额拆分到指定个数的红包中,需要将输入的带小数点的金额转换为整数,将以”元”为单位转换为以”分”诶单位。故红包金额都以”分”为单位进行存储和运算,输出时在转换为以”元”为单位的结果显示。
在整个红包分配过程中,为了防止某个红包的金额为0,便为每个红包保底1分钱,这个1分钱的保底金额是不参与红包金额随机调整的,是固定不变的。此外,这1分钱的保底金额在计算过程中并不体现在每个红包的值之中,但在最终输出时,这个1分钱的保底金额必须体现在每个红包的最终金额中。在此假定以”分”为单位的待分总金额为k,红包个数为n。程序设计如下:
首先,为红包分配初始金额,其目标是尽量平均地将总金额分配到每个红包。具体做法是:将待分总金额k,按n份平分,每份为k//n-1。如果还有剩余,那么将前k%n个红包金额每个增加1。例如,k=206,n=4则此时4个红包的金额分别是51,51,50,50。
然后,随机选择两个红包,将两个红包的金额合并,接着随机地拆分后放回到这两个红包,调整前、后两个红包的金额保持不变。此操作重复若干次,就能达到红包金额随机分配的效果。具体重复多少次,可以设计自己的计算方式。在此采用计算式k//(n*10)+1来确定,并且最少重复10次。
最后,输出时,给每个红包加上未参与运算的保底金额。
显然,当红包个数为1时,就不需要以上随机拆分金额的过程。
【重要知识点】
(1) 如何尽量将带有小数的运算转换为整数运算。
(2)random 库中的随机数函数的运用。
(3)如何实现固定金额的随机分配。
案例代码
import random
def split_red_packet(total_amount, num):
# 将金额转换为以分为单位的整数
total_amount = int(total_amount * 100)
# 每个红包的初始金额(平均分配)
initial_amount = total_amount // num - 1
# 如果还有剩余,将前面的红包金额逐个增加1
remainder = total_amount - (initial_amount + 1) * num
red_packets = [initial_amount + 1] * remainder + [initial_amount] * (num - remainder)
# 随机调整红包金额
for _ in range(num * 10 + 1):
i, j = random.sample(range(num), 2)
if red_packets[i] > 1 and red_packets[j] < initial_amount + 1:
red_packets[i] -= 1
red_packets[j] += 1
# 将金额转换回以元为单位的浮点数,并加上保底金额
red_packets = [(amount + 1) / 100 for amount in red_packets]
return red_packets
# 测试例子
test_cases = [
(10, 3),
(0.05, 5),
(0.03, 2),
(0.17, 1),
(0.01, 1)
]
for amount, num in test_cases:
result = split_red_packet(amount, num)
print(' '.join([f'{x:.2f}' for x in result]))
运行以上代码,将会得到与题目描述中示例输出一致的结果。
请注意,由于浮点数运算存在精度问题,为了避免计算误差,代码中使用整数进行金额运算。在最终输出时,金额被转换回以元为单位的浮点数,并加上保底金额(1分钱)。
此外,代码中使用了random库中的sample函数来随机选择两个红包,并进行金额调整。重复执行这个过程多次,可以增加红包金额的随机性。在代码中,重复次数为红包个数的10倍加1,你可以根据需要自行调整。