首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

撲克牌洗牌算法實作:從基礎交換法到線程安全封裝

     編輯:關於C語言

前言

前幾天與幾位從事線上棋牌遊戲開發的朋友聚會,話題自然落在洗牌算法上。因為早年開發過多款撲克遊戲,手邊正好有一個現成的洗牌實作,便拿出來與大家分享驗證。這個算法的核心思路非常直觀——採用隨機交換法:將 54 張牌依序排列,隨機抽取兩張進行交換。理論上,交換次數若達到牌數(54 次),牌序已極度混亂;若求更保險,加倍至 108 次即可滿足實戰需求。

核心實作:CPoker 類別

完整程式碼結構清晰,包含牌面定義、初始化邏輯、洗牌核心(Wash)、取牌介面(Get)以及除錯輸出(PrintInfo)。關鍵在於 Random() 函式:每次呼叫產生兩個 0~53 的隨機索引,若撞號則將第二個索引位移一位(邊界回繞),隨後呼叫 Exchange() 完成結構體級別的記憶體交換。

// 關鍵片段:隨機交換邏輯
void Random(void) {
    int a = GetRandomBetween(0, POKER_MAX);
    int b = GetRandomBetween(0, POKER_MAX);
    if (a == b) {
        b = a + 1;
        if (POKER_MAX <= b) b = 0;
    }
    Exchange(a, b);
}

// 預設洗 54 次,呼叫端可自行增加次數
void Wash(int nTime = POKER_MAX) {
    for (int i = 0; i < nTime; ++i) Random();
}

線程安全增強版:CPokerWithLock

原版在多線程環境下存在競態風險(一邊洗牌、一邊取牌會導致資料損壞)。參考《0bug-C/C++商用工程之道》中的「資源鎖」模式,以私有聚合方式封裝 CPoker,所有公有方法加鎖,實現線程安全:

class CPokerWithLock {
public:
    void Wash(int nTime = POKER_MAX) {
        m_Lock.Lock();
        m_Poker.Wash(nTime);
        m_Lock.Unlock();
    }
    bool Get(unsigned int nIndex, SCard* pCard) {
        bool bRet = false;
        m_Lock.Lock();
        bRet = m_Poker.Get(nIndex, pCard);
        m_Lock.Unlock();
        return bRet;
    }
private:
    CPoker m_Poker;        // 被保護的資源
    CMutexLock m_Lock;     // 跨平台安全鎖(書中 6.2 節實作)
};

測試與驗證

測試代碼演示初始序列、洗牌後序列、再次洗牌序列的變化。若無書本配套鎖類別,直接將 CPokerWithLock Poker; 改為 CPoker Poker; 即可使用非線程安全版本。全程在 VS2008 下編譯通過,邏輯驗證無誤。

總結

這個洗牌算法雖非 Fisher-Yates 標準演算法,但以「雙重隨機選位交換」模擬真人洗牌的非確定性特徵,工程上簡單有效。線程安全封裝展示了資源鎖模式在實際專案中的輕量級應用。歡迎各位同好交流指正。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved