C++11引入了新的随机数生成器mt19937
mt
是因为这个伪随机数产生器基于Mersenne Twister
算法。
19937
是因为产生随的机数的周期长,可达到2^19937-1
。
虽然周期很长,但这是在种子固定后依次生成2^19937-1
个数才能保证不重复,实际的应用是需要重启或者多开的,每次重启种子会重设,迭代次数又重新开始,如果我们把种子设成固定值显然每次生成的序列和前一次都会是重复的,如果我们用时间作为种子,这样可以让每次的序列都从一个随机位置开始迭代,但是这样也就无法保证本次周期内生成的数不会和之前的某次生成的重复,或许不巧的情况下生成了10个就重复了,也是有极小概率的。
为了解决这个问题,我们可以更进一步的,在用时间作为随机种子的基础上再在前面拼接当前时间,比如拼接“%Y%m%d%H%M%S”精确到秒,因为这串时间必然是递增的,即使后面不巧重复了,拼接后的整体也是唯一的,因此只要确保在一秒内没有重复即可,而在一秒内除非程序重启,否则由mt19937
的周期保证不会重复。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include <chrono> #include <sstream> #include <random> #include <mutex> #include <iomanip>
inline std::string gen_uuid() { static std::mutex s_mutex; std::lock_guard lock(s_mutex); static std::mt19937 seed_engine( std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock().now().time_since_epoch()).count() ); std::stringstream ss; int64_t t = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock().now().time_since_epoch()).count(); auto tm = std::localtime(&t); std::uniform_int_distribution<std::size_t> random_gen; std::size_t value = random_gen(seed_engine); ss << std::put_time(tm, "%Y%m%d%H%M%S") << std::hex << std::setw(16) << std::setfill('0') << value; return ss.str(); }
|
注意到函数整体用mutex加了锁,首先我不确定random_gen(seed_engine)
是否线程安全,其次我确定std::localtime
不是线程安全的,因为其返回一个std::tm
结构体的指针,可能是指向std::localtime
内的静态成员变量,我不清楚为什么STL这样设计这个函数。
进一步改进方式,如果考虑到程序多开,可以给每个程序一个ID,在生成时传递gen_uuid(app_id)
然后拼接到生成的随机字符串中。
Python版本,后面通过{:016x}
截断16位16进制数是为了和C++生成的保持长度一致
1 2 3 4 5 6 7
| import datetime import uuid
def gen_uuid(): return "{}{:016x}".format(datetime.datetime.now().strftime("%Y%m%d%H%M%S"), (uuid.uuid4().int&(0xFFFFFFFFFFFFFFFF)) )
|