C#范围内生成不重复随机数
范围为 [startNum, endNum] , 其中0<=startNum<endNum 。
生成的个数为needNum,并且needNum <= endNum - startNum + 1
因为C#的Random类不进行种子设置的话是伪随机,所以我们要改进一下Random类的Next方法,让它尽可能朝着真随机靠近。
优化后的代码如下:
public static int Random(int starNum, int endNum)
{
byte [] randomBytes = new byte[4];
RNGCryptoServiceProvider rngProvider = new RNGCryptoServiceProvider();
rngProvider.GetBytes(randomBytes);
Int32 iSeed = BitConverter.ToInt32(randomBytes, 0);
Random random = new Random(iSeed);
return random.Next(starNum, endNum + 1);
}
public static bool IsParameterValid(int startNum, int endNum, int needCount)
{
if (startNum < 0 || endNum <0 || startNum > endNum || needCount == 0 || needCount > endNum - startNum + 1)
{
return false;
}
return true;
}
方法一 使用两个数组,从第一个数组中随机位置抽取一个,放到第二个数组中,并且在第一个数组中删除这个值, 接下来从第一个数组的剩余数据中重复上面的步骤,直到第二个数组中获得了目标个数的值停止。 代码如下:
public static List<int> PickMethod1(int startNum, int endNum, int needCount) { if (!IsParameterValid(startNum, endNum, needCount)) { return null; } List<int> posList = new List<int>(); List<int> newPosList = new List<int>(); for (int i = 0; i <= endNum - startNum; i++) { posList.Add(i); } int count = 0; while (count < needCount) { int pickPos = Random(0, posList.Count -1); newPosList.Add(posList[pickPos]); posList.RemoveAt(pickPos); count++; } return newPosList; }
优点:从剩余的数据中随机取值,不用判断重复,相对于方法二来说速度会更快,特别是needNum 很接近 endNum - startNum + 1的时候,会更加明显
缺点:需要一个额外的数组,需要更多的内存方法二 使用HashTable,在一个循环中不停地在范围内随机的取值,并且检查检查这个值是否在HashTable中,如果不存在则存放到Hashtable中, 如果存在则重复前面的步骤,直到Hashtable的Count数量达到想要的数量值。
代码如下:public static List<int> PickMethod2(int startNum, int endNum, int needCount) { if (!IsParameterValid(startNum, endNum, needCount)) { return null; } Hashtable tb = new Hashtable(); while (tb.Count < needCount) { int pickPos = Random(0, endNum - startNum); if (!tb.ContainsKey(pickPos)) { tb.Add(pickPos, pickPos); } } List<int> posList = new List<int>(); foreach (int key in tb.Keys) { posList.Add(key); } return posList; }
优点:只需要一个Hashtable就可以解决,节省空间,需要的内存更少,算法也更简洁。
缺点:每次随机获取一个数据,都需要判断是否已存在,这会加大计算量,特别是needNum 很接近 endNum - startNum + 1的时候,会更加明显
完整代码:
MathTools.cs
希望它能帮到你或者帮你找到思路(^_^)选个表情,或者留个评论吧!