從N個數裡面,隨機抽取M個數(可以用作抽獎隨機使用者)
直接抽取
const cards = Array(62).fill().map((_,i)=>i+1); //初始化一個 1~62 的陣列 function draw(n = 1){ // 一次抽取 n 個,預設一次 1 個 var ret = []; for(var i = 0; i < n; i++){ let idx = Math.floor(cards.length * Math.random()); ret.push(...cards.splice(idx, 1)); } return ret; } console.log(draw(10)); //抽取一次,10箇中獎者
上面這個方法非常直觀,首先生成一個順序的 1 ~ 62 號的陣列,然後從其中隨機抽取 10 次,為了不重複,將抽取的數字通過 cards.splice(idx,
1)
從原陣列中取出來。
上面這種方式可行,但它不是最好的,因為每次 splice 一個數字,取 10 個數字需要 splice 10 次,這看起來不是特別好。可以想到另一種方法,先對陣列進行“洗牌”,然後一次把 10 個數字取出來:
先洗牌
function draw(amount, n = 1){ const cards = Array(amount).fill().map((_,i)=>i+1); for(let i = amount - 1; i >= 0; i--){ let rand = Math.floor((i + 1) * Math.random()); [cards[rand], cards[i]] = [cards[i], cards[rand]]; } return cards.slice(0, n); } console.log(draw(62, 10));
上面這個版本是月影實際現場寫出的(略有修改),它是不錯的,但是它也有明顯缺點。首先它先把所有的牌都排序了,但實際上只需要排序 10 張牌就好,多餘的排序沒有必要。其次,它不方便連續抽獎,比如第一次抽取 10 個號,然後再想多抽取 5 個號,它就做不到了。
我們先解決第一個問題:
不需要洗所有的牌
function draw(amount, n = 1){ const cards = Array(amount).fill().map((_,i)=>i+1); for(let i = amount - 1, stop = amount - n - 1; i > stop; i--){ let rand = Math.floor((i + 1) * Math.random()); [cards[rand], cards[i]] = [cards[i], cards[rand]]; } return cards.slice(-n); } console.log(draw(62, 10));
上面這個版本是優化過的版本,顯然如果取 10 個數,只需要迴圈 10 次即可,不需要把 64 張牌都洗了。
要解決可以連續抽獎的問題,就需要把 cards 提取出來(就像方案 1 的隨機抽取一樣),但是那樣的話就使得函式有副作用,雖說是臨時寫一個抽獎,也不喜歡設計得太糙。或者,那就加一個構造器執行初始化?
構造器負責初始化
function Box(amount){
this.cards = Array(amount).fill().map((_,i)=>i+1);
}
Box.prototype.draw = function(n = 1){
let amount = this.cards.length, cards = this.cards;
for(let i = amount - 1, stop = amount - n - 1; i > stop; i--){
let rand = Math.floor((i + 1) * Math.random());
[cards[rand], cards[i]] = [cards[i], cards[rand]];
}
let ret = cards.slice(-n);
cards.length = amount - n;
return ret;
}
var box = new Box(62);
console.log(box.draw(5), box.draw(5)); //一次取 5 個,取 2 次
更優雅的解決方式?
實際上,對於一次可能抽取任意多個獲獎人的場景,用 ES6 的 generators 非常合適,我們可以直接拿洗牌的版本略做修改:
function * draw(amount){
const cards = Array(amount).fill().map((_,i)=>i+1);
for(let i = amount - 1; i >= 0; i--){
let rand = Math.floor((i + 1) * Math.random());
[cards[rand], cards[i]] = [cards[i], cards[rand]];
yield cards[i];
}
}
var drawer = draw(62);
console.log(Array(10).fill().map(()=>drawer.next().value)); //一次取出10個結果
最後補充一個小技巧,利用 Array(n).fill().map(...)
可以方便快速地構造陣列:
相關推薦
從N個數裡面,隨機抽取M個數(可以用作抽獎隨機使用者)
直接抽取 const cards = Array(62).fill().map((_,i)=>i+1); //初始化一個 1~62 的陣列 function draw(n = 1){ // 一次抽取 n 個,預設一次 1 個 var ret = []; for(var i = 0;
網易面試題之 牛牛的作業薄上有一個長度為 n 的排列 A,這個排列包含了從1到n的n個數,但是因為一些原因, * 其中有一些位置(不超過 10 個)看不清了,但是牛牛記得這個數列順序對的數量是 k,
package wangyi; /** * Created by Administrator on 2016/12/7. * 牛牛的作業薄上有一個長度為 n 的排列 A,這個排列包含了從1到n的n個數,但是因為一些原因, * 其中有一些位置(不超過 10 個)看不清
已知n個人(以編號1,2,3...n分別表示)圍坐在一張圓桌周圍。從編號為k的人開始報數,數到m的那個人出列;他的下一個人又從1開始報數,數到m的那個人又出列;依此規律重複下去,直至殺掉所有人,當剩下
public class Game { public int fun(int N,int k,int m) { int[] array=new int[N]; //建立N個人 int n=N; //剩餘人數 for(int
每日一練——從長度為n的數組裡選出m個數使和為固定值sum
這個問題是我從leetcode上一道問題所想到的,原題:如果是從陣列中選出2個數相加使之成為固定的數sum,這當然很簡單,把陣列中的數字遍歷一遍,判斷另一個數字是否也在陣列中即可。程式碼如下。
2017年完美世界校招筆試題,從n層大樓往下扔m顆玻璃珠確定珠子破碎的臨界樓層
這道題可以搜尋google扔玻璃珠筆試題以及騰訊2017的校招筆試也有一道這樣的題,不同的是,以上的題中只是用了兩顆玻璃珠,而這裡是m顆玻璃珠; 不過,同樣是扔珠子,所以思路都是一樣,只不過變得更有普
nyoj 19 擅長排列的小明 【全排列(n中抽取m個數)】
擅長排列的小明 時間限制:1000 ms | 記憶體限制:65535 KB 難度:4 描述 小明十分聰明,而且十分擅長排列計算。比如給小明一個數字5,他能立刻給出1-5按字典序的全排列,如果你想為難他,在這5個數字中選出幾個數字讓他繼續全排列,那麼你就錯了,他同樣的
LightOJ - 1117 Helping Cicada (求1~n有多少個數不能被這m個數中任意一個整除)(容斥+狀態壓縮)
vol == show fine cst href main http color 題意:http://www.lightoj.com/volume_showproblem.php?problem=1117 考慮1個數k,1~n有[n/k]個數能被k整除,[a]表示a向下取
小李子日記,輸入十個整數,將前面m個數
#include<stdio.h> int main() { int num1[80] = { 0 }, num2[80] = { 0 }; int *p1, i, n, m, *p2; printf(“Please enter n:”); scanf_s("%d", &
有n個整數,指定位置m處插入g個值(用指標和函式)
#include <stdio.h> void main() { void move(int *p,int *s,int n,int m,int g); int a[30],b[20]; i
S="S1 S2...Sn"是一個長度為N的字串,存放在一個數組中,程式設計將S改造之後輸出:
S="S1 S2…Sn"是一個長度為N的字串,存放在一個數組中,程式設計將S改造之後輸出: 將S的所有第偶數個字元按照其原來的下標從大到小的次序放在S的後半部分; 將S的所有第奇數個字元按照其原來的下標從小到大的次序放在S的前半部分; 例如:S=‘ABCDEFGHIJKL’
S="S1 S2...Sn"是一個長度為N的字串,存放在一個數組中,程式設計將S改造之後輸出:
S="S1 S2…Sn"是一個長度為N的字串,存放在一個數組中,程式設計將S改造之後輸出: 將S的所有第偶數個字元按照其原來的下標從大到小的次序放在S的後半部分; 將S的所有第奇數個字元按照其原來的下標從小到大的次序放在S的前半部分; 例如:S=‘ABCDEF
用C語言如何程式設計實現從三個陣列中各抽取幾個數進行組合的問題?
例:從陣列A中抽取3個數,陣列B中抽取2個數,陣列C中抽取1個數,組成6個數的組合,求源程式。 A={3,4,6,7,8,9,11,13,14,15,16,18,19,20,24,27,28,29,3
n個數裡找出前m個數
引子每年十一月各大IT公司都不約而同、爭後恐後地到各大高校進行全國巡迴招聘。與此同時,網上也開始出現大量筆試面試題;網上流傳的題目往往都很精巧,既能讓考查基礎知識,又在平淡中隱含了廣闊的天地供優秀學生馳騁。 這兩天在網上淘到一1道筆試題目(注1),雖然真假未知,但的確是道好題
找出n個數中最大的m個數的一些解決辦法
方法1: 排序後直接選出,這樣時間開銷為O(NlogN) 方法2: 先用選第k大元素的方法選出第k大元素(具體可以參考選第k大元素的那篇BLOG),按Knuth的說法,時間開銷是O(n),這樣的話,如果我們找到第n-m大的元素,設其為a,然後順序掃描一遍原序列,即可以得到最
給定一個數組,其中只有一個數出現一次,別的數都出現3次,找出這個數(go)
1.思路 用兩個數one=0、two=0分別記錄bits位上1出現的次數,如果一個數出現一次,則one等於這個數,two=0; 如果一個數出現兩次,則two等於這個數, one等於0;如果一個數出現第三次,則one = 0, two = 0 ,three等於這個數。 我們以陣
VUE從開始到結束,一般遇到的知識點(轉的:看別人寫的不錯)
-------------搭建專案----------------------------------------------------------------------- npm install --global vue-cli vue init webpack my-project --
系統從SqlServer轉向Mysql儲存,同時進行同步方案(多系統資料同步)
業務系統底層儲存由原先SQLServer轉向Mysql,但由於歷史原因,SQLServer的資料庫與資料需要保留(原先業務的下游系統有很長資料庫同步鏈條),並需要實時與Mysql進行同步(單向同步)以下
ElasticSearch從入門到精通,史上最全(持續更新,未完待續,每天一點點)
1.ElasticSearch的簡介 ElasticSearch:智慧搜尋,分散式的搜尋引擎 是ELK的一個組成,是一個產品,而且是非常完善的產品,ELK代表的是:E就是ElasticSearch,L就是Logstach,K就是kibana E:EalsticSea
堅持堅持!用Java寫出刪除一個連結串列的倒數第N個節點,並返回頭節點(N總是可達的)
這個題目有個前提條件就是N總是可達的,所以直接省去了一種情況(N不可達時的情況) 思路: 資料結構裡面首先給定兩個指標p和q分別都指向這個連結串列的頭節點,然後若想求出這個連結串列的倒數第N個節點,方法就是先讓p向後挪N個位置,q不動。 因為N總是可達的,所以會有倆種情況,
給定一個數組,其中只有一個數出現一次,別的數都出現3次,找出這個數
題目描述 給定一個數組,其中只有一個數x出現一次,別的數都出現3次,找出這個數x。(線性時間複雜度) 思路 這個用異或不可以。 可以設定一個長度為32的int陣列。統計每位上出現1的次數,如果次數能被3整除,說明x該位上為0,否則為1 java程式碼實