线段切割法就是将红包的总金额视为一段定长的长度,从中随机切(红包份数-1)个点,然后依次将线段分配。
下面是python的实现,当出现相同切割点时重新取点。这个不是最好的解决相同切割点的方法,如果可以实现顺序记录每一个取到的随机点,后续再生成随机点时,取点的范围中删去这些点,这样应该会更好。这个优化后续完善。。
#!/usr/bin/python
#-*- coding utf-8 -*-
#使用线段切割法:将红包总金额视为一段长度,从中随机取人数减1个切割点将线段切割为等同人数的份数然后分配
__author__ = 'Luo Chenyi'
__data__ = '2020-12-18'
__vertion__ = 'v1.00.00'
import random
#MANUAL_SET = True
MANUAL_SET = False
if MANUAL_SET == True:
total_momey = int(input("Please input the amount of money:"))
total_num = int(input("How many people:"))
total_times = int(input("How many cycles:"))
else:
total_momey = int(100)
total_num = int(10)
total_times = int(1000)
variance_exam_times = int(10)
#输出每次的抢红包结果
#DEBUG_INF_FOR_EACH_RED_PACKET_SNATCHING = True
DEBUG_INF_FOR_EACH_RED_PACKET_SNATCHING = False
#输入:
# momey: 总金额
# num: 总人数
# times: 总次数
#返回:
# variance: 进行了times抢红包的方差
def red_packet(money, num, times=1):
if money <= 0:
raise AttributeError('The amount should greater than 0.')
elif num <= 0:
raise AttributeError('The num should greater than 0.')
#参数初始化和定义
current_money = money * 100 #当前剩余的钱,初始值为money,乘100倍表示精确到分
avg = (current_money * times)/ num #平均每人抢到的总金额 = 总金额 / 总人数
variance = 0 #方差, 用1000次红包的总金额来计算方差,方差越小,表示越公平
circle_times = 0 #循环次数
L = [] #定义一个空表,记录每个人抢到的红包金额
CutPoint = [] #定义一个空表,记录线段的切割点
for i in range(0, num):
L.append(0)
while circle_times < times:
circle_times += 1
if DEBUG_INF_FOR_EACH_RED_PACKET_SNATCHING == True:
#输出本次抢红包结果
print('********************************************')
print('Number of red packets snatched: %d' %circle_times)
#切割点生成和排序,分num份则切(num-1)个点
if num == 1:
CutPoint = [0]
else:
#生成切割点
CutPoint = list(set([random.randint(1, current_money - 1) for i in range(num - 1)]))
while len(CutPoint) != num - 1:
#有相同切割点则重新生成切割点
CutPoint = list(set([random.randint(1, current_money - 1) for i in range(num - 1)]))
#切割点按从小到大排列
CutPoint.sort()
if DEBUG_INF_FOR_EACH_RED_PACKET_SNATCHING == True:
print('Length of CurPoint : %d' %len(CutPoint))
print('CurPoint is :', CutPoint)
for i in range(0, num - 1):
if i == 0:
amount_of_i = CutPoint[i]
else:
amount_of_i = CutPoint[i] - CutPoint[i - 1]
L[i] += amount_of_i
if DEBUG_INF_FOR_EACH_RED_PACKET_SNATCHING == True:
print(' id[%d] get %.02f' %(i + 1, (float)(amount_of_i / 100)))
amount_of_i = current_money - CutPoint[num - 2]
L[num - 1] += amount_of_i
if DEBUG_INF_FOR_EACH_RED_PACKET_SNATCHING == True:
print(' id[%d] get %.02f' %(num, (float)(amount_of_i / 100)))
#输出多次抢红包的最终结果
current_money = 0
variance = 0
print('********************************************')
print(' Total money is %d, for %d people, circle %d times' %(money, num, times))
print(' Total amount of red packets %.02f' %(money * times))
print(' Average value is %.02f' %(float)(avg / 100))
for i in range(0, num):
current_money += L[i]
variance += pow(abs((L[i] - avg) / times), 2)
print(' id[%d] totally get %.02f average each time %.02f' %(i + 1, (float)(L[i] / 100), (float)(L[i] / 100 / times)))
variance = (float)(variance / num / 10000)
print(' The variance : %.2f' %variance)
print(' Total amount of received : %.2f' %(current_money / 100))
if (money * times) != (current_money / 100):
raise AttributeError('Total amount of red packets(%.02f) is not equal to total amount of received(%.02f)' %(money * times, current_money / 10))
print('********************************************')
return (int)(variance * 100)
if __name__ == '__main__':
variance_sum = 0
for i in range(variance_exam_times):
variance_sum += red_packet(total_momey, total_num, total_times)
print('Sum of all variance exam(%d times) is %.02f' %(variance_exam_times, (float)(variance_sum / 100)))
print('Done')