Chapter 1.有限連續(xù)范圍內(nèi)生成不重復(fù)隨機(jī)數(shù)及其應(yīng)用

歡迎來(lái)到「我是真的狗雜談世界」干跛,關(guān)注不迷路

背景/前言

最近遇到了兩個(gè)可以轉(zhuǎn)化為本文題目問(wèn)題的需求點(diǎn)(具體需求在下面應(yīng)用節(jié)選會(huì)講到),決定整理和記錄下來(lái)祟绊。

技術(shù)問(wèn)題描述

  • 問(wèn)題:給定一個(gè)連續(xù)且有限的值范圍(從min到max楼入,步長(zhǎng)為step),從中隨機(jī)取出不重復(fù)的n個(gè)值牧抽。
  • 舉例:從108~10086的整數(shù)范圍內(nèi)嘉熊,取出10個(gè)不重復(fù)的隨機(jī)數(shù)。

技術(shù)問(wèn)題方案

解決問(wèn)題總會(huì)有一些通用的思想扬舒,比如:窮舉阐肤、分治、迭代讲坎、倍增孕惜、回溯、貪心晨炕、動(dòng)規(guī)

那運(yùn)用這些思想可以想到如下解法(當(dāng)然還會(huì)有別的解法)


粗暴隨機(jī)法

  1. 維護(hù)一個(gè)結(jié)果集
  2. 在范圍內(nèi)生成一個(gè)隨機(jī)數(shù)衫画,并判斷隨機(jī)數(shù)是否在結(jié)果集內(nèi)
  3. 隨機(jī)數(shù)不在結(jié)果集內(nèi)則加入結(jié)果集,并回到2步驟直至結(jié)果集內(nèi)數(shù)量滿足
  4. 隨機(jī)數(shù)在結(jié)果集內(nèi)則直接回到2步驟直至結(jié)果集內(nèi)數(shù)量滿足
function unique_rand($min, $max, $num) {
    $count = 0;
    $return = [];
    while ($count < $num) {
        $return[] = mt_rand($min, $max);
        // 借助翻轉(zhuǎn)法識(shí)別并剔除重復(fù)值
        $return = array_flip(array_flip($return));
        $count = count($return);
    }
    shuffle($return);
    return $return;
}

剔除隨機(jī)法

  1. 窮舉全量范圍并構(gòu)建數(shù)組(k1=min,k2=min+step,kx=min+(x-1)*step,...kn=max)
  2. 每次在k范圍內(nèi)產(chǎn)生一個(gè)隨機(jī)數(shù)a府瞄,并將ka的值放入結(jié)果集碧磅,同時(shí)用kn的值填充ka碘箍,n自減
  3. 重復(fù)2步驟直至結(jié)果集內(nèi)數(shù)量滿足
function unique_rand($min, $max, $num) {
    $numbers = range($min, $max);
    // 實(shí)現(xiàn)上偷個(gè)懶遵馆,直接采用內(nèi)置打亂函數(shù),再取前多少位的方式
    shuffle($numbers);
    $result = array_slice($numbers, 0, $num);
    return $result;
}

均勻分布法

  1. 將全量范圍分為均勻的n段子范圍
  2. 從每一段中獲取一個(gè)隨機(jī)數(shù)放入結(jié)果集

再升級(jí)一下就是雙倍隨機(jī)法(但雙倍隨機(jī)法需要處理額度不足問(wèn)題)

// 代碼就偷個(gè)懶不寫了

語(yǔ)言內(nèi)置函數(shù)/標(biāo)準(zhǔn)庫(kù)

有的語(yǔ)言可能內(nèi)置了相關(guān)方法丰榴,比如

/**
 * Pick one or more random keys out of an array
 * @link https://php.net/manual/en/function.array-rand.php
 * @param array $array <p>
 * The input array.
 * </p>
 * @param int $num [optional] <p>
 * Specifies how many entries you want to pick.
 * </p>
 * @return int|string|array If you are picking only one entry, array_rand
 * returns the key for a random entry. Otherwise, it returns an array
 * of keys for the random entries. This is done so that you can pick
 * random keys as well as values out of the array.
 */
function array_rand(array $array, int $num = 1): array|string|int {}

技術(shù)問(wèn)題場(chǎng)景

場(chǎng)景羅列

不同的方案肯定各有優(yōu)劣货邓,需要結(jié)合具體場(chǎng)景來(lái)分析。
我們先考慮一下影響實(shí)現(xiàn)的維度有:

  • 范圍基數(shù): c = ((max-min)/step)+1四濒;這個(gè)值有大/小的維度取值
  • 目標(biāo)結(jié)果比例: r = n/c换况;這個(gè)值有少/中/多的維度取值

因?yàn)槲覀兛梢粤_列場(chǎng)景如下:

  1. c小r少: 比如從100個(gè)里面產(chǎn)生3個(gè)
  2. c小r中: 比如從100個(gè)里面產(chǎn)生48個(gè)
  3. c小r多: 比如從100個(gè)里面產(chǎn)生97個(gè)
  4. c大r少: 比如從1000000個(gè)里面產(chǎn)生10個(gè)
  5. c大r中: 比如從1000000個(gè)里面產(chǎn)生489876個(gè)
  6. c大r多: 比如從1000000個(gè)里面產(chǎn)生999997個(gè)

但考慮到3和6兩種r多的情況可以時(shí)1和4情況的取反职辨,因此合并:

  1. c小r少: 比如從100個(gè)里面產(chǎn)生3個(gè)
  2. c小r中: 比如從100個(gè)里面產(chǎn)生48個(gè)
  3. c大r少: 比如從1000000個(gè)里面產(chǎn)生10個(gè)
  4. c大r中: 比如從1000000個(gè)里面產(chǎn)生489876個(gè)

場(chǎng)景與解法匹配

可以多種解法混用


c小r少 c小r中 c大r少 c大r中
粗暴隨機(jī)法 [1] [2] [1] [2]
剔除隨機(jī)法 高? [3] [3] [4] [4]
均勻分布法 低? [5] [6] [5] [6]

業(yè)務(wù)應(yīng)用

連抽N份獎(jiǎng)品

  • 需求描述:用戶達(dá)成特定任務(wù)后有資格參與最終的抽獎(jiǎng)環(huán)節(jié),在抽獎(jiǎng)環(huán)節(jié)通過(guò)管理后臺(tái)進(jìn)行中獎(jiǎng)人員抽取戈二,同等級(jí)獎(jiǎng)品需同時(shí)抽出
  • 轉(zhuǎn)換關(guān)聯(lián):將有資格的用戶放在一張表中舒裤,抽取時(shí)獲取最新記錄自增ID N,就轉(zhuǎn)換成了從1~N中生成X個(gè)不重復(fù)隨機(jī)數(shù)的問(wèn)題

紅包瓜分(搶紅包)

  • 需求描述:一個(gè)房間有N個(gè)座位觉吭,每個(gè)座位可以讓一個(gè)人入座參與一場(chǎng)單人小游戲(不需要同時(shí)開局同時(shí)結(jié)束)腾供,且一個(gè)房間對(duì)應(yīng)一定金額的紅包A,每個(gè)人完成單人小游戲后可以瓜分其中一定的金額
  • 轉(zhuǎn)換關(guān)聯(lián):?jiǎn)稳送瓿捎螒虻臅r(shí)間點(diǎn)是不同的鲜滩,但可以將紅包額度瓜分的時(shí)機(jī)前置到房間初始化之時(shí)伴鳖,再結(jié)合線段分割法,就轉(zhuǎn)換成了先從0~A中生成N-1個(gè)不重復(fù)隨機(jī)數(shù)徙硅,再以這N-1個(gè)隨機(jī)數(shù)排序座位切割點(diǎn)構(gòu)成N段隨機(jī)長(zhǎng)度問(wèn)題

  1. r少榜聂,不容易生成重復(fù)值導(dǎo)致時(shí)間復(fù)雜度升高 ? ?

  2. r中,容易生成重復(fù)值導(dǎo)致時(shí)間復(fù)雜度升高 ? ?

  3. c小嗓蘑,內(nèi)存容納得下须肆,通過(guò)時(shí)間換空間 ? ?

  4. c大,內(nèi)存分配消耗增大脐往,且可能內(nèi)存不足 ? ?

  5. r少休吠,結(jié)果分布會(huì)比較均勻,如果要求隨機(jī)性高則不適用 ? ?

  6. r中业簿,結(jié)果分布本身跟隨概率分布就會(huì)比較均勻瘤礁,因此此方法匹配度上升 ? ?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過(guò)簡(jiǎn)信或評(píng)論聯(lián)系作者梅尤。
  • 序言:七十年代末柜思,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子巷燥,更是在濱河造成了極大的恐慌赡盘,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缰揪,死亡現(xiàn)場(chǎng)離奇詭異陨享,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)钝腺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門抛姑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人艳狐,你說(shuō)我怎么就攤上這事定硝。” “怎么了毫目?”我有些...
    開封第一講書人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵蔬啡,是天一觀的道長(zhǎng)诲侮。 經(jīng)常有香客問(wèn)我,道長(zhǎng)箱蟆,這世上最難降的妖魔是什么沟绪? 我笑而不...
    開封第一講書人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮空猜,結(jié)果婚禮上近零,老公的妹妹穿的比我還像新娘。我一直安慰自己抄肖,他們只是感情好久信,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著漓摩,像睡著了一般裙士。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上管毙,一...
    開封第一講書人閱讀 52,457評(píng)論 1 311
  • 那天腿椎,我揣著相機(jī)與錄音,去河邊找鬼夭咬。 笑死啃炸,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的卓舵。 我是一名探鬼主播南用,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼掏湾!你這毒婦竟也來(lái)了裹虫?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤融击,失蹤者是張志新(化名)和其女友劉穎筑公,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體尊浪,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡匣屡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了拇涤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捣作。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖工育,靈堂內(nèi)的尸體忽然破棺而出虾宇,到底是詐尸還是另有隱情搓彻,我是刑警寧澤如绸,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布嘱朽,位于F島的核電站,受9級(jí)特大地震影響怔接,放射性物質(zhì)發(fā)生泄漏搪泳。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一扼脐、第九天 我趴在偏房一處隱蔽的房頂上張望岸军。 院中可真熱鬧,春花似錦瓦侮、人聲如沸艰赞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)方妖。三九已至,卻和暖如春罚攀,著一層夾襖步出監(jiān)牢的瞬間党觅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工斋泄, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留杯瞻,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓炫掐,卻偏偏與公主長(zhǎng)得像魁莉,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子募胃,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360

推薦閱讀更多精彩內(nèi)容