python 生成n個隨機數(shù),其和為給定的數(shù)

問題描述:

在日常生活中搶紅包的時候,輸入紅包金額和紅包個數(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()
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容