天天看點

前端面試題:高效地随機選取數組中的元素正常解法利用洗牌算法隻取所需性能比較總結REFERNCE

有前端題目大概是這樣的:考慮到性能問題,如何快速從一個巨大的數組中随機擷取部分元素。

比如有個數組有100K個元素,從中不重複随機選取10K個元素。

為了示範友善我們将資料簡化,先給出方案最後再用大點的資料來測試性能的對比。

正常做法倒也不難,生成一個0到數組長度減1的随機數,這個數也就是被選中元素在原數組中的下标,獲得該元素後将值儲存到另一個數組同時通過數組的splice方法将該元素從原數組中删除,以保證下次不會重複取到。

按以上思路,代碼大概就是這樣的:

運作結果如下圖:

前端面試題:高效地随機選取數組中的元素正常解法利用洗牌算法隻取所需性能比較總結REFERNCE

當然上面例子中為了便于示範,将題目要求的100 000 大數目簡化為總數為5,同時隻取3個。

由測試結果看這種做法是完全可行的。

但存在一個問題:為了下次随機時不重複選取已經選擇過的元素,我們将選擇過的元素從原數組中通過splice方法進行删除,但這個splice方法操作的過程本身就是數組重新維護其元素索引的過程,這意味着被選擇的元素之後的所有元素需要前移一個位置來重新生成一個緊湊的數組,可以想象如果我們取走了原數組中的第1個元素,那麼之後的99 999個元素都需要發生變動來完成重組數組的操作,無疑有點耗時。

這個想法較之前的方法有點逆行的感覺,前面着重點是随機,是以每次都産生一個随機下标到原數組去取,現在是先将數組元素随機打亂,再去正常取。由于洗牌算法非常高效且省去了數組的重組,較之前性能應該有所提升。

照這個思路最後實作的代碼大概就是這個樣子的:

運作結果:

前端面試題:高效地随機選取數組中的元素正常解法利用洗牌算法隻取所需性能比較總結REFERNCE

從結果來看,此種方法也是可行的。

細想還是存在問題,對于一個比較大的數組來說,不管你的洗牌算法多麼高效(即使上面Fisher-Yates算法時間複雜度為O(n)),要随機整個數組也還是很龐大的工程的吧。

那就是我們沒有必要随機掉整個數組,在我們取完需要數量的元素後,可以将Fisher-Yates亂序方法中止掉!

思路是非常明顯的了, 這樣可以省下不少無意義的操作。

是以最後的實作大概成了這樣子:

上面代碼将Fisher-Yates算法略做修改,在取得滿足要求的元素之後便停止了,是以較前面的做法更加科學。

前端面試題:高效地随機選取數組中的元素正常解法利用洗牌算法隻取所需性能比較總結REFERNCE

最後給出上面三個方法耗時的比較,這裡将需要操作的數組元素個數回歸到題目中要求的100 000來。

前端面試題:高效地随機選取數組中的元素正常解法利用洗牌算法隻取所需性能比較總結REFERNCE

目前PO主隻能想到這些,更優的做法還有待進一步探究。

繼續閱讀