昨晚在家看 “南洋十大邪術”,又發現徐錦江了,果然是在情色片中起家的,起興處。。。被以前學校的一個小師弟抖屏攪了。。。悲劇!
由於金三銀四的好時期,小師弟也跑去找工作了,也就碰到了各種各樣的面試題,然後也就引出了今天的這篇博文,就是:如何產生1-100
之間的100個不重複的隨機數,不過這裡還好,在攜程面試.net是沒有筆試的:-)
如果這是你是第一次看到這個題目,也許你的想法有很多。
1:首先從原始陣列中隨機選擇一個數字,然後將該數字從陣列中剔除,再隨記選,再剔除,重複99次,就解決了。
我們知道從陣列中剔除一個元素的複雜度為O(N),那麼隨機選取n個數字,它的複雜度就是O(N2)了。
2:用hash作為中間過濾層,因為在陣列中,我們採用隨機數的話,也許隨機數在多次隨機中可能會有重複,所以需要用hash來判斷一下,
如果在hash中重複,則繼續產生隨機數,直到不重複為止,當然這個複雜度就不好說了,得要看隨機數隨機不隨機了,好的話,O(N)搞定,
不走運的話無上限~
3:就像標題說的一樣,很多問題我們都能在現實生活中找到寫照,畢竟很多東西是來源於現實,又抽象於現實,比如這個題目在現實生活中,
可以對應到的就是“洗撲克牌”,在演算法中也叫“洗牌原理”,我們知道洗撲克牌的方式就是隨機的交換撲克牌的位置,又叫做"切牌",當你切了
很多次後,我們的撲克牌就可以認為是足夠亂了,複雜度也就變成了O(N),用程式碼實現就是這樣的。
<1> 先有序的生成52張牌,然後有序的放到陣列中。
<2>從1-52中隨機的產生一個數,然後將當前次數的位置跟隨機數的位置進行交換,重複52次,我們的牌就可以認為足夠亂了。
4:程式碼實現
<1> 首先定義牌的資料結構,定義一個“花色”和“數字”
1 /// <summary> 2 /// 具體撲克牌 3 /// </summary> 4 public class Card 5 { 6 public char suit; 7 8 public string num; 9 }
<2>有序的生成52張牌
1 /// <summary> 2 /// 開牌 3 /// </summary> 4 public void NewCard() 5 { 6 for (int i = 1; i <= card.Length; i++) 7 { 8 var suit = ((i - 1) / 13) + 3; 9 var num = i % 13; 10 11 string temp; 12 13 switch (num) 14 { 15 case 1: temp = "A"; break; 16 case 11: temp = "J"; break; 17 case 12: temp = "Q"; break; 18 case 0: temp = "K"; break; 19 default: temp = num.ToString(); break; 20 } 21 22 card[i - 1] = new Card() 23 { 24 suit = (char)suit, 25 num = temp 26 }; 27 } 28 }
<3> 然後就是切牌了,剛才也說了思路,就是拿隨機數的位置與當前i的位置進行交換,不過一說到交換就想起了“氣泡排序”,可能被毒害太
深了(┬_┬),不知道你感覺到了沒。
1 /// <summary> 2 /// 洗牌 3 /// </summary> 4 public void Shuffle() 5 { 6 for (int i = 0; i < card.Length; i++) 7 { 8 var rand = new Random((int)DateTime.Now.Ticks).Next(0, card.Length); 9 10 //因為隨機數是偽隨記,正真的隨機數是要跟硬體打交道的,所以這裡設定了停留1ms 11 System.Threading.Thread.Sleep(1); 12 13 var temp = card[rand]; 14 15 card[rand] = card[i]; 16 17 card[i] = temp; 18 } 19 }
<4> 最後我們看看效果
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace ConsoleApplication1 8 { 9 public class Program 10 { 11 static void Main(string[] args) 12 { 13 CardClass cc = new CardClass(); 14 15 cc.NewCard(); 16 17 Console.WriteLine("\n\n=======================洗牌之前 ===========================\n"); 18 cc.Output(); 19 20 Console.WriteLine("\n\n=======================洗牌之後 ===========================\n"); 21 cc.Shuffle(); 22 cc.Output(); 23 24 25 Console.Read(); 26 } 27 } 28 29 public class CardClass 30 { 31 public Card[] card = new Card[52]; 32 33 /// <summary> 34 /// 具體撲克牌 35 /// </summary> 36 public class Card 37 { 38 public char suit; 39 40 public string num; 41 } 42 43 /// <summary> 44 /// 開牌 45 /// </summary> 46 public void NewCard() 47 { 48 for (int i = 1; i <= card.Length; i++) 49 { 50 var suit = ((i - 1) / 13) + 3; 51 var num = i % 13; 52 53 string temp; 54 55 switch (num) 56 { 57 case 1: temp = "A"; break; 58 case 11: temp = "J"; break; 59 case 12: temp = "Q"; break; 60 case 0: temp = "K"; break; 61 default: temp = num.ToString(); break; 62 } 63 64 card[i - 1] = new Card() 65 { 66 suit = (char)suit, 67 num = temp 68 }; 69 } 70 } 71 72 /// <summary> 73 /// 洗牌 74 /// </summary> 75 public void Shuffle() 76 { 77 for (int i = 0; i < card.Length; i++) 78 { 79 var rand = new Random((int)DateTime.Now.Ticks).Next(0, card.Length); 80 81 //因為隨機數是偽隨記,正真的隨機數是要跟硬體打交道的,所以這裡設定了停留1ms 82 System.Threading.Thread.Sleep(1); 83 84 var temp = card[rand]; 85 86 card[rand] = card[i]; 87 88 card[i] = temp; 89 } 90 } 91 92 /// <summary> 93 /// 輸入牌型 94 /// </summary> 95 public void Output() 96 { 97 for (int i = 0; i < card.Length; i++) 98 { 99 if (i % 13 == 0) 100 Console.WriteLine(); 101 102 Console.Write("{0}{1} ", card[i].suit, card[i].num); 103 } 104 } 105 } 106 }
其實“洗牌原理”只要說破了是很簡單的,就是你想不到,哈哈。。。然後小師弟也就明白了,我也就繼續看徐錦江的精彩表現了,o(∩_∩)o