C++ 随机数

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

以下代码都是瞎写的
现在支持伪随机数生成(19.5.17)

基类 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& items, const std::function<uint64_t(const ItemT&)>& GetWeightFunc);函数,每次随机一次奖励都要重新计算总权值和权值区间,所以不建议在多次抽奖的循环中使用此静态方法。

Random.h


#include "stdinc.h"
#pragma once

namespace Lunacia
{

	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(bool isPRNG = false, uint64_t inSeed = 0);
		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 GetRandNum();
		static uint64_t GetRandNum(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 GetRandNum();
		long double GetPeakY();

	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::GetRandNum()
	{
		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::GetRandNum(0, total);
		return witMap.upper_bound(randNum)->second;
	}

	template<typename ItemT>
	inline const ItemT & RandomItem<ItemT>::GetRandItem()
	{
		//Random item.
		const uint64_t& randNum = RandomAvg::GetRandNum(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;
		}
	}

};	//End of namespace Lunacia.

Random.cpp

#include "Random.h"

namespace Lunacia
{
	//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()
	{
		Random::Init();
	}

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

	void Random::Init(bool isPRNG, uint64_t inSeed)
	{
		if (isPRNG)
		{
			baseGener.seed(inSeed);
			return;
		}

		//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::GetRandNum()
	{
		baseGener.discard(sysTime.LowPart % baseGener.state_size);
		SyncSeed();

		return avgNum(baseGener);
	}

	//Return [min, max).
	uint64_t RandomAvg::GetRandNum(const uint64_t& min, const uint64_t& max)
	{
		if (min >= max)
		{
			ErrorThrow(
				"RandomAvg::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 GetRandNum() % (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 = GetRandNum(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 = GetRandNum(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;
	}

	long double RandomNorm::GetPeakY()
	{
		return 1.0 / (std::powl(2 * PI, 0.5) * _stdDev);
	}


};


posted on 2018-10-10 19:26  __Even  阅读(694)  评论(0编辑  收藏  举报

导航