分享

C 随机数

 印度阿三17 2018-10-10

待着无聊,我又开始乱敲代码了。
这次是:随机数。。。
这里用到了C 11的新版随机数,感觉……还可以?

以下代码都是瞎写的
以下代码能够正常工作但不是最新的。我又进行了性能上的改动。(2018.9.20)

基类 Random,静态类,其中所有的函数都为静态函数。它的作用主要是生成一个随机数种子。
随机数种子的是自从执行代码的机器开机到执行时刻所经历的微秒数。一旦CPU不支持QueryPerformanceFrequency的API函数,那么则用当前时间的毫秒数作为种子。
此类是线程安全的。
每当获取的随机数数量达到一个周期,则重新获得种子。
获取每个随机数都不是顺序获取的。每获取一个,都会抛弃未知个之后的随机数(baseGener.discard 函数生效)。

派生类 RandomAvg,静态类,并且此类不能够被继承。它的作用是生成一个平均分布的随机数。
其中 HitProbability 函数表示,输入一个小于1.0的概率值,返回 bool。true表示命中了概率,false表示没有命中。
其中 HitExpectation 函数表示,输入一个小于1.0的期望概率值,返回无符号int,表示连续几次才能命中一次期望概率。例子,英雄联盟一个英雄在一场游戏的某个时刻的面板暴击率为20%,那么,他第一刀暴击的概率为20%;若没有出现暴击,则第二刀暴击概率为40%,以此类推,直到他出现暴击。也就是说,输入一个期望概率probExp,则在ceil(1.0/probExp)次中至少出现一次命中。

派生类 RandomNorm,非静态类,并且此类不能够被继承。它的作用是生成一个正态分布的随机数。构造函数的参数1为平均值,参数2是标准差。注意,返回的随机值并不是根据给定的随机X输出Y。具体百度“正态分布”。

派生类 RandomItem,非静态类,模板类,并且此类不能够被继承。它的作用是给定一个奖池vector,根据奖池中每一个奖励的权值来随机奖励。模版类型是结构体,里面应该包括奖励信息和一个奖励权值。构造函数的参数1为奖池,参数2为如何从奖励结构体中返回权值的函数,此函数用法在mian.cpp中提供了示例。之所以没有实现成静态类是由于效率考虑。参考static const ItemT& GetRandItem(const std::vector

Random.h

#include "stdinc.h"

#include <CoreWindow.h>
#include <random>
#include <atomic>
#include <functional>

#include <map>

#pragma once
class Random
{
protected:
    static std::mt19937_64 baseGener;

    static LARGE_INTEGER sysTime;
    static std::atomic<uint64_t> ranCount;
    static const uint64_t RandBase;

public:
    Random();
    ~Random();

public:
    static void Init();
    static void Reset();

    static uint64_t GetRandNum();

protected:
    static void SyncSeed(); 
};

class RandomAvg final : public Random
{
public:
    RandomAvg();
    ~RandomAvg();
    static void Init();
    static void Reset();

public:
    static uint64_t GetAveNum();
    static uint64_t GetAveNum(const uint64_t& min, const uint64_t& max); //Return [min, max).

    static bool HitProbability(const double& prob);
    static uint64_t HitExpectation(const double& probExp);

private:
    static std::uniform_int_distribution<uint64_t> avgNum;
};

class RandomNorm final : public Random
{
private:
    long double _avg;
    long double _stdDev;

public:
    RandomNorm();
    RandomNorm(long double avg, long double stdDev);

    ~RandomNorm();

public:
    void Init(const long double& avg, const long double& stdDev);
    void Reset();
    void Clear();

public:
    template<typename RetT = long double>
    RetT GetNormNum();

private:
    std::normal_distribution<long double>* ptrNormNum = nullptr;
};

template<typename ItemT>
class RandomItem final : public Random
{
public:
    RandomItem() = delete;
    RandomItem(const std::vector<ItemT>& items, const std::function<uint64_t(const ItemT&)>& GetWeightFunc);
    ~RandomItem();

public:
    void Init(const std::vector<ItemT>& items, const std::function<uint64_t(const ItemT&)>& GetWeightFunc);
    void Reset() = delete;
    void Clear();

public:
    static const ItemT& GetRandItem(const std::vector<ItemT>& items, const std::function<uint64_t(const ItemT&)>& GetWeightFunc);
    const ItemT& GetRandItem();
    const uint64_t& GetTotalWeight();

private:
    static void FillWeightMap(
        const std::vector<ItemT>& items, 
        const std::function<uint64_t(const ItemT&)>& GetWeightFunc, 
        std::map<uint64_t, const ItemT&>& __out witMap, 
        uint64_t& __out total);

private:
    std::map<uint64_t, const ItemT&> witMap;
    std::function<uint64_t(const ItemT&)> GetWeightFunc;
    uint64_t total;
};

//////////////////template functions//////////////////

template<typename RetT>
RetT RandomNorm::GetNormNum()
{
    if (ptrNormNum == nullptr)
    {
        Init(0.0, 1.0);
    }

    const long double& resVal = (*ptrNormNum)(baseGener);

    if (std::is_same<RetT, long double>::value ||
        std::is_same<RetT, double>::value ||
        std::is_same<RetT, float>::value)
    {
        return static_cast<RetT>(resVal);
    }
    else if (std::is_same<RetT, uint64_t>::value ||
        std::is_same<RetT, int>::value ||
        std::is_same<RetT, uint32_t>::value)
    {
        return static_cast<RetT>(std::llroundl(resVal));
    }
    else
    {
        return  static_cast<RetT>(resVal);
    }
}

template<typename ItemT>
inline RandomItem<ItemT>::RandomItem(const std::vector<ItemT>& items, const std::function<uint64_t(const ItemT&)>& GetWeightFunc)
{
    this->Init(items, GetWeightFunc);
}

template<typename ItemT>
inline RandomItem<ItemT>::~RandomItem()
{
    this->Clear();
}

template<typename ItemT>
void RandomItem<ItemT>::Init(const std::vector<ItemT>& items, const std::function<uint64_t(const ItemT&)>& GetWeightFunc)
{
    Random::Init();

    this->GetWeightFunc = GetWeightFunc;
    FillWeightMap(items, GetWeightFunc, this->witMap, this->total);
}

template<typename ItemT>
inline void RandomItem<ItemT>::Clear()
{
    witMap.clear();
    total = 0LL;
}

template<typename ItemT>
const ItemT & RandomItem<ItemT>::GetRandItem(const std::vector<ItemT>& items, const std::function<uint64_t(const ItemT&)>& GetWeightFunc)
{
    //total weight and each start...
    std::map<uint64_t, const ItemT&> witMap;
    uint64_t total = 0LL;
    FillWeightMap(items, GetWeightFunc, witMap, total);

    //Random item.
    const uint64_t& randNum = RandomAvg::GetAveNum(0, total);
    return witMap.upper_bound(randNum)->second;
}

template<typename ItemT>
inline const ItemT & RandomItem<ItemT>::GetRandItem()
{
    //Random item.
    const uint64_t& randNum = RandomAvg::GetAveNum(0, total);
    return witMap.upper_bound(randNum)->second;
}

template<typename ItemT>
inline const uint64_t & RandomItem<ItemT>::GetTotalWeight()
{
    return total;
}

template<typename ItemT>
void RandomItem<ItemT>::FillWeightMap(
    const std::vector<ItemT>& items, 
    const std::function<uint64_t(const ItemT&)>& GetWeightFunc,
    std::map<uint64_t, const ItemT&>& __out witMap, 
    uint64_t& __out total
)
{
    witMap.clear();
    total = 0LL;

    for (const ItemT& each : items)
    {
        uint64_t wit = GetWeightFunc(each);
        if (wit == 0LL)
        {
            continue;
        }
        total  = wit;
        witMap.insert(std::pair<uint64_t, const ItemT&>(total, each));
    }

    if (witMap.empty())
    {
        ErrorThrow("[Error] Items vector is empty or all item weight is 0.");
        return;
    }
}



Random.cpp

#include "Random.h"

//Random
std::mt19937_64 Random::baseGener;
LARGE_INTEGER Random::sysTime;
std::atomic<uint64_t> Random::ranCount = 0LL;
const uint64_t Random::RandBase = 100000000LL;

Random::Random()
{
    Init();
}

Random::~Random()
{
    Reset();
}

void Random::Init()
{
    //QueryPerformanceCounter
    LARGE_INTEGER _freq;
    LARGE_INTEGER _count;

    QueryPerformanceFrequency(&_freq);
    QueryPerformanceCounter(&_count);

    sysTime.QuadPart = _count.QuadPart * 1000000 / _freq.QuadPart;

    uint64_t seed = sysTime.QuadPart;
    if (seed == 0LL)
    {
        WarningThrow("QueryPerformanceFrequency function is nonsupport.");

        //Initializating the seed by time.
        {
            using namespace std::chrono;
            time_point<system_clock, milliseconds> tp =                 time_point_cast<milliseconds>(system_clock::now());
            auto tmp = duration_cast<milliseconds>(tp.time_since_epoch());

            seed = tmp.count();
        }
    }

    baseGener.seed(seed);
}

void Random::Reset()
{
    baseGener.seed();

    ranCount = 0LL;
}

uint64_t Random::GetRandNum()
{
    const uint64_t& ret = baseGener();
    SyncSeed();

    return ret;
}

void Random::SyncSeed()
{
    static const uint64_t& period =
        (static_cast<uint64_t>(std::powl(2, (baseGener.word_size * (baseGener.state_size - 1))) - 1)) / baseGener.state_size;

    if (  ranCount >= period)
    {
        ranCount = 0LL;
        Init();
    }
}

//RandomAvg
std::uniform_int_distribution<uint64_t> RandomAvg::avgNum;

RandomAvg::RandomAvg()
{
    RandomAvg::Init();
}

RandomAvg::~RandomAvg()
{
    RandomAvg::Reset();
}

void RandomAvg::Init()
{
    Random::Init();
}

void RandomAvg::Reset()
{
    RandomAvg::avgNum.reset();
}

uint64_t RandomAvg::GetAveNum()
{
    baseGener.discard(sysTime.LowPart % baseGener.state_size);
    SyncSeed();

    return avgNum(baseGener);
}

//Return [min, max).
uint64_t RandomAvg::GetAveNum(const uint64_t& min, const uint64_t& max)
{
    if (min >= max)
    {
        ErrorThrow(
            "GodHand::GetAveNum The minimum cannot be greater than or equal to the maximum. "
            "min: "   std::to_string(min)   "; max: "   std::to_string(max)   "."
        );
        return 0LL;
    }
    return GetAveNum() % (max - min)   min;
}

bool RandomAvg::HitProbability(const double& prob)
{
    if (prob >= 1.0)
    {
        return true;
    }

    if (prob <= 0.0)
    {
        return false;
    }

    const uint64_t randNum = GetAveNum(0, RandBase)   1;
    uint64_t exp = static_cast<uint64_t>(prob * RandBase);

    return randNum <= exp;
}

uint64_t RandomAvg::HitExpectation(const double & prob)
{
    if (static_cast<int>(prob) >= 1)
    {
        return 1;
    }

    if (prob <= 0.0)
    {
        return 0;
    }

    uint64_t rand = GetAveNum(0, RandBase);
    return static_cast<uint64_t>((rand   1) * (1.0 / prob)) / RandBase   1;
}

//Norm

RandomNorm::RandomNorm():
    RandomNorm(0.0, 1.0)
{
}

RandomNorm::RandomNorm(long double avg, long double stdDev)
{
    Init(avg, stdDev);
}

RandomNorm::~RandomNorm()
{
    Clear();
}

void RandomNorm::Init(const long double& avg, const long double& stdDev)
{
    _avg = avg;
    _stdDev = stdDev;

    Random::Init();
    delete ptrNormNum;

    ptrNormNum =         new  std::normal_distribution<long double>(avg, stdDev);
}

void RandomNorm::Reset()
{
    Random::Reset();
}

void RandomNorm::Clear() 
{
    delete ptrNormNum;
    ptrNormNum = nullptr;
}


stdinc.h

#pragma once

#include <thread>
#include <chrono>
#include <iostream>
#include <string>
#include <windows.h>

#include "LogThrow.h"

static const double DoubleESP = 1e-9;

LogThrow.h

#pragma once

#include <string>
#include <windows.h>
#include <ostream>

#ifdef _DEBUG
#define ErrorThrow(errMsg)  { LogThrow::_ErrorOccur(errMsg, __FILE__, __LINE__);}
#define WarningThrow(errMsg)  { LogThrow::_WarningOccur(errMsg, __FILE__, __LINE__);}
#define DebugLog(errMsg)  { LogThrow::_DebugLog(errMsg, __FILE__, __LINE__);}

#include <cassert>
#else
#define ErrorThrow(errMsg) {}
#endif // _DEBUG

#include <iostream>

namespace LogThrow
{
    static void _ErrorOccur(const std::string& errMsg, const std::string& fileName, int line)
    {
        std::string msg("[ERROR] "   errMsg   '\n'   "File: "   fileName   '\n'   "Line: "   std::to_string(line));
        std::logic_error(msg.c_str());
        MessageBox(NULL, msg.c_str(), NULL, MB_ICONINFORMATION | MB_OK);
        ExitProcess(0);
    }

    static void _WarningOccur(const std::string& errMsg, const std::string& fileName, int line)
    {
        std::string msg("[WARNING] "   errMsg   '\n'   "File: "   fileName   '\n'   "Line: "   std::to_string(line));
        std::cout << msg << std::endl;
    }

    static void _DebugLog(const std::string& errMsg, const std::string& fileName, int line)
    {
        std::string msg("[DEBUG] "   errMsg   '\n'   "File: "   fileName   '\n'   "Line: "   std::to_string(line));
        std::cout << msg << std::endl;
    }
};

main.cpp


#include "Tilee.h"
#include "Location.h"
#include "stdinc.h"
#include "Random.h"

using namespace Lunacia;

struct Item 
{
    uint32_t weight;
    int id;
};

int main(void)
{
    Random::Init();

    //uint64_t i = 0;
    //while(true)
    //{
    //  uint64_t less10count = 0;
    //  uint64_t times = RandomAvg::HitExpectation(0.0000002);
    //  if (times <= 10)
    //  {
    //      if (times == 1)
    //      {
    //          std::cout << "*************Boooom(yi fa ru hun)!!!  ";
    //      }
    //      else
    //      {
    //          std::cout << "times less 10: " << times << std::endl;
    //      }
    //      std::cout << "total count: " << i << std::endl;
    //        less10count;
    //  }
    //  if (less10count >= 100 || i >= MAXLONG64)
    //  {
    //      break;
    //  }
    //  i  ;
    //}

    //RandomNorm rn(18.0, 9);
    //for (int i = 0; i < 1000; i  )
    //{
    //  std::cout << rn.GetNormNum<double>() << std::endl;
    //}
    std::vector<Item> items;

    for (size_t i = 1; i < 100; i  )
    {
        Item* newItem = new Item();
        newItem->weight = 10;
        newItem->id = i;

        items.push_back(*newItem);
    }


    const std::function<uint64_t(const Item&)>& 
        GetWeightFunc = [](const Item& it)->uint64_t 
        { 
            return it.weight; 
        };

    RandomItem ri(items, GetWeightFunc);
    for (size_t i = 1; i < 100; i  )
    {
        Item ita = ri.GetRandItem();
        //std::cout << "total Weight: " << ri.GetTotalWeight() << std::endl;
        //std::cout << "weight: " << ita.weight << std::endl;
        std::cout << "id: " << ita.id << std::endl;
    }
    
    //GetWeightFunc(items[0]);
    ri.Clear();
    system("pause");
    return 0;
}
来源:http://www./content-1-46451.html

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多