随机“排序”的最有效方法 (随机排列)C# 中的整数列表
我需要以最有效的方式对整数列表(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;
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(13)
一个好的线性时间混洗算法是 Fisher-Yates shuffle。
您会发现您提出的算法存在的一个问题是,当您接近洗牌结束时,您的循环将花费大量时间寻找尚未交换的随机选择的元素。 一旦到达最后一个要交换的元素,这可能需要不确定的时间。
另外,如果要排序的元素数量为奇数,您的算法似乎永远不会终止。
A good linear-time shuffling algorithm is the Fisher-Yates shuffle.
One problem you'll find with your proposed algorithm is that as you near the end of the shuffle, your loop will spend a lot of time looking for randomly chosen elements that have not yet been swapped. This may take an indeterminate amount of time once it gets to the last element to swap.
Also, it looks like your algorithm will never terminate if there are an odd number of elements to sort.
修改为处理列表或其他实现 IEnumerable 的对象
modified to handle lists or other objects implementing IEnumerable
我们可以从中创建一个扩展方法,以获得任何 IList 集合的随机枚举器。
这对我们想要随机枚举的列表的索引使用反向 Fisher-Yates 洗牌。 它的大小有点大(分配 4*list.Count 字节),但运行时间为 O(n)。
We can make an extension method out of this to get a Random enumerator for any IList collection
This uses a reverse Fisher-Yates shuffle on the indexes of the list we want to randomly enumerate through. Its a bit of a size hog (allocating 4*list.Count bytes), but runs in O(n).
正如 Greg 指出的那样,Fisher-Yates shuffle 将是最好的方法。 以下是来自维基百科的算法的实现:
As Greg pointed out the Fisher-Yates shuffle would be the best approach. Here is an implementation of the algorithm from Wikipedia:
我不确定效率因素,但我使用了类似于以下内容的东西,如果您不反对使用 ArrayList:
使用这个,您不必担心中间交换。
I am not sure of the efficiency factor, but I have used something similar to the following, if you aren't opposed to using an ArrayList:
Using this, you do not have to worry about the intermediate swapping.
为了提高效率,您可以保留一组已交换的值/索引,而不是用于指示它们已交换的布尔值。 从剩余池中选择随机交换索引。 当池为 0 时,或者当您完成初始列表时,您就完成了。 您没有可能尝试选择随机交换索引值。
当你进行交换时,只需将它们从池中删除即可。
对于您正在查看的数据大小来说,这没什么大不了的。
To improve your efficiency you can keep a set of values/indices that have been swapped rather than a boolean for indicating they were swapped. Pick your randomized swap index from the remaining pool. When the pool is 0, or when you made it through the initial list then you are done. You don't have the potential to try to select a random swap index value.
When you do a swap, just remove them from the pool.
For the size of data you are looking at it is no big deal.
ICR 的答案非常快,但生成的数组不是正态分布的。 如果你想要正态分布,代码如下:
请注意,在我的测试中,该算法比 ICR 提供的算法慢 6 倍,但这是我能想出的获得正态结果分布的唯一方法
ICR's answer is very fast, but the resulting arrays aren't distributed normally. If you want a normal distribution, here's the code:
Note that in my tests, this algorithm was 6 times slower than the one ICR provided, however this is the only way I could come up with to get a normal result distribution
这样的事情行不通吗?
Wouldn't something like this work?
怎么样:
瞧!
what about :
voila!
这是我用过的。
这肯定不是最快的,但对于大多数情况来说它可能已经足够好了,最重要的是,它非常简单。
Here is what I used.
This is surely not the fastest one, but it is probably good enough for most cases and most importantly, it is very simple.
您可以使用名为 ListShuffle 的 NuGet 包 (源代码)使用 Fisher-Yates 算法以线程安全的方式对列表进行洗牌,并具有可选的加密强随机性。
或(性能较差但加密能力强)
You can use a NuGet package called ListShuffle (source code) to shuffle a list in a thread-safe way using the Fisher-Yates algorithm, with optional cryptographically-strong random.
or (less performant but cryptographically-strong)
我使用临时哈希表创建了一种方法,允许哈希表的自然键排序随机化。 只需添加、读取和丢弃即可。
I made a method using a temporary Hashtable, allowing the Hashtable's natural key sort to randomize. Simply add, read and discard.