【问题标题】:Generate random number with my array values without repetition用我的数组值生成随机数而不重复
【发布时间】:2013-01-13 15:10:30
【问题描述】:

我是 C# 新手,我正在使用数组制作应用程序。我有一个数组,其数字如下所示:

int[] array2 = new int[] { 1, 3, 5, 7, 9 };

我需要做的是改变这些数字在数组中的顺序而不重复,因为当我使用随机函数时,这会显示重复的数字。

我看到了这个方法,但不知道如何用数字应用它:http://www.dotnetperls.com/shuffle

【问题讨论】:

  • 你想在这里实现什么,你的示例数组没有任何重复?
  • var array2 = new int[] { 1, 3, 5, 7, 9 }.OrderBy(_ => rnd.Next()) 还不够吗?
  • 您是否需要保证每一种可能的排序都具有相同的可能性?您是否还需要保证不可能从过去的洗牌中推断出未来的洗牌? (后一个要求对于机会游戏是必要的;如果您可以从当前的牌组中推断出未来的牌组,那么在线扑克会变得容易得多。)如果任何一个问题的答案都是肯定的,那么这里发布的答案都不是正确的。如果您不关心小偏差或攻击者预测您的内部状态的能力,那么这里发布的答案是合理的。

标签: c# arrays shuffle


【解决方案1】:

您可以使用以下 LINQ 链:

int[] array2 = new int[] { 1, 3, 5, 7, 9 };
var random = new Random();
var total = (int)array2.
    OrderBy(digit => random.Next()).
    Select((digit, index) => digit*Math.Pow(10, index)).
    Sum();

首先,它对元素进行随机排序,然后选择每个元素乘以 10 的索引次方,然后将它们相加并将结果转换为整数。另外,请注意我没有为您的Random 实例提供有用的种子。您可能想要这样做,以产生伪随机结果。

您可能还想使用here 中描述的求幂方法,以避免强制转换为整数。

编辑:正如 Rhumborl 指出的,您可能只需要改组后的数组。在这种情况下:

var shuffledArray = array2.OrderBy(n => random.Next()).
   ToArray();

应该适合你。

【讨论】:

  • 我认为 OP 希望在它的末尾仍然有一个数组,在这种情况下只需 var random = new Random(); array2 = array2.OrderBy(n => random.Next()).ToArray(); 就可以了。
  • @Rhumborl 我的回答基于问题的标题,但从其内容来看,您所说的可能是真的。我会编辑我的帖子。
  • 请注意,此算法虽然如这里所示正确,但有两个问题。首先,如果列表非常长,那么这是一个 O(n lg n) 操作,而不是像 Knuth shuffle 那样的 O(n) 操作。其次,这取决于 orderby 实现仅对排序元素的序列进行一次评估这一事实。重新评估排序子句的实现显然会得到两个完全不同的随机结果,这可能会导致排序算法出现问题。我的观点是,在使用随机性进行洗牌时要格外小心。
  • 然而,这种技术的最大好处是它不会像 Knuth shuffle 那样破坏原始列表。
  • @Eve:确实,如果你说OrderBy(digit => (new Random()).Next()),那么如果排序恰好小于刻度粒度,你会得到相同的种子到当前时间“随机”数字,并且排序变成了无操作。 myList.Sort((x, y) => random.Next(-1, 2));的问题在于,比较可以告诉你A项大于B项,B项大于C项,C项大于A项,允许排序算法永远崩溃或循环如果你给它这样一个行为不端的比较关系。
【解决方案2】:

如果您使用 C# 工作,则最好使用 C# 结构。

你可以使用这个通用函数

using System;
using System.Collections.Generic;

public static class ListExtensions
{
    public static void Shuffle<T>(this IList<T> list)
    {
        var randomNumber = new Random(DateTime.Now.Millisecond);
        var n = list.Count;
        while (n > 1)
        {
            n--;
            var k = randomNumber.Next(n + 1);
            var value = list[k];
            list[k] = list[n];
            list[n] = value;
        }
    }
}

然后您的代码应如下所示:

List<int> list2 = new List<int>(){1, 3, 5, 7, 9};
Shuffle(list2);

【讨论】:

  • Knuth shuffle 的良好实现。我注意到 int 数组已经可以转换为 IList&lt;int&gt;,因此如果您不想使用,则无需使用 List&lt;int&gt;
  • 不应该是list2.Shuffle()吗?在创建扩展方法时。
【解决方案3】:

我不太清楚你所说的改变顺序而不重复是什么意思。如果您只想生成一个从不重复的随机数,您可以这样做

private Random rand = new Random();
private List<int> used = new List<int>;
protected int randomNonrepeating() {
   int i = rand.next();
   while(used.contains(i))
       i = rand.next();
   used.add(i);
   return i;
}

我的猜测是,这并不是您想要的。如果您只想在提供的链接上修改算法以使用整数数组而不是字符串。您只需要更改类型。像这样的

using System;

使用 System.Collections.Generic; 使用 System.Linq;

静态类 RandomStringArrayTool { 静态随机 _random = new Random();

public static string[] RandomizeStrings(int[] arr)
{
List<KeyValuePair<int, int>> list = new List<KeyValuePair<int, int>>();
// Add all strings from array
// Add new random int each time
foreach (var s in arr)
{
    list.Add(new KeyValuePair<int, int>(_random.Next(), s));
}
// Sort the list by the random number
var sorted = from item in list
         orderby item.Key
         select item;
// Allocate new string array
int[] result = new string[arr.Length];
// Copy values to array
int index = 0;
foreach (KeyValuePair<int, int> pair in sorted)
{
    result[index] = pair.Value;
    index++;
}
// Return copied array
return result;
}

}

希望这会有所帮助。

【讨论】:

  • 如果列表很大,您的第一个算法效率不高。如果列表中有一千个项目,计算出你的算法将运行多长时间是有益的。
  • 您的第二个算法很好,但您编写的算法比需要的时间长了大约 15 倍。 return (from item in list orderby _random.Next() select item).ToArray(); 会做得很好。
【解决方案4】:

正如您所说,您已经有一个包含数字的数组可以使用。所以我不会向你展示如何制作一个充满唯一数字的数组。

这就是你如何洗牌。

  1. 弄清楚如何生成介于 0 和数组长度之间的随机数。
  2. 编写一个从 length_of_the_array-1 到 0 的循环。(将此索引用作 idx1)

在循环内部执行以下操作:

一个。使用步骤 1 中的方法生成介于 0 和 idx1(含)之间的随机数。(让随机数为 idx2。)
湾。交换数组中 idx1 和 idx2 的元素。

可以通过执行以下操作来完成简单的交换:

int tmp = 数组[idx1];
数组[idx1] = 数组[idx2];
数组[idx2] = tmp;

循环结束,你得到一个随机排列的数组。

【讨论】:

  • 你描述的洗牌算法引入了偏见;这是描述不正确的 Knuth Fischer Yates 洗牌的最常见方式。 Jeff 有一个很好的分析,描述了为什么这个算法在这里是错误的:codinghorror.com/blog/2007/12/shuffling.html
  • Roee Gaveril 的回答给出了 shuffle 算法的正确实现。
  • 我的意思是使用一个随机数而不是两个。然后它与Roee的相同。我将忽略生成的整个随机数可以很容易地从 Jeff 文章中计算出来,但这些都是程序使用的伪随机数生成器。为了正确起见,我也会编辑我的答案。
猜你喜欢
  • 2018-07-03
  • 2012-08-30
  • 1970-01-01
  • 2015-07-12
  • 2016-05-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多