在C#中随机“排序”(随机播放)整数列表的最有效方法

时间:2008-12-17 17:34:00

标签: c# random shuffle

我需要以最有效的方式随机“排序”整数列表(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;
    }
}

13 个答案:

答案 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