求職不怕考
pcw-chendx@vip.sina.com
與編程相關的招聘,都會準備很多考題,一不小心考生就在考題上栽了跟頭,特別是沒有多少工作經驗的應屆畢業生,往往回答得過於理論化,很難令考官滿意。為此,我們特意推出本系列,通過對真實考題的分析讓大家在回答考題時有更多的實用性,讓考官滿意,順利找到工作。
高效產生隨機數組
爪哇米工作室 陳躍峰
演算法是程式的靈魂,所以在面試中基本都要考核應聘者的演算法能力。演算法的種類有很多,每種演算法在實現時都有獨立的方式,本文將介紹的產生隨機數組的演算法只是其中的一種。產生隨機數組,即固定的一組數組但是數字位置隨機的數組,是一種反映應聘者基礎數字邏輯掌握程度的基礎演算法。下面以一個簡單的例子來介紹高效產生隨機數組的演算法實現。
招聘題目:產生20個隨機數字,該組數字中包含2組1-10之間的整數,即包含2個1、2個2,依次類推,但是每個數位位置是隨機的。
答案A:
int num[] = new int[20];
int index = 0; //數組下標
int ranNum; //隨機數字
int time = 0; //數字已出現次數
Random r = new Random();
while(true){
ranNum = r.nextInt(9) + 1; //隨機1-10之間的數字
//統計數字ranNum已出現次數
time = 0;
for(int i = 0;i < index;i++){
if(num[i] == ranNum){
time++;
}
}
//如果未出現2次
if(time != 2){
num[index] = ranNum;
index++; //繼續賦值下一個
}
if(index == num.length){ //全部賦值
break;
}
}
答案B:
//產生2組1-10之間的規則數字
int num[] = new int[20];
for(int i = 0;i < num.length;i++){
num[i] = i / 2 + 1;
}
//隨機打亂數字順序
Random r = new Random();
int times = 30; //兩兩交換次數
int ranIndex; //隨機下標
int temp; //交換變數
int cIndex; //當前下標
for(int i = 0;i < times;i++){
ranIndex = r.nextInt(20); //[0-20]之間的隨機數,作為下標
//和下標i % 20交換
cIndex = i % 20;
if(cIndex != ranIndex){
temp = num[cIndex];
num[cIndex] = num[ranIndex];
num[ranIndex] = temp;
}
}
正確答案:B
答案分析
在答案A中,使用的是自然思維,既按照隨機產生一個1-10之間的數字,然後判斷該數字是否已在數組中出現2次,如果未出現2次,則將該數字賦值到數組中,接著隨機下一個數字。使用這種方式實現的代碼,雖然理論上可以完成要求的功能,但是在程式的執行效率上很不穩定,每次隨機的過程需要的時間的耗時差異很大,甚至有可能卡住程式的執行,所以在程式中產生比較多的隨機數字組合時,不推薦這樣進行實現。
在答案B中,採用的思路是一種變通的思路,即首先產生一組規則的數字,然後隨機打亂改組數位順序。使用這種思路,程式的執行效率很穩定,而且實現起來比較簡單。
實現的具體步驟是,首先產生兩組1-10之間的整數,按照1,1,2,2,……這樣的順序依次賦值到數組中,這樣得到的是一個規則的數組num,這個數組中包含所有需要的數字,即2組1-10之間的數字,但是數位順序是規則的。接著使用兩兩交換的方式隨機交換數組中數位位置,交換的方法是產生一個0到數組長度(不包含)的隨機數字,作為一個數組交換時的下標,然後和數組當前的下標,即第一次下標為0,第二次下標為1,的元素進行交換,這樣經過一定次數的交換以後,數組中的數字位置就變得隨機了。而每次的交換消耗的時間是基本固定的,所以實現起來整個演算法的執行效率很穩定,產生的速度也很高效。
實際應用
高效產生隨機數組這種演算法在實際的程式開發中也大量的進行使用。例如在棋牌類的遊戲開發中,例如鬥地主、雙扣等,在每次遊戲開始時,都需要打亂撲克牌的順序,然後再將打亂以後的撲克牌依次發放給每個遊戲玩家。而這種打亂撲克牌順序的演算法通常就被稱作洗牌演算法。
從程式開發的角度看洗牌演算法,實際上產生的就是一組規則的隨機數字,例如對於一副撲克牌來說,在程式中如果需要代表一副撲克牌,根據撲克牌的特點(每幅牌包含54張不同的牌)則只需要為每張牌進行編號即可,例如將撲克牌中的每張牌依次對應編號成0-53之間的整數,那麼一副牌就是一個包含0-53之間所有整數的數組。而洗牌就是將這樣一個規則的數組變成一個隨機數組,但是必須包含所有0-53之間的所有整數,這個要求和前面介紹中的演算法實現的功能是一致的。
則根據前面介紹的演算法,在一副撲克牌的鬥地主遊戲中實現洗牌的代碼如下:
//產生一幅牌,編號為0-53
int poker[] = new int[54];
for(int i = 0;i < poker.length;i++){
poker[i] = i;
}
//洗牌
Random r = new Random();
int times = 54; //兩兩交換次數
int ranIndex; //隨機下標
int temp; //交換數字
int cIndex; //當前下標
for(int i = 0;i < times;i++){
ranIndex = r.nextInt(54); //[0-53]之間的隨機數
//和下標i % 54交換
cIndex = i % 54;
if(cIndex != ranIndex){
temp = poker[cIndex];
poker[cIndex] = poker[ranIndex];
poker[ranIndex] = temp;
}
}
點評:在該代碼中,利用前面介紹的演算法首先產生一個包含[0,53]之間所有整數的規則數組poker,然後再使用兩兩交換的方式對於該數組中的元素交換54次,這個次數可以根據需要進行調整,從而得到了一個符合邏輯要求的數組poker。而對於玩家來說,poker這個數組就是一副被洗過的撲克牌,可以以此為基礎進行遊戲後續的邏輯的開發了。