【问题标题】:Random number generator in C# - unique valuesC# 中的随机数生成器 - 唯一值
【发布时间】:2010-02-28 14:27:27
【问题描述】:

我正忙于用 C# 编写一个数组。我可以用随机生成器填充它,但现在我的问题是我该怎么做,但是我可以检查该值是否已经在数组中,如果是,则生成一个新值

额外信息:
最大值:100
元素数量:100

重要的请继续我的想法

我的想法

public void FillArray(int[] A, int Range)
{
    for (int I = 0; I < A.Length; I++)
    {
        A[I] = ValidNumber(T, I, Range);
    }
} /* Fill Array */

选择排序的实现

public void SelectionSort(int[] A)
{
    int K, X;
    for (int I = 0; I < A.Length - 1; I++)
    {
        K = I;
        X = A[K];
        for (int J = I + 1; J < A.Length; J++)
        {
            if (A[J] < X)
            {
                K = J;
                X = A[K];
            }
        }
        A[K] = A[I];
        A[I] = X;
    }
} /* Selection sort */

这些只是一些想法,现在我想知道如何修复它,所以如果那里有 allread (fillarray),我可以用选择排序查看,如果用新的随机值替换它。所以我想用整数创建一个随机数组 - 从 1 到 100 以随机顺序

【问题讨论】:

  • 闻起来像家庭作业,尤其是在(有效的、好的)建议下方的 cmets,其骨架似乎是作业的一部分:“从此代码开始”
  • 是的,这确实是作业的一部分,我不会撒谎说我只是被困在一部分中,并且想要一些我认为还不错的提示/反馈
  • 正确的代码缩进和更好的变量名将帮助人们理解您的代码,从而为您提供更好的解决方案。

标签: c# arrays random


【解决方案1】:

我该怎么做,但是这样我可以检查该值是否已经在数组中,如果是,则生成一个新值

永远不要这样做,因为这是一个非常糟糕的主意

为了说明为什么这是一个糟糕的想法,请考虑同一问题的另一个版本:通过以下过程将一百万个数字随机排序:

  1. 从一到一百万中选择一个数字。
  2. 检查它是否已经在列表中。
  3. 如果是,请返回步骤 1
  4. 否则,将号码添加到列表中。
  5. 列表中是否包含一百万项?如果是,你就完成了。如果没有,请返回第 1 步。

显然这是可行的。这是个好主意吗?假设你快完成了。该列表上有 999999 项。唯一缺少的项目是 857313。你是做什么的?你选择一个随机数,比如 12。现在你检查列表中的 999999 个项目,看看它们中是否有任何一个是 12。12 可能是你选择的第一个数字,所以找到它可能很快。或者它可能是最后一个,所以需要很长时间。平均需要 500000 次检查才能确定 12 是否在列表中。确实如此,因为列表中只缺少一个数字。

12 没有成功。回到起点。选择另一个随机数,例如 53259。在列表中吗?还有 50 万张支票。

继续这样做,直到生成 857313,每百万次尝试发生一次。

因此,平均而言,将最后一项放入列表中需要 500000 x 1000000 = 5000 亿次比较。这可能需要更多的时间。可能需要进行数万亿次比较。或者你可能会很幸运,这需要一个。但平均而言,有五万亿次比较。

这是一种产生随机排序列表的可怕方式。

有两种对列表进行随机排序的好方法

(1) 制作一个给定排序功能的可以对列表进行排序的设备。提供基于随机种子的稳定排序。

请注意,当被问及“A 是否大于 B?”时,您应该通过创建一个返回随机结果的方法来产生随机排序。那是一个不稳定的排序;许多排序算法都基于稳定的排序顺序,当给定不稳定的排序顺序时,它们会进入无限循环或出现其他不良行为。

这个算法是 O(n lg n) 并且有一个很好的特性,它很容易用标准部分写出来,正如其他答案所表明的那样。对于典型实现中的小列表,它也非常快。

(2) 从源列表中按索引随机选择一个项目,将其从源列表中删除,然后将其放在目标列表中。

后者被称为 Knuth Shuffle 或 Fischer-Yates Shuffle,是一种非常快速的算法。您可以“就地”完成,将现有数组更改为打乱顺序,或创建一个新列表。它还有一个很好的特性,您可以“为游戏付费”,根据需要对列表的“顶部”进行洗牌。如果您有一百万个项目要洗牌,但您只需要前一百个,您可以计算出前一百个的排序顺序并称之为好。

【讨论】:

  • 我觉得第二个建议很出色。
  • 很好的答案,这是一个很好的方法,因为这个问题与家庭作业有关,你不只是说复制这个,你就完成了 +1
  • 我目前正在使用第二种方法,非常棒。很好的答案,+1。
【解决方案2】:

下面将生成一个随机排列的数字 1-100 的数组。

    Random rnd = new Random();
    var randomNumbers = Enumerable.Range(1, 100).OrderBy(i => rnd.Next()).ToArray();

【讨论】:

  • 您应该向Random() 提供种子,例如new Random(DateTime.Now.Millisecond)
  • Random 上的空构造函数默认执行此操作(Enviroment.TickCount)
  • @Obalix:不,你不应该。如果您希望以后能够再次提供 same 种子,则应仅提供种子。否则,请依赖默认播种。
  • 另外,请注意DateTime.Now.Millisecond 将永远只有 1000 个唯一值。这是一个很小的种子范围。
  • @Jesper:这个实现不稳定,可能永远不会完成,因为排序键会为同一个项目计算多次,所以它应该总是返回相同的值。您应该投影到一个临时匿名类型以保存该项目的随机值,对该随机值进行排序,然后再次投影以提取原始项目
【解决方案3】:

根据您的描述,我认为您需要一个由 100 个整数组成的数组,其值从 1 到 100 并且没有重复的数字。如果数字是整数,则不需要生成随机数,因为所有可能的数字都在数组中。因此,只能随机化顺序或数字。

使用 Linq 和 Jesper Palm 的方法 - 使用 Thomas Levesque 的以下语句将为您提供所需的数组。

Random rnd = new Random();
var randomNumbers = Enumerable.Range(1, 100)
                              .Select(x => new { val = x, order = rnd.Next() })
                              .OrderBy(i => i.order)
                              .Select(x => x.val)
                              .ToArray();

该方法甚至非常快,肯定比任何比较操作都更有效。

要向原始发布者解释上述内容,请参阅下面的评论:

  • Enumerable.Range(1, 100) 创建从 1 到 100 的整数范围。
  • .Select(x =&gt; new { val = x, order = rnd.Next() }) 创建一个新的临时对象来保存值和订单位置,这是由一个随机数确定的。
  • .OrderBy(i =&gt; i.order) 按顺序位置对临时对象进行排序。
  • .Select(x =&gt; x.val) 选择临时对象的值,从而转换回 int。
  • .ToArray() 再次将整个事物变回数组。

使用的语法是 .NET 3.5 中可用的 LINQ。对于旧版本,您将不得不自己实现它,这要复杂得多,也需要更长的时间。

按照 Eric 的评论:如果需要改组,您可以按如下方式执行代码

var list = myInputList;
var result = list.Select(x => new { val = x, order = rnd.Next() })
                 .OrderBy(i => i.order)
                 .Select(x => x.val)
                 .ToArray();

【讨论】:

  • 谢谢大家的答案,但请看看我的想法顺便说一句,你如何在那个灰色框中添加代码?是的,我是这里的新手。另外我想指出,我是 c# 的新手,只有几个月的经验。而且我什至不知道你们在这里展示的一些东西,甚至像 enumerable.range 这样的必须是范围。订购者必须订购它。但是我猜代码它将对数组中的所有数字进行排序,这不是我不想达到的概念。我只想获得一个随机数组,其中包含 100 个元素,数字在 1 到 100 之间,以随机顺序排列。
  • @ShallowHeart,我认为你没有抓住重点。代码从 1 到 100 取数字,然后将它们“排序”成随机确定的顺序。这是洗牌的标准技术。
  • @Obalix 您的回答为我节省了很多时间。如果我想得到一个最大范围变化的 30 个数字的数组,你能告诉我我需要做什么吗?我所做的是使用您的 sn-p 生成完整范围的随机数数组,并将所需的前 30 个元素存储在我的数组中。我如何修改此代码以将其合并以减少循环时间。
【解决方案4】:

这是一个简单的实现:

int[] values = new int[100];
Random random = new Random();
for(int i = 0; i < values.Length; i++)
{
    int v;
    do
    {
        v = random.Next(100) + 1;
    } while (Array.IndexOf(values, v) != -1)
    values[i] = v;
}

但这会非常低效,尤其是在数组末尾附近...

更好的解决方案是考虑到这一点,因为您希望以随机顺序从 1 到 100 的 100 个不同值,您的数组最终将包含从 1 到 100 的所有可能值。因此您只需要生成这些值的序列值,然后“洗牌”:

int[] values = Enumerable.Range(1, 100).ToArray();
Random random = new Random();
for(int i = values.Length - 1; i > 0; i--)
{
    int j = random.Next(i + 1);
    int tmp = values[i];
    values[i] = values[j];
    values[j] = tmp;
}

编辑:一种更好的方法,应该适用于不太具体的情况:

T[] RandomCombinationWithNoRepeat<T>(IEnumerable<T> itemsToPickFrom, int numberOfItemsToPick)
{
    // Copy original items to pick from, because we need to modify it
    List<T> itemsCopy = new List<T>(itemsToPickFrom);
    T[] array = new T[numberOfItemsToPick];
    Random random = new Random();
    for(int i = 0; i < numberOfItemsToPick; i++)
    {
        // Pick item and remove it from list
        int index = random.Next(itemsCopy.Count);
        array[i] = itemsCopy[index];
        itemsCopy.RemoveAt(index);
    }
    return array;
}

在你的情况下,你会这样使用它:

int[] result = RandomCombinationWithNoRepeat(Enumerable.Range(1, 100), 100);

【讨论】:

  • 我更多的是在寻找一个 oop 解决方案,更多的是考虑一个像这样的 OOP 编程 [code] public void FillArray(int[]A, int range) { for (int I = 0; I
  • 我不认为List&lt;T&gt; 包含属性Length,您可能想要Count
【解决方案5】:

据我了解。您需要一组带有随机数的整数。我假设使用 int 数组或 int 列表无关紧要。 这是您描述的一个简单的完整方法。
using System; using System.Collections.Generic; using System.Text;

命名空间 FillRandom { 课堂节目 { 静态无效主要(字符串 [] 参数) { int minValue = 1; 整数最大值 = 100; //创建一个容量设置为100的int列表 列表数组 = 新列表(100);

        FillArray(array, minValue, maxValue, array.Capacity);

        //print out all values in the array
        foreach (int i in array)
        {
            Console.WriteLine(i);
        }
    }

    private static void FillArray(List<int> array, int minValue, int maxValue, int capacity)
    {
        int count = 0;
        while (array.Count != capacity - 1)
        {
            Random rnd = new Random();
            int value = rnd.Next(minValue, maxValue);
            if (!array.Contains(value))
            {
                array.Add(value);
            }
            count++;
        }
        //print out the number of times the looping occurs
        Console.WriteLine("count: "+count);
    }        
}

}

您可以创建一个控制台项目并尝试一下。

【讨论】:

  • Eric 完全正确,这确实是一个坏主意,我尝试使用“count”将其打印出来。 ;) 不管怎样,一个有趣的发现是,如果每次都创建一个新的 Random 对象(大约 100k 到 200k 循环),但使用相同的对象,它实际上会更难填充数组。
猜你喜欢
  • 2013-01-06
  • 1970-01-01
  • 1970-01-01
  • 2013-03-13
  • 2018-05-03
  • 1970-01-01
  • 1970-01-01
  • 2019-01-12
相关资源
最近更新 更多