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

工業級加權隨機數生成器:CTonyRandomArea 設計與實現

     編輯:關於C語言

問題背景

模擬設備多狀態行為:每種狀態出現概率不同(如 12%、40%、40%、7%、1%)。需一個隨機生成器,按配置概率分布返回狀態值,用於 5000 萬條記錄、20 萬設備的高併發壓測場景。

核心設計:查表法(空間換時間)

  1. 輸入:double percent[](不要求和為 100,內部自動歸一化)。
  2. 構建 10000 槽位表(精度 0.01%),按比例填充狀態值。
  3. 運行時:rand() % 10000 命中槽位,直接返回對應狀態。

優勢:O(1) 查詢、無鎖並行安全(構建後只讀)、分布精確、無條件分支預測失敗。

完整實現


#define MAX_TOKENS 100
#define TABLE_SIZE 10000

class CTonyRandomArea {
public:
    // pPercent: 概率數組;count: 有效類型數
    CTonyRandomArea(double *pPercent, char count) {
        m_count = (count > MAX_TOKENS) ? MAX_TOKENS : count;

        // 1. 複製並歸一化百分比
        double sum = 0;
        for (int i = 0; i < m_count; ++i) {
            m_percent[i] = pPercent[i];
            sum += m_percent[i];
        }
        if (fabs(sum - 100.0) > 1e-9) { // 非 100 自動歸一化
            for (int i = 0; i < m_count; ++i)
                m_percent[i] = m_percent[i] / sum * 100.0;
        }

        // 2. 轉換為整數槽位數(0.01% 精度)
        for (int i = 0; i < m_count; ++i)
            m_slots[i] = (short)(m_percent[i] * 100); // 10000 * 1% = 100

        // 3. 填充查表數組
        int pos = 0;
        for (int i = 0; i < m_count; ++i) {
            int end = pos + m_slots[i];
            for (int j = pos; j < end; ++j)
                m_table[j] = (char)i;
            pos = end;
        }
        // 兜底:浮點誤差導致 pos < 10000 時填最後一類
        for (int j = pos; j < TABLE_SIZE; ++j)
            m_table[j] = m_count - 1;
    }

    // 核心 API:按概率返回隨機類型
    char GetRandomType() {
        return m_table[GetRandomBetween(0, TABLE_SIZE)];
    }

    // 可選:指定索引查表(測試/調試用)
    char GetType(int idx) {
        if (idx < 0) idx = 0;
        if (idx >= TABLE_SIZE) idx = TABLE_SIZE - 1;
        return m_table[idx];
    }

    void PrintfInfo() { /* 打印內部分布驗證 */ }

private:
    double m_percent[MAX_TOKENS];
    short  m_slots[MAX_TOKENS];
    char   m_table[TABLE_SIZE];
    char   m_count;
};

// 跨平台隨機數工具(避免 rand() 返回 0)
inline int GetRandomBetween(int min, int max) {
    if (min > max) { int t = min; min = max; max = t; }
    if (min == max) max = min + 10;
    int range = max - min;
    int r = rand(); if (r == 0) r = 1; // 簡單處理
    return min + (r % range);
}

測試代碼


void TestCTonyRandomArea() {
    double p[5] = {12.0, 40.0, 40.0, 7.0, 1.0};
    CTonyRandomArea gen(p, 5);
    gen.PrintfInfo();

    for (int i = 0; i < 20; ++i)
        printf("RandType = %d\n", gen.GetRandomType());
}

工程亮點解析

特性實現細節
自動歸一化輸入原始計數或百分比均可,構造函數內部處理
精度控制10000 槽位 = 0.01% 精度,滿足工業級需求
無鎖並行構造後 m_table 只讀,多線程直接調用 GetRandomType 無需鎖(符合《0bug》P50「用鎖最高境界——不用」)
API 分層GetRandomType(高層)、GetType(中層查表)、GetRandomBetween(底層隨機),調用者按需選擇
防禦性編程索引越界鉗制、rand()==0 修正、參數順序自動交換

應用場景延伸

  • 游戲 NPC 行為權重:巡邏/休息/攻擊/聊天按概率觸發。
  • 遊戲屬性觸發:暴擊、冰凍、燃燒等特效按配置概率生效。
  • 網絡流量模擬:不同協議包按比例混發。
  • A/B 測試分流:用戶按權重分配到不同實驗組。

避坑指南

  • 構造函數過長:違反「單一職責」,建議拆分為 Normalize()、BuildTable() 私有方法。
  • 浮點比較:if (sum != 100.0) 風險極大,已修正為 fabs(sum-100) > 1e-9。
  • 隨機種子:項目初始化階段統一 srand((unsigned)time(nullptr)),勿重複調用。
  • 大數組棧溢出:m_table[10000] 為成員變量(堆/數據段),非棟上分配,安全。

本文出自 “肖舸的blog”,原文地址:https://tonyxiaohome.blog.51cto.com/925273/296530

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