【问题标题】:Exclude values from Random.Range()?从 Random.Range() 中排除值?
【发布时间】:2016-10-19 03:08:53
【问题描述】:

如果您使用Random.Range() 生成值,有什么方法可以排除该范围内的某些值(例如:选择1 到20 之间的数字,而不是6 到8 之间的数字)?

【问题讨论】:

  • 您可以编写自己的方法来检查生成的数字是否在排除范围内,在这种情况下重新生成它。
  • 我加了random标签,是为了把这个问题介绍给本站众多随机数专家,让我的回答有点可信度。

标签: c# random unity3d


【解决方案1】:

最好的方法是使用你最喜欢的生成器生成一个介于 1 和 17 之间的整数 n,然后使用转换

if (n > 5){
    n += 3;
}

如果您在 1 到 20 之间进行抽样,然后丢弃值,您可能会引入统计异常,特别是对于低差异序列。

【讨论】:

    【解决方案2】:

    所以你实际上想要17 (20 - 3) 不同的值

      [1..5] U [9..20]
    

    你可以实现这样的东西:

      // Simplest, not thread-safe
      private static Random random = new Random();
    
      ...  
    
      int r = (r = random.Next(1, 17)) > 5
        ? r + 3
        : r;
    

    在一般(和复杂)的情况下,我建议生成一个包含所有可能值的数组,然后取 来自它的项目:

      int[] values = Enumerable
        .Range(1, 100) // [1..100], but
        .Where(item => item % 2 == 1) // Odd values only
        .Where(item => !(item >= 5 && item <= 15)) // with [5..15] range excluded
        //TODO: Add as many conditions via .Where(item => ...) as you want
        .ToArray();
    
      ...
    
      int r = values[random.Next(values.Length)];
    

    【讨论】:

    • 加一,这在统计上是正确的;我相信条件测试中对您在赋值左侧定义的变量的赋值在 C# 中是明确定义的!
    • Unity3D 提供了一个 Random 类,大多数用户都依赖它。此外,由于内部原因(iOS 上的 FullAOT),在 Unity 中不推荐 Linq,这使得它的某些功能并不总是有效。
    【解决方案3】:

    是的,您可以在 LINQ 中简单地使用 where 语句

       var list = Enumerable.Range(1, 20).Where(a => a < 6 || a > 8).ToArray();
    

    没有 LINQ 的其他方式

            public IEnumerable RangeBetween()
            {
                foreach (var i in Enumerable.Range(1, 20))
                {
                    if (i < 6 || i > 8)
                    {
                        yield return i;
                    }
                }
            }
    

    编辑:现在我看到不是一个严格的 C# 问题。它影响UnityRandom。但是为了获得完整的答案,我建议您使用上面的代码和Enumerable.Range,然后使用它来生成数字:

    list[Random.Next(list.Length)];
    

    【讨论】:

    • 这是用于游戏引擎的。如果您可以避免使用 linq,请避免使用它。它分配内存,并且某些功能在 iOS 中无法使用。
    • @Programmer 也避免使用 lambda 表达式?
    • 我不会这么说的。我的意思是如果他大部分时间都调用那个代码,他应该避免它。
    • 如果您使用列表而不是数组,@Programmer LINQ 在 iOS 上可以正常工作。它是抽象循环和列表的复杂性的重要资产。我认为它仅适用于 iOS 上的列表的原因是 LINQ 不在列表上使用 IEnumerable(在 iOS 上存在问题),而在数组上却使用了。很有趣。
    • @MyIsaak 是的,它适用于 List。由于 iOS 上没有 AOT 功能,它无法正常工作。虽然我只在必要时才使用它的一些linq函数。尽管如此,他答案中的第一个代码仍然会分配内存只是为了生成随机数......不值得牺牲。
    【解决方案4】:

    另一种方法是制作一个包含有效返回值的数组,然后随机选择一个:

    void Main()
    {
        var rng = new Random();
        var validValues = Enumerable.Range(1, 20).Except(new int[] {6, 7, 8}).ToArray();
    
        for (int i = 0; i < 25; i++)
        {
            Console.Write(validValues[rng.Next(0, validValues.Length)]);
            Console.Write(" ");
        }
    }
    

    编辑:哎呀!刚刚注意到这是针对 Unity3D 的,所以这个例子可能不合适。不过,它使用标准的 Random 类工作。

    【讨论】:

      【解决方案5】:

      如果 r 在 6-8 范围内,这将选择一个不同的随机数。

      int r = 0;
      do
      {
          r = Random.Next(20);
      } while (r >= 6 && r <= 8)
      

      【讨论】:

      • 我认为使用这种方法有机会(非常小,但仍然)程序进入无限循环......如果你总是得到一个介于 6 和 8 之间的数字。跨度>
      • 我想评论这个。这是冻结游戏的好方法。 @Ignacio 你是对的。这以前发生在我身上。在 Unity 中生成随机数时没有 while 循环。
      • @Programmer,不只是在Unity中,它可以冻结任何程序
      • @Ignacio 很高兴知道这一点。
      • 呃,它真的不能冻结程序。计算机依赖于能够生成随机数并在任何地方使用随机过程 - 互联网及其协议等事物依赖于此 - 数据包有可能会丢弃并且永远不会到达,但从统计上讲这永远不会发生。如果你从宇宙开始到现在在计算机上运行这个程序,它就不会被卡住 - 很容易证明在统一概率(或任何非零概率的非零概率的数字不介于 6 和 8 之间)出现)它可能不会被卡住超过一秒钟。
      【解决方案6】:

      我将在这个主题上投入 2 美分。这是一个完整的类,展示了一种生成随机数的方法,不包括您选择的数字以及如何使用它。

      using UnityEngine;
      using System.Collections.Generic;
      using System.Linq;
      
      public class test2 : MonoBehaviour {
      
          System.Random rand;
          int[] excludelist;
          void Start()
          {
               rand = new System.Random();
              excludelist = new int[] { 5,9,3};
              for(int i = 0; i<20;i++)
              {
                  Debug.Log(MakeMeNumber(excludelist));
              }
      
          }
          private int MakeMeNumber(params int[] excludeList)
          {
              var excluding = new HashSet<int>(excludeList);
              var range = Enumerable.Range(1, 20).Where(i => !excluding.Contains(i));
      
      
              int index = rand.Next(0, 20 - excluding.Count);
              return range.ElementAt(index);
          }
      }
      

      【讨论】:

        【解决方案7】:

        我创建了一个专为该案例设计的资产(已付费)。

        它只包含一些有用的函数,可以根据给定的间隔(例如,在 X 和 Y 之间,不包括 Z)生成随机数。

        第三个函数 RandomExclude.RandomInt (int, int, Vector2) 将涵盖您的示例(1 到 20,但不包括 6 到 8)。第一个 int 是可能的最低值,第二个 int 是最高值,Vector2 是我们排除的区间。在您的情况下,它看起来像这样:

        int ourOutcome = RandomExclude.RandomInt (1, 20, new Vector2 (6, 8));

        资产包含更多类似的功能,它们让您生成一个不包括一个数字、几个数字或几个区间的 int。

        资产本身已经涵盖了所有详细的实施说明,所以不用担心。

        https://assetstore.unity.com/packages/tools/input-management/random-exclude-133786

        请随意尝试!

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2018-10-11
          • 2015-01-23
          • 2021-12-30
          • 2019-03-09
          • 2018-08-29
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多