【C語言->數據結構與算法】->洗牌算法->如何生成一組不重複的隨機數

Ⅰ 洗牌算法介紹

洗牌算法應用於我們需要生成一組不重複的全隨機數的情況,就像一副牌,我們發出去是不會有重複的,在發之前把這副牌洗了,這樣發出去的牌既是不重複的,也是隨機的,這就是洗牌算法的思想。接下來,我會帶領大家逐步學習這個思想。

Ⅱ 如何生成範圍內的隨機數

要學習洗牌算法,生成多個隨機數,我們首先需要明白如何生成固定範圍內的一個隨機數。
這裏用到了三個函數,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;

假設控制範圍是從minNummaxNum,生成的隨機數是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張牌洗亂,然後發出去。我們需要做如下幾步👇

  1. 將54張牌按0到53編號,並按其編號存入大小爲54的數組中。
  2. 令每一個數組元素的值等於其下標。
  3. 在[0,54)中隨機生成一個數,將這個數對應的下標的值與下標爲53-1的值交換
  4. 重複以上步驟,依次與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;
}

運行結果如下👇
在這裏插入圖片描述
在這裏插入圖片描述
以上即爲我對洗牌算法的講解。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章