在服务器里面需要用到从一组数中随机M个不同的数出来的需要,这种需求实现出来本身不复杂,最简单的就是一直随机,每次随机出来的数字判断是否跟之前有重复,如果没有就加入返回队列中。这种简单的算法在数据源很大,需要随机的数很少的时候,效率还是可以的,而且实现非常简单。但是在数据源本身就很小,而且需要随机的数据量比数据源差不都,那么就可能会发生需要随机多次才能随机出一个和现有随机数不同的数值,那么可能导致算法效率非常低下。
通过google了一些不同的随机算法之后,结合这个需要本身,针对数组下标替换算法进行了部分修改。主体的算法还是通过随机索引和当前数组的索引进行交换,然后当前索引后移,继续进行随机交换。这个算法需要注意的地方,就是数据源本身在进入算法之前需要按照数据范围进行排序初始化。
算法实现的代码如下:
1 void GenUniRandVec(vector<uint32_t>& orignVec, uint32_t uOrignSize, uint32_t uNum, vector<uint32_t>& randVec) 2 { 3 if (uNum > uOrignSize) // 超过能随机的范围 4 { 5 return; 6 } 7 8 /* 9 采用数组下标置换的方式,传入的orignVec需要是指定随机范围内数字排序好之后的数组 10 如果获得了指定数量的随机值后,算法就终止 11 */ 12 uint32_t uRandCount = 0; 13 for (uint32_t i=0; i<uOrignSize; i++) 14 { 15 if (uRandCount == uNum) 16 { 17 break; 18 } 19 20 if (i+1 == uOrignSize) 21 { 22 return; 23 } 24 25 // 在min和max之间随机一个数字 26 uint32_t uRandIndex = GenRandom(i+1, uOrignSize); 27 28 // 置换元素下标 29 std::swap(orignVec[i], orignVec[uRandIndex]); 30 randVec.push_back(orignVec[i]); 31 ++uRandCount; 32 } 33 34 return; 35 }
当随机出指定数量的不重复随机数之后,函数就退出。
这个算法不能用作数量非常大的数据源,会导致orignVec在初始化的时候需要非常长的时间。这种数据量非常大的数据源,就可以直接进行随机,如果发生重复再随机可以。