問題描述:
在日常生活中搶紅包的時候,輸入紅包金額和紅包個數(shù)可以自動生成n個紅包。其本質(zhì)是生成n個隨機數(shù),和為一個給定的數(shù)??紤]利用Python來實現(xiàn)。
思路一:
分割法,把紅包總額 k 看成一根長度為 k 的線段,假設(shè)現(xiàn)在需要分成 n 份,那么在這根線段上隨機產(chǎn)生 n-1 個點,將線段分成 n 份,分割后的每一段線段即為要求的隨機數(shù)。

代碼實現(xiàn):
import random
def func1(amount,num):
list1 = []
for i in range(0,num-1):
a = random.randint(0,amount) # 生成 n-1 個隨機節(jié)點
list1.append(a)
list1.sort() # 節(jié)點排序
list1.append(amount) # 設(shè)置第 n 個節(jié)點為amount,即總金額
list2 = []
for i in range(len(list1)):
if i == 0:
b = list1[i] # 第一段長度為第 1 個節(jié)點 - 0
else:
b = list1[i] - list1[i-1] # 其余段為第 n 個節(jié)點 - 第 n-1 個節(jié)點
list2.append(b)
print(list2)
結(jié)果:
輸入:func1(100 , 12)
輸出:[3, 0, 18, 2, 2, 27, 15, 1, 8, 1, 20, 3]
思路二:
還是假設(shè)總額為 k,分成 n 個紅包。采用依次生成點的方式。算法邏輯為:生成第一個隨機數(shù) x1,x1 服從均值為 k / n (即紅包總額除以紅包個數(shù))、方差為一個常數(shù) sigma 的正態(tài)分布;此時,剩余紅包總額 k1為:(紅包總額 k) - (隨機數(shù)1 x1),生成第二個隨機數(shù) x2,x2服從均值為 k1 / (n - 1) (即剩余紅包總額除以剩余紅包個數(shù))、方差為一個常數(shù) sigma 的正態(tài)分布;同理再求得此時的剩余紅包總額 k2,并生成第三個隨機數(shù) x3......直到生成完第 n-1 個隨機數(shù),最后一個隨機數(shù)由總額 k 減去 前 n-1 個隨機數(shù)求得。
為了確保生成的數(shù)為正,每次生成隨機數(shù)需要兩個判斷:1. 該生成的隨機數(shù)是否為正;2.剩余總額 - 該隨機數(shù)是否為正。兩個條件若有一個不滿足則該隨機數(shù)重新生成。

代碼實現(xiàn):
import random
def func2(amount , num):
list3 = []
num2 = num # 剩余紅包數(shù)
for i in range(0,num):
if num2 != 1: # 除了最后一個隨機數(shù),其余隨機數(shù)的生成規(guī)則
mu = int(amount / num2)
sigma = 8 # 設(shè)定方差 sigma 為一個常數(shù)
isnotpos = True
while isnotpos: # 循環(huán),當(dāng)生成的隨機數(shù)小于0或者剩余總額小于0時重新生成隨機數(shù)
a = random.normalvariate(mu,sigma)
if a > 0:
if (amount - a) > 0:
isnotpos = False
a = int(a)
list3.append(a)
amount = amount - a # 計算剩余總額
num2 = num2 - 1 # 剩余紅包數(shù) - 1
else: # 最后一個隨機數(shù)就等于最后剩余的總額
a = amount
list3.append(a)
print(list3)
結(jié)果:
輸入:func1(100 , 12)
輸出:[8, 12, 6, 3, 10, 1, 11, 13, 14, 4, 9, 9]
總結(jié):
本文提供了生成和為 k 的 n 個隨機數(shù)的兩種方法,兩種方法比較如下:
-
第一種方法算法邏輯簡單,只需要生成一次隨機數(shù),且無判斷過程,因此算法銷量要遠遠高于方法二。引入
time模塊比較兩個方法的耗時,其結(jié)果如下所示,可以方法一的效率要高于方法二。
兩方法耗時比較 方法一隨機性比較強,可能不同紅包(隨機數(shù))之間差距較大;方法二由于可以自主設(shè)定方差值,因此可以控制不同紅包(隨機數(shù))之間的差距。當(dāng)需要生成差異性不大的一系列隨機數(shù)時,可以選擇方法二。
注意!當(dāng)用方法二生成的隨機數(shù)不滿意時可以修改代碼中的方差sigma的值
附錄:
紅包生成器完整代碼:
import random
import tkinter as tk
import time
#方法一
def func1():
text1.delete(1.0,tk.END)
start = time.clock()
amount = int(entry1.get())
num = int(entry2.get())
list1 = []
for i in range(0,num-1): # 生成 n-1 個隨機節(jié)點
a = random.randint(0,amount)
list1.append(a)
list1.sort() # 節(jié)點排序
list1.append(amount) # 設(shè)置第 n 個節(jié)點為amount,即總金額
list2 = []
for i in range(len(list1)):
if i == 0:
b = list1[i] # 第一段長度為第 1 個節(jié)點 - 0
else:
b = list1[i] - list1[i-1] # 其余段為第 n 個節(jié)點 - 第 n-1 個節(jié)點
text1.insert('end',str(b) + ' ')
list2.append(b)
end = time.clock()
text1.insert('end','\n耗時:%s 秒' %(end - start))
print(list2) # 打印耗時
# 方法二
def func2():
text2.delete(1.0,tk.END)
start = time.clock()
amount = int(entry1.get())
num = int(entry2.get())
list3 = []
num2 = num # 剩余紅包數(shù)
for i in range(0,num):
if num2 != 1: # 除了最后一個隨機數(shù),其余隨機數(shù)的生成規(guī)則
mu = int(amount / num2)
sigma = 5 # 設(shè)定方差 sigma 為一個常數(shù)
isnotpos = True
while isnotpos: # 循環(huán),當(dāng)生成的隨機數(shù)小于0或者剩余總額小于0時重新生成隨機數(shù)
a = random.normalvariate(mu,sigma)
if a > 0:
if (amount - a) > 0:
isnotpos = False
a = int(a)
text2.insert('end',str(a) + ' ')
list3.append(a)
amount = amount - a # 計算剩余總額
num2 = num2 - 1 # 剩余紅包數(shù) - 1
else: # 最后一個隨機數(shù)就等于最后剩余的總額
a = amount
text2.insert('end',str(a) + ' ')
list3.append(a)
end = time.clock()
text2.insert('end','\n耗時:%s 秒' %(end - start)) # 打印耗時
print(list3)
win = tk.Tk() # 用tkinter寫界面
win.geometry('600x350')
win.title("紅包生成器")
label = tk.Label(win, text="點擊生成你的紅包!",font=('黑體,40'))
label.pack(side = 'top',pady = '30')
frm = frm1 = tk.Frame(win)
frm.pack()
label1 = tk.Label(frm, text="請輸入紅包金額 :")
label1.pack(side = 'left')
entry1 = tk.Entry(frm, width = 5)
entry1.pack(side = 'left',padx = '20')
label2 = tk.Label(frm, text="請輸入紅包個數(shù) (1-10):")
label2.pack(side = 'left')
entry2 = tk.Entry(frm, width = 5)
entry2.pack(side = 'left',padx = '20')
frm1 = tk.Frame(win)
frm1.pack(pady = '30')
frm2 = tk.Frame(win)
frm2.pack(pady = '30')
text1 = tk.Text(frm1, height=2,width=55)
text2 = tk.Text(frm2, height=2,width=55)
btn1 = tk.Button(frm1,text='方法一',command = func1)
btn2 = tk.Button(frm2,text='方法二',command = func2)
text1.pack(side = 'left',padx = '50')
btn1.pack(side = 'right', padx = '20')
text2.pack(side = 'left',padx = '50')
btn2.pack(side = 'right', padx = '20')
text1.pack()
win.mainloop()
