【问题标题】:Select from a range but exclude certain numbers [duplicate]从范围中选择但排除某些数字[重复]
【发布时间】:2013-12-22 12:22:30
【问题描述】:

是否可以从给定范围 (1-90) 中选择一个随机数,但排除某些数字。排除的数字是动态创建的,但假设它们是 3、8 和 80。

我设法创建了随机数生成器,但无法识别任何可以满足我要求的函数。

Random r = new Random();
this.num = r.Next(1, 90);

要排除的数字是先前生成的数字。因此,如果随机数为 1,则会将其添加到排除的数字列表中。

【问题讨论】:

  • 如果你有 K 个不排除的数字,那么在 1 和 K 之间选择一个随机数,然后将该数字映射到实际值。例如,如果您的有效值为 [1,3,5,6],请在 1 和 4 之间选择一个随机值。如果您随机选择 3,那么您的结果为 5,因为它在有效值列表中排名第三。
  • @mbeckish 如果您有不连续的排除范围,则映射是不平凡的,例如[1, 27, 29, 35, 76]
  • @ashes999 - 如果您的范围小到可以放入数组中,这很简单。 OP 没有提供具体细节,这就是为什么我没有深入了解映射的细节。
  • 感谢大家的精彩回答。到目前为止一直非常有用,并且读起来很有趣。很抱歉,但问题有更新。

标签: c# wpf random numbers range


【解决方案1】:

使用一些方便的扩展方法here,您可以创建一系列数字并从中随机选择。例如,使用这些扩展方法:

public static T RandomElement(this IEnumerable<T> enumerable)
{
    return enumerable.RandomElementUsing(new Random());
}

public static T RandomElementUsing(this IEnumerable<T> enumerable, Random rand)
{
    int index = rand.Next(0, enumerable.Count());
    return enumerable.ElementAt(index);
}

您可以将这些应用于过滤后的数字范围:

var random = Enumerable.Range(1, 90).Except(arrayOfRemovedNumbers).RandomElement();

【讨论】:

  • 我认为将 ElementAt 与非直接 IList 源一起使用并不是很有效。
  • @KingKing:你能详细说明一下吗?诚然,我没有测试过它,但 MSDN 文档似乎表明它可以工作。
  • 是的,我同意它工作得很好,我只是想说如果可枚举的源很大,ElementAt 的性能就不好,除非我们在一些直接的 IList 上使用它。不过我觉得 OP 没问题。
  • @KingKing:性能绝对是 OP 想要测试的东西,同意。对于这种情况下的整数列表,我无法想象这有什么大不了的。当然比建议在循环中选择一个新的随机数的答案更好:)
  • 谢谢,每个人都有好主意,但我从这个答案中学到了新东西,太好了,谢谢。
【解决方案2】:

创建一个容器来存放你不想要的数字:

var excludedNumbers = new List<int> { 1, 15, 35, 89 };

然后使用做类似的事情:

Random random = new Random();

int number;

do
{
   number = r.Next(1, 90);
} while (excludedNumbers.Contains(number));

// number is not in the excluded list now

【讨论】:

  • 将赋值放在 while 循环中并不是一个好主意。
  • @ashes999 你是对的。我已经更新了我的答案!
【解决方案3】:

可能不是最佳选择,但您可以使用 while 循环来检查您不想要的数字

Random r = new Random();
this.num = r.Next(1, 90);
do
{
    this.num = r.Next(1, 90);
}  
while (this.num == 3 || this.num == 8 || this.num == 90);

对于很多数字,您可以使用数组或列表并循环遍历它们

int[] exclude = { 3, 8, 90, 11, 24 };
Random r = new Random();
this.num = r.Next(1, 90);
do
{
    this.num = r.Next(1, 90);
}
while (exclude.Contains(this.num));

【讨论】:

  • 我相信他自己可以做到这一点。我猜他是在寻求更优雅的解决方案:)。
  • 如果我们有很多排除的数字,例如大约 50 个数字怎么办?使用 while 循环可能会延迟您的计算,例如,如果我们有 88 个排除的数字,它可能需要循环很多次才能找到未排除的数字。
  • @KingKing 然后只需将它们添加到数组中并检查当前数字是否存在。
  • @nphx 正如我所说的不是最佳选择
  • @nphx 我的评论已更新,这不是唯一的问题。
【解决方案4】:

您的最新更新意味着每个值只能选择一次,这使问题变得简单。

  1. 创建范围内的值集合。
  2. 随机洗牌。
  3. 要“随机”选择一个项目,只需返回集合中的第一个项目,然后将其从集合中删除。

【讨论】:

    【解决方案5】:
    Random r = new Random();
    this.num = r.Next(1, 90);
    
    int excluded[] = new int[] { 3,8,80 }; // list any numbers in this array you want to exclude
    
    for (int i = 0; i < excluded.Length; i++)
    {
        if (this.num == excluded[i])
        {
            this.num = r.Next(1, 90); // or you can try doing something else here
        }
    }
    

    【讨论】:

      【解决方案6】:

      确保excludedNumbersHashSet 以获得最佳性能。

      var random = new Random();
      var exludedNumbers = new HashSet<int>(new int[] { 3, 8, 80});
      var randomNumber = (from n in Enumerable.Range(int.MinValue, int.MaxValue)
                          let number = random.Next(1, 90)
                          where !exludedNumbers.Contains(number)
                          select number).First();
      

      【讨论】:

        【解决方案7】:

        此解决方案在 O(n) 最坏情况下执行此操作,其中 n 是您的排除列表和常量内存。代码稍长一些,但如果您符合以下条件,则可能是相关的:

        • 可能有大量排除项
        • 需要运行多次
        • 范围大

        它保留了随机分布,因为它实际上跳过了排除列表并在不包括集合的范围内生成一个随机数。

        这是实现:

        private static int RandomInRangeExcludingNumbers(Random random, int min, int max, int[] excluded)
        {
            if (excluded.Length == 0) return random.Next(min, max);
        
            //array should be sorted, remove this check if you
            //can make sure, or sort the array before using it
            //to improve performance. Also no duplicates allowed
            //by this implementation
            int previous = excluded[0];
            for (int i = 1; i < excluded.Length; i++)
            {
                if (previous >= excluded[i])
                {
                    throw new ArgumentException("excluded array should be sorted");
                }
            }
        
            //basic error checking, check that (min - max) > excluded.Length
            if (max - min <= excluded.Length)
                throw new ArgumentException("the range should be larger than the list of exclusions");
        
            int output = random.Next(min, max - excluded.Length);
        
        
            int j = 0;
            //set the start index to be the first element that can fall into the range
            while (j < excluded.Length && excluded[j] < min) j++;
        
            //skip each number occurring between min and the randomly generated number
            while (j < excluded.Length && excluded[j] <= output)
            {
                j++;
                output++;
                while (excluded.Contains(output))
                    output++;
            }
        
            return output;
        }
        

        还有一个测试功能,以确保它可以正常工作(超过 100k 个元素)

        private static void Test()
        {
            Random random = new Random();
            int[] excluded = new[] { 3, 7, 80 };
            int min = 1, max = 90;
        
            for (int i = 0; i < 100000; i++)
            {
                int randomValue = RandomInRangeExcludingNumbers(random, min, max, excluded);
        
                if (randomValue < min || randomValue >= max || excluded.Contains(randomValue))
                {
                    Console.WriteLine("Error! {0}", randomValue);
                }
            }
            Console.WriteLine("Done");
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2023-03-12
          • 1970-01-01
          • 1970-01-01
          • 2018-02-02
          • 1970-01-01
          • 2015-03-03
          • 2011-12-21
          • 1970-01-01
          相关资源
          最近更新 更多