Fisher-Yates 洗牌演算法

更新 發佈閱讀 5 分鐘

相信你對於演算法有了一點認識,如果今天要求您將一組牌進行洗牌的動作,該怎麼做?

Fisher-Yates 洗牌演算法

Fisher-Yates 洗牌算法是從 最後一個數開始隨機選擇並交換,其用意是為了公平的打亂,使得每種組合的機率相等,且能夠保證 O(N) 的時間複雜度。

O(N)簡單介紹

O(N) 代表 「線性時間(Linear Time)」,表示演算法的執行時間與輸入大小 N 成正比

簡單來說:當 N 增加,執行時間也會以相同比例增加。

  • N = 10,執行大約 10 次
  • N = 100,執行大約 100 次

這邊要特別提醒一點,通常程式步驟的時間複雜度會是用程式執行會碰到的最壞狀況 (Worst Case) 來表示

也因此,當我們要從 n 個櫃子中找到特定的目標,最慘的情況需要花剛好 n 個步驟才能找到,我們就會說此搜尋的時間複雜度為 O(n)

O(N) 與其他複雜度的比較

raw-image

好,回到主題,我們來分析一下程式碼的思維。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

// 函數生成一組隨機排序的唯一數字
void generateRandomUniqueNumbers(int arr[], int size) {
// 步驟 1:初始化陣列,依序填入從 1 到 size 的數字
for (int i = 0; i < size; i++) {
arr[i] = i + 1; // 將每個元素賦予從 1 到 size 的唯一數值
}

// 步驟 2:使用 Fisher-Yates 演算法將陣列隨機打亂
for (int i = size - 1; i > 0; i--) { // 從陣列尾部開始向前迭代
// 生成一個介於 0 和 i 之間的隨機索引 j
int j = rand() % (i + 1);

// 交換索引 i 和 j 的元素
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
// 到此為止,arr 包含了從 1 到 size 的數字並隨機排列
}

int main() {
int n;

// 步驟 1:詢問用戶需要生成的唯一隨機整數數量
printf("Enter the number of unique random integers (excluding 0): ");
scanf("%d", &n);

// 步驟 2:宣告一個陣列來儲存唯一隨機數字
int arr[n];

// 步驟 3:將隨機數生成器的種子設為當前時間
srand(time(0));

// 步驟 4:生成隨機唯一數字
generateRandomUniqueNumbers(arr, n);

// 步驟 5:輸出隨機排序的唯一整數
printf("Random unique integers:\n");
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]); // 輸出陣列中的每個數字
}
printf("\n"); // 打印完所有數字後換行

return 0;
}
  • Fisher-Yates 洗牌演算法步驟
    • 從最後一個元素開始,選擇 i = N-1(索引從 4 開始)。
    • [0, i] 區間內選擇一個隨機索引 j,並與 array[i] 交換。
    • 縮小範圍 i--,繼續隨機選取 j,直到 i = 0

步驟示範

初始陣列:

索引:  0   1   2   3   4
數字: [1, 2, 3, 4, 5] // 原始數組

第一輪(i = 4,隨機選擇 j = 2)

  1. i = 4(最後一個元素 5
  2. [0, 4] 中選擇 j = 2
  3. 交換 array[4]array[2]
索引:  0   1   2   3   4
數字: [1, 2, 5, 4, 3]

第二輪(i = 3,隨機選擇 j = 1)

  1. i = 3(元素 4
  2. [0, 3] 中選擇 j = 1
  3. 交換 array[3]array[1]
索引:  0   1   2   3   4
數字: [1, 4, 5, 2, 3]

第三輪(i = 2,隨機選擇 j = 2)

  1. i = 2(元素 5
  2. [0, 2] 中選擇 j = 2
  3. 因為 j = i,不交換
索引:  0   1   2   3   4
數字: [1, 4, 5, 2, 3]
  • (這一步 i = j,所以不變)

第四輪(i = 1,隨機選擇 j = 0)

  1. i = 1(元素 4
  2. [0, 1] 中選擇 j = 0
  3. 交換 array[1]array[0]
索引:  0   1   2   3   4
數字: [4, 1, 5, 2, 3]

完成洗牌!

最終結果(每次執行可能不同):

[4, 1, 5, 2, 3]
留言
avatar-img
留言分享你的想法!
avatar-img
電資鼠 - 您的學習好夥伴
15會員
242內容數
在當今數位時代,電資領域人才需求爆發式成長,不論是前端網頁設計、嵌入式開發、人工智慧、物聯網還是軟硬體整合,這些技術都在改變世界。而掌握 C/C++、Python、數位邏輯、電路學與嵌入式開發等大學電資領域的課程,正是進入這個高薪、高需求產業的關鍵!
2025/03/09
雙向串列 (Double Linked List, DLL) 是一種鏈結資料結構,本章節將以完整註解,搭配關鍵操作地方的圖示輔助學習,讓你輕鬆搞懂複雜觀念,並透過C語言實作。
Thumbnail
2025/03/09
雙向串列 (Double Linked List, DLL) 是一種鏈結資料結構,本章節將以完整註解,搭配關鍵操作地方的圖示輔助學習,讓你輕鬆搞懂複雜觀念,並透過C語言實作。
Thumbnail
2025/03/08
環狀鏈結串列是一種特殊的鏈結串列,其最後一個節點的指標指向第一個節點,而非 NULL,形成一個循環結構。本章節將以豐富圖示,引導讀者了解環狀串列在各種地方執行插入和刪除節點的步驟,輕鬆學會資工科的專業知識-環狀串列。
Thumbnail
2025/03/08
環狀鏈結串列是一種特殊的鏈結串列,其最後一個節點的指標指向第一個節點,而非 NULL,形成一個循環結構。本章節將以豐富圖示,引導讀者了解環狀串列在各種地方執行插入和刪除節點的步驟,輕鬆學會資工科的專業知識-環狀串列。
Thumbnail
2025/03/07
本章節將探討右上三角稀疏矩陣。
Thumbnail
2025/03/07
本章節將探討右上三角稀疏矩陣。
Thumbnail
看更多
你可能也想看
Thumbnail
高中數學主題練習—配方法
Thumbnail
高中數學主題練習—配方法
Thumbnail
高中數學主題練習—配方法
Thumbnail
高中數學主題練習—配方法
Thumbnail
中學數學基礎練習—一元一次方程式
Thumbnail
中學數學基礎練習—一元一次方程式
Thumbnail
中學數學基礎練習—一元一次方程式
Thumbnail
中學數學基礎練習—一元一次方程式
Thumbnail
中學數學基礎練習—一元一次方程式
Thumbnail
中學數學基礎練習—一元一次方程式
Thumbnail
我捨棄了編號系統,解放三倍大腦思考能量
Thumbnail
我捨棄了編號系統,解放三倍大腦思考能量
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News