您有在工作中有类似这样的需求吗:从10万条不重复的数据中随机取出1千条不重复的数据?这里我们通过几种方法来实现此需求,并对每种方法进行性能比较,然后得出较优的方案,如果您有更优的方案,欢迎分享。
初始化数据:
//最大值
const int maxValue = 1000000;
//循环次数
int cycleCount = 1;
//获取数量
int getCount = 1000;
int[] iAry = new int[maxValue];
for (int i = 0; i < maxValue; ++i)
{
iAry[i] = i;
}
Random random = new Random();
第一种方案:使用LINQ获取随机数用Guid排序
/// <summary>
/// Linq获取随机数,使用Guid作为排序字段
/// </summary>
/// <param name="iAry">已初始化好的数据</param>
/// <param name="getCount">需要获取的数量</param>
private static int[] LinqGetRandomUseGuidSort(int[] iAry, int getCount)
{
var iResult = (from p in iAry select p).OrderBy(e => Guid.NewGuid()).Take(getCount);
//LINQ的延迟执行,所以到必须到这步才能出真正的执行时间
var list = iResult.ToArray<int>();
return list;
}
第二种方案:Linq获取随机数,使用int作为排序字段
/// <summary>
/// Linq获取随机数,使用int作为排序字段
/// </summary>
/// <param name="iAry">已初始化好的数据</param>
/// <param name="random">随机对象</param>
/// <param name="maxValue">最大数量</param>
/// <param name="getCount">需要获取的数量</param>
private static int[] LinqGetRandomUseRandomSort(int[] iAry, Random random, int maxValue, int getCount)
{
var iResult = (from p in iAry select p).OrderBy(e => random.Next(0, maxValue)).Take(getCount);
//LINQ的延迟执行,所以到必须到这步才能出真正的执行时间
var list = iResult.ToArray<int>();
return list;
}
第三种方案: 模拟LINQ获取随机数,使用int作为排序字段
public struct Item
{
public int Key;
public int Value;
}
public class ItemComparer : IComparer<Item>
{
public int Compare(Item x, Item y)
{
return x.Key - y.Key;
}
}
/// <summary>
/// 模拟LINQ获取随机数,使用int作为排序字段
/// </summary>
/// <param name="iAry">已初始化好的数据</param>
/// <param name="random">随机对象</param>
/// <param name="maxValue">最大数量</param>
/// <param name="getCount">需要获取的数量</param>
/// <returns></returns>
private static int[] ArrayGetRandomUseRandomSort(int[] iAry, Random random, int maxValue, int getCount)
{
Item[] ary = new Item[maxValue];
for (int i = 0; i < maxValue; ++i)
{
ary[i] = new Item { Key = random.Next(0, maxValue), Value = i };
}
//LINQ内部是用的快速排序
Array.Sort(ary, new ItemComparer());
int[] tempAry = new int[getCount];
for (int j = 0; j < getCount; ++j)
{
tempAry[j] = ary[j].Value;
}
return tempAry;
}
第四种方案:通过移动数组下标,获取前getCount数量的随机数
/// <summary>
/// 通过移动数据下标,获取前getCount数量的随机数
/// </summary>
/// <param name="iAry">已初始化好的数据</param>
/// <param name="random">随机对象</param>
/// <param name="maxValue">最大数量</param>
/// <param name="getCount">需要获取的数量</param>
private static int[] MoveArraySubscriptGetRandom(int[] iAry, Random random, int maxValue, int getCount)
{
//数组随机下标
int tempIndex = 0;
//当前得到的数量
int tempCount = 0;
//数组随机下标指向的数值
int tempValue = 0;
//当得到数量为getCount时跳出循环,那么数组的前getCount条数据就是要获取的随机值
while (tempCount < getCount)
{
//从当前得到的数量+1开始获取下标
tempIndex = random.Next(tempCount + 1, maxValue);
//把取到的值存入数组的前面位置
tempValue = iAry[tempIndex];
iAry[tempIndex] = iAry[tempCount];
iAry[tempCount] = tempValue;
tempCount += 1;
}
//返回数量为iAry的总数量,使用时只需使用前getCount条即可
return iAry;
}
F5运行,输出如下:
Ctrl+F5运行,输出如下:
当我们修改循环次数为10次和输入数量变为10000时,看一下效果:
综述:从以上可以看出,Ctrl+F5会比F5下性能高,第一种方案:使用LINQ获取随机数用Guid排序性能 < 第二种方案:使用LINQ获取随机数用Random的int排序性能 < 第三种方案:模拟LINQ获取随机数用int作为排序性能 < 第四种方案:使用移动数组下标获取随机数。而且【第四种方案:使用移动数组下标获取随机数】性能会远远优于其它三种方案。如果您有更优的方案,欢迎分享,谢谢!~
感谢:LQ和LKX两位同事在此问题上进行的讨论与贡献!~
作者:心海巨澜
出处:http://xinhaijulan.cnblogs.com
版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。