# .NET如何写正确的“抽奖”——数组乱序算法

```T[] ShuffleCopy<T>(IEnumerable<T> data, Random r)
{
var arr = data.ToArray();

for (var i = arr.Length - 1; i > 0; --i)
{
int randomIndex = r.Next(i + 1);

T temp = arr[i];
arr[i] = arr[randomIndex];
arr[randomIndex] = temp;
}

return arr;
}```

```int[] Measure(int n, int maxTime)
{
var data = Enumerable.Range(1, n);
var sum = new int[n];

var r = new Random();
for (var times = 0; times < maxTime; ++times)
{
var result = ShuffleCopy(data, r);
for (var i = 0; i < n; ++i)
{
sum[i] += result[i] != i ? 1 : 0;
}
}

return sum;
}```

```Util.Chart(
Measure(10, 50_0000).Select((v, i) => new { X = i, Y = v}),
x => x.X, y => y.Y, Util.SeriesType.Bar
).Dump();```

`T[] ShuffleCopy<T>(IEnumerable<T> data, Random r) => data.ToArray();`

# 不然呢？

## 错误示例1

```[0,1,2,3,4,5,6,7,8,9].sort((a, b) => Math.random() - 0.5)
// 或者
[0,1,2,3,4,5,6,7,8,9].sort((a, b) => Math.random() - Math.random())```

`(10) [8, 4, 3, 6, 2, 1, 7, 9, 5, 0]`

```T[] ShuffleCopy<T>(IEnumerable<T> data, Random r) =>
data.OrderBy(v => r.NextDouble() < 0.5).ToArray();```

`50万`条数据统计结果如下：

### 为什么会这样？

```T[] ShuffleCopy<T>(IEnumerable<T> data, Random r) => data
.Select(v => new { Random = r.NextDouble(), Value = v})
.OrderBy(v => v.Random)
.Select(x => x.Value)
.ToArray();```

## 错误示例2

```T[] ShuffleCopy<T>(IEnumerable<T> data, Random r)
{
var arr = data.ToArray();

for (var i = 0; i < arr.Length; ++i)
{
int randomIndex = r.Next(arr.Length);

T temp = arr[i];
arr[i] = arr[randomIndex];
arr[randomIndex] = temp;
}

return arr;
}```

`Measure(10, 50_0000).Select(x => (x / 50_0000.0).ToString("P2")).Dump();`

```0 90.00%
1 90.54%
2 90.97%
3 91.29%
4 91.41%
5 91.38%
6 91.31%
7 90.97%
8 90.60%
9 90.01%```

```0 90.02%
1 90.05%
2 90.04%
3 89.98%
4 90.05%
5 90.04%
6 90.07%
7 90.03%
8 89.97%
9 90.02%```

```// 安装NuGet包：FlysEngine.Desktop
using var form = new RenderWindow();
var r = new Random();
var points = Enumerable.Range(0, 10000)
.Select(x => (x: r.NextDouble() + r.NextDouble(), y: r.NextDouble()))
.ToArray();
form.Draw += (o, ctx) =>
{
ctx.Clear(Color.CornflowerBlue);
foreach (var p in points)
{
ctx.FillRectangle(new RectangleF(
(float)p.x / 2 * ctx.Size.Width,
(float)p.y * ctx.Size.Width,
ctx.Size.Width / 100, ctx.Size.Height / 100), form.XResource.GetColor(Color.Black));
}
};
RenderLoop.Run(form, () => form.Render(0, PresentFlags.None));```

## 错误示例3

```T[] ShuffleCopy<T>(IEnumerable<T> data, Random r)
{
var arr = data.ToArray();

for (var i = arr.Length - 1; i > 0; --i)
{
int randomIndex = r.Next(i);

T temp = arr[i];
arr[i] = arr[randomIndex];
arr[randomIndex] = temp;
}

return arr;
}```

# 总结

```T[] ShuffleCopy<T>(IEnumerable<T> data, Random r)
{
var arr = data.ToArray();

for (var i = arr.Length - 1; i > 0; --i)
{
int randomIndex = r.Next(i + 1);

T temp = arr[i];
arr[i] = arr[randomIndex];
arr[randomIndex] = temp;
}

return arr;
}```

133 篇文章34 人订阅

0 条评论

## 相关文章

9410

### 图片相似度识别：aHash算法

aHash、pHash、dHash是常用的图像相似度识别算法，原理简单，实现方便，个人把这三个算法作为学习图片相似度识别的入门算法。本次起，从aHash开始，对...

18120

8320

### 最短路专题2 | CodeForces 449B - SPFA算法

Jzzhu is the president of country A. There are n cities numbered from 1 to n in ...

10220

### Spring Boot 概述

Spring Boot可以以jar包的形式独立运行，运行一个Spring Boot项目只需要通过java -jar xx.jar来运行 。

13040

14820

### 不写代码实现条件循环？只用Jmeter就能实现

Jmeter是常用的接口测试工具，可以方便地对各种接口进行测试。有时，我们可能需要在一次测试流程中对某个接口进行若干次请求，以达成一定目的。这时，我们无需在脚本...

13730

5510

10320

### SAS-爬取帖子下的邮箱，给他们发一封邮件...

SAS中获取网页上信息的原理其实很简单，就是将网页上的html代码给导入进数据集中，然后利用一定规律来获取自己想要的提取的信息...（目前个人浅显的理解），那么...

8330