?閱讀本文前,請您先點擊上面的藍色字體“Android掃地僧”,“關(guān)注”后再點擊星標,及時上車,優(yōu)質(zhì)干貨,重磅資源第一時間送達。
首先,出一個簡單的題目:有一個大小為100的數(shù)組,里面的元素是從 1 到 100,怎樣隨機從里面選擇 1 個數(shù)呢?
很簡單,利用 Math.random() * 100 ,就可以拿到一個 0 到 99 的隨機數(shù),然后去數(shù)組找對應(yīng)的位置就行了。
下面難度提高一點哈! 有一個大小為100的數(shù)組,里面的元素是從 1 到 100,怎樣隨機從里面選擇 50 個數(shù)呢?注意!不能重復。
如果根據(jù)上面的思路,你一定想到了:是不是隨機 50 次就行了?
那么問題來了,這 50 次你是不能保證都是不能重復的,對吧!
嗯,你是不是又有靈感了,弄一個數(shù)組,把每一次隨機的數(shù)都放到數(shù)組里,下一次隨機就看這個數(shù)組里面有沒有這數(shù),有的話就繼續(xù)隨機,直到這個數(shù)組里面有 50 個數(shù)字就停止。完美!
嗯,講道理的話,這樣做也沒有問題,肯定可以實現(xiàn)題目要求。但是如果我們想一下,就會發(fā)現(xiàn)問題:如果我們考慮極限,就比如拿 99 個數(shù)字(你不要跟我說:那就考慮相反的情況,拿 1 個數(shù)字,把這個數(shù)字去掉就行>。< ),是不是越往后重復的概率越高,那么需要重復隨機的次數(shù)也越大?
是的,小老弟,你很聰明,一下子就理解了。我們可以看下代碼:
var idxArr = []while(idxArr.length < 99){
let idx = parseInt(Math.random()*100)
var time = 0
while(idxArr.includes(idx)) {
idx = parseInt(Math.random()*100)
console.log('time', time++)
}
idxArr.push(idx);}
代碼很簡單,如果隨機的數(shù)字不在數(shù)組里面,就 push 進去,否則就重新隨機。這里用了一個 times 來計算每一個數(shù)需要隨機的次數(shù)??梢钥聪螺敵觯?/p>
可以看到,一開始基本是沒有重復的,但是到了后面最后一個,要拿到一個沒有出現(xiàn)過的隨機數(shù)需要多達 65 次。
如果你把數(shù)組再放大一點,結(jié)果會更加夸張。當然,現(xiàn)實里不會有這么極端的情況,就像你說的,要拿 99 個數(shù)就反過來剔除 1 個數(shù)就行了。但是呢,就算沒有 99 也可能有 50 對吧,這個性質(zhì)還是一樣的,就是可能重復較多。
這個時候就需要換一個思路,如果先將數(shù)組里面的元素打亂,那么按順序選擇前 50 個不就可以了?
是的!
但我們得注意什么叫亂?
一副撲克有 54 張牌,有 54! 種排列方式。所謂的打亂指的是,你所執(zhí)行的操作,應(yīng)該能夠 等概率地生成 這 54! 種結(jié)果中的一種。
所以呢,進入主題了:洗牌算法。
原理很簡單:最后一個數(shù)和前面任意 n-1 個數(shù)中的一個交換,然后倒數(shù)第二個數(shù)和前面任意 n-2 個數(shù)中的一個交換。。。依次下去就行了。
直接看代碼:
public void shuffle(int[] array) {
int length = array.length;
Random random = new Random();
int temp;
while (length > 0) {
int index = random.nextInt(length);
temp = array[index];
array[index] = array[length -1];
array[length -1] = temp;
length--;
}
}
?
//[1,2,3,4,5,6,7,8,9]
//輸出[6 1 7 8 5 2 4 9 3 ]
在整個過程中,這個算法保證了每一個元素出現(xiàn)在每一個位置的概率是相等的。
這個算法真的很牛逼很經(jīng)典,而且代碼很少。
需要更多算法相關(guān),請在公眾號回復【算法】,即可獲取LeetCode算法刷題精講
****推****薦****閱****讀****
**1. **Android進階資源分享(2月15日更新) ****
2.Android面試題附答案,會了起碼30K****
點“在看”的都漲工資了