我需要以最有效的方式随机“排序”整数列表(0-1999)。有什么想法吗?
目前,我正在做这样的事情:
bool[] bIndexSet = new bool[iItemCount];
for (int iCurIndex = 0; iCurIndex < iItemCount; iCurIndex++)
{
int iSwapIndex = random.Next(iItemCount);
if (!bIndexSet[iSwapIndex] && iSwapIndex != iCurIndex)
{
int iTemp = values[iSwapIndex];
values[iSwapIndex] = values[iCurIndex];
values[iCurIndex] = values[iSwapIndex];
bIndexSet[iCurIndex] = true;
bIndexSet[iSwapIndex] = true;
}
}
答案 0 :(得分:53)
良好的线性时间混洗算法是Fisher-Yates shuffle。
您提出的算法会遇到一个问题,即当您接近洗牌结束时,您的循环将花费大量时间寻找尚未交换的随机选择的元素。一旦到达交换的最后一个元素,这可能需要不确定的时间。
此外,如果要排序奇数个元素,您的算法似乎永远不会终止。
答案 1 :(得分:30)
static Random random = new Random();
public static IEnumerable<T> RandomPermutation<T>(IEnumerable<T> sequence)
{
T[] retArray = sequence.ToArray();
for (int i = 0; i < retArray.Length - 1; i += 1)
{
int swapIndex = random.Next(i, retArray.Length);
if (swapIndex != i) {
T temp = retArray[i];
retArray[i] = retArray[swapIndex];
retArray[swapIndex] = temp;
}
}
return retArray;
}
已修改为处理实现IEnumerable的列表或其他对象
答案 2 :(得分:18)
我们可以使用扩展方法来获取任何IList集合的随机枚举器
class Program
{
static void Main(string[] args)
{
IList<int> l = new List<int>();
l.Add(7);
l.Add(11);
l.Add(13);
l.Add(17);
foreach (var i in l.AsRandom())
Console.WriteLine(i);
Console.ReadLine();
}
}
public static class MyExtensions
{
public static IEnumerable<T> AsRandom<T>(this IList<T> list)
{
int[] indexes = Enumerable.Range(0, list.Count).ToArray();
Random generator = new Random();
for (int i = 0; i < list.Count; ++i )
{
int position = generator.Next(i, list.Count);
yield return list[indexes[position]];
indexes[position] = indexes[i];
}
}
}
这对我们想要随机枚举的列表的索引使用反向Fisher-Yates shuffle。它有点大小(分配4 * list.Count字节),但在O(n)中运行。
答案 3 :(得分:5)
正如Greg指出的那样Fisher-Yates shuffle将是最好的方法。以下是维基百科的算法实现:
public static void shuffle (int[] array)
{
Random rng = new Random(); // i.e., java.util.Random.
int n = array.length; // The number of items left to shuffle (loop invariant).
while (n > 1)
{
int k = rng.nextInt(n); // 0 <= k < n.
n--; // n is now the last pertinent index;
int temp = array[n]; // swap array[n] with array[k] (does nothing if k == n).
array[n] = array[k];
array[k] = temp;
}
}
上面的实现依赖于 Random.nextInt(int)提供 足够随机和无偏见 结果
答案 4 :(得分:4)
我不确定效率因素,但是如果您不反对使用ArrayList,我使用了类似下面的内容:
private ArrayList ShuffleArrayList(ArrayList source)
{
ArrayList sortedList = new ArrayList();
Random generator = new Random();
while (source.Count > 0)
{
int position = generator.Next(source.Count);
sortedList.Add(source[position]);
source.RemoveAt(position);
}
return sortedList;
}
使用它,您不必担心中间交换。
答案 5 :(得分:2)
为了提高效率,您可以保留一组已交换的值/索引而不是布尔值来表示它们已被交换。从剩余池中选择随机交换索引。当池为0时,或者当您通过初始列表时,您就完成了。您没有可能尝试选择随机交换索引值。
进行交换时,只需将其从池中删除即可。
对于您正在查看的数据大小,这没什么大不了的。
答案 6 :(得分:2)
itemList.OrderBy(x=>Guid.NewGuid()).Take(amount).ToList()
答案 7 :(得分:1)
ICR的答案非常快,但结果数组未正常分发。如果您想要正态分布,请输入以下代码:
public static IEnumerable<T> RandomPermutation<T>(this IEnumerable<T> sequence, int start,int end)
{
T[] array = sequence as T[] ?? sequence.ToArray();
var result = new T[array.Length];
for (int i = 0; i < start; i++)
{
result[i] = array[i];
}
for (int i = end; i < array.Length; i++)
{
result[i] = array[i];
}
var sortArray=new List<KeyValuePair<double,T>>(array.Length-start-(array.Length-end));
lock (random)
{
for (int i = start; i < end; i++)
{
sortArray.Add(new KeyValuePair<double, T>(random.NextDouble(), array[i]));
}
}
sortArray.Sort((i,j)=>i.Key.CompareTo(j.Key));
for (int i = start; i < end; i++)
{
result[i] = sortArray[i - start].Value;
}
return result;
}
请注意,在我的测试中,此算法比ICR提供的算法慢6倍,但这是我能够提出正常结果分布的唯一方法
答案 8 :(得分:0)
不会有这样的工作吗?
var list = new[]{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
var random = new Random();
list.Sort((a,b)=>random.Next(-1,1));
答案 9 :(得分:0)
我猜最后两行必须在Micah的答案中互换。因此,代码可能看起来像
public static void shuffle(int[] array) {
Random rng = new Random(); // i.e., java.util.Random.
int n = array.Length; // The number of items left to shuffle (loop invariant).
while (n > 1) {
int k = rng.Next(n); // 0 <= k < n.
n--; // n is now the last pertinent index;
int temp = array[n]; // swap array[n] with array[k] (does nothing if k == n).
array[n] = array[k];
array[k] = temp;
}
}
答案 10 :(得分:0)
怎么样:
System.Array.Sort(arrayinstance, RandomizerMethod);
...
//any evoluated random class could do it !
private static readonly System.Random Randomizer = new System.Random();
private static int RandomizerMethod<T>(T x, T y)
where T : IComparable<T>
{
if (x.CompareTo(y) == 0)
return 0;
return Randomizer.Next().CompareTo(Randomizer.Next());
}
瞧!
答案 11 :(得分:0)
这是我用的。 当然,这不是最快的方法,但是对于大多数情况而言,它可能已经足够了,最重要的是,它非常简单。
IEnumerable<ListItem> list = ...;
Random random = new Random(); // important to not initialize a new random in the OrderBy() function
return list.OrderBy(i => random.Next());
答案 12 :(得分:-1)
我使用临时Hashtable创建了一个方法,允许Hashtable的自然键排序随机化。只需添加,阅读和丢弃。
int min = 1;
int max = 100;
Random random;
Hashtable hash = new Hashtable();
for (int x = min; x <= max; x++)
{
random = new Random(DateTime.Now.Millisecond + x);
hash.Add(random.Next(Int32.MinValue, Int32.MaxValue), x);
}
foreach (int key in hash.Keys)
{
HttpContext.Current.Response.Write("<br/>" + hash[key] + "::" + key);
}
hash.Clear(); // cleanup