洗牌算法
Ⅰ 洗牌算法介紹
洗牌算法應用於我們需要生成一組不重複的全隨機數的情況,就像一副牌,我們發出去是不會有重複的,在發之前把這副牌洗了,這樣發出去的牌既是不重複的,也是隨機的,這就是洗牌算法的思想。接下來,我會帶領大家逐步學習這個思想。
Ⅱ 如何生成範圍內的隨機數
要學習洗牌算法,生成多個隨機數,我們首先需要明白如何生成固定範圍內的一個隨機數。
這裏用到了三個函數,srand()
,rand()
,和time()
。前兩個的頭文件是stdlib.h
,第三個的頭文件是time.h
。
A. srand()
聲明:void srand(unsigned int seed);
功能:
srand和rand()配合使用產生僞隨機數序列。rand函數在產生隨機數前,需要系統提供的生成僞隨機數序列的種子,rand根據這個種子的值產生一系列隨機數。如果系統提供的種子沒有變化,每次調用rand函數生成的僞隨機數序列都是一樣的。srand(unsigned seed)通過參數seed改變系統提供的種子值。
簡單來說,就是根據其參數unsigned seed,提供一個隨機數發生的種子,參數改變則種子改變。
B. rand()
聲明: int rand();
功能:生成一個僞隨機數。
爲什麼說是僞隨機數呢?因爲只要種子不變,每次產生的“隨機數”是相同的,所以並不是真正的隨機數。
我做以下驗證👇
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main() {
int num;
//srand(1);
num = rand();
printf("%d\n", num);
return 0;
}
這時候運行三次結果如下👇
一直輸出的是同一個所謂的隨機數。如果不改變srand()的參數值,我們把那行註釋放開,也是得到如下結果👇
所以我們需要和srand()函數配合使用,通過改變種子值,可以改變產生的僞隨機數。
但是又有一個問題了,srand()的參數改變,種子纔會改變,所以爲了生成一個隨機數,我們需要提供一個隨機數給srand(),讓它產生一個隨機的種子,由此產生一個隨機數。這就陷入了死循環中。
要解決這個問題,我們只需要考慮,有哪個變量,是始終在改變的?答案就是時間。
我們只需要用時間作爲srand()的參數,就可以生成隨機種子了,由此可以生成真正的隨機數。由此引入我們的第三個函數。
C. time()
聲明:time_t time(time_t *t);
這裏補充一點,time_t
的實質類型是long
。
功能:如果t是空指針,直接返回當前時間。如果t不是空指針,返回當前時間的 同時,將返回值賦予t指向的內存空間。
這裏再補充一點,time() 函數返回自 Unix 紀元(January 1 1970 00:00:00 GMT)起的當前時間的秒數。
我做以下驗證👇
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main() {
int num;
srand(time(NULL));
num = rand();
printf("%d\n", num);
return 0;
}
運行結果如下👇
可以看到,確實生成了真正的隨機數。
D. 如何將隨機數控制在一定範圍內
這裏給大家提供一個公式,感興趣的同學可以自行推導驗證。
randInteger = rand() % (maxNum - minNum + 1) + minNum;
假設控制範圍是從minNum
到maxNum
,生成的隨機數是randInteger
。
這樣就可以控制生成的最小數了。
E. 生成一定範圍內的隨機數的代碼實現
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <malloc.h>
typedef unsigned char boolean;
#define TRUE 1
#define FALSE 0
int randInteger(int minNum, int maxNum);
int *randArray(int *array, const int minNum, const int maxNum, const int count);
void showArray(const int *array, const int count);
int randInteger(int minNum, int maxNum) {
int maxNumber;
int minNumber;
int randInteger;
int temp;
static boolean isFirst = TRUE;
minNumber = minNum <= 0 ? 1 : minNum;
maxNumber = maxNum > 32767 ? 32767 : maxNum;
if (minNum > maxNum) {
temp = minNum;
minNum = maxNum;
maxNum = minNum;
}
if (TRUE == isFirst) {
srand(time(NULL));
isFirst = FALSE;
}
randInteger = rand() % (maxNum - minNum + 1) + minNum;
return randInteger;
}
int *randArray(int *array, const int minNum, const int maxNum, const int count) {
int index;
for (index = 0; index < count; index++) {
array[index] = randInteger(minNum, maxNum);
}
return array;
}
void showArray(const int *array, const int count) {
int index;
for (index = 0; index < count; index++) {
printf("%d ", array[index]);
}
printf("\n");
}
int main() {
int minNum;
int maxNum;
int count;
int *array = NULL;
printf("請輸入要生成的隨機數個數:\n");
scanf("%d", &count);
array = (int*) calloc(sizeof(int), count);
printf("請輸入要生成的隨機數範圍:\n");
scanf("%d %d", &minNum, &maxNum);
randArray(array, minNum, maxNum, count);
showArray(array, count);
free(array);
return 0;
}
我將程序最大值設置爲32767,最小值爲1。若用戶輸入的最大值和最小值超過了這個範圍,程序就會自動修正,改變其最大最小值。這個限度大家可以自行設置,只要小於231 - 1,就是合理的。
我還設置了一個靜態存儲類static boolean isFirst = TRUE;
。這個是爲了保證我的其他用途,當我需要僅生成一組隨機數,後面都保持不變時,設置這個變量就可以保證這個事情。
運行結果如下👇
Ⅲ 洗牌算法思想
既然是洗牌,那麼我們就以洗牌爲例。比如我們要將54張牌洗亂,然後發出去。我們需要做如下幾步👇
- 將54張牌按0到53編號,並按其編號存入大小爲54的數組中。
- 令每一個數組元素的值等於其下標。
- 在[0,54)中隨機生成一個數,將這個數對應的下標的值與下標爲53-1的值交換
- 重複以上步驟,依次與53 - 2,53 - 3直到53 - 53對應的下標的值交換。
經過以上的循環,一副牌就被我們洗亂了。這時候我們只需要按照平時發牌的樣子,從第一張到最後一張,依次發出去就好了,這時候拿到牌的人手裏的牌自然就是亂的。
Ⅳ 洗牌算法的代碼實現
代碼如下👇
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <time.h>
void shuffle(int *randArray, int count);
void show(int *randArray, int count);
void show(int *randArray, int count) {
int i;
for (i = 0; i < count; i++) {
printf("第%d個數是:%d\n", i+1, randArray[i]);
}
}
void shuffle(int *randArray, int count) {
int i;
int change = count;
int randomNum;
int temp;
for (i = 0; i < count; i++) {
randArray[i] = i;
}
srand(time(NULL));
for (i = 0; change - 1; i++) {
randomNum = rand() % (count + 1);
temp = randArray[randomNum];
randArray[randomNum] = randArray[change - 1];
randArray[change - 1] = temp;
change--;
}
}
int main() {
int count;
int *randArray = NULL;
printf("請輸入需要的無重複隨機數個數:");
scanf("%d", &count);
randArray = (int *) calloc(sizeof(int), count);
shuffle(randArray, count);
printf("以下爲洗牌之後的數:\n");
show(randArray, count);
return 0;
}
運行結果如下👇
以上即爲我對洗牌算法的講解。