如何生成不能成为 List 中的值的随机 INT

本文关键字:随机 INT int 不能 何生成 能成为 List | 更新日期: 2023-09-27 18:34:47

>我正在尝试执行以下操作。

假设我有一个 List,我想在特定范围内生成一个新的 int,但该值不能已在 List 中定义。

List<int> PredefinedIntsList = new List<int>() { 1, 3, 4, 8, 9 };
Random rnd = new Random();
int NewRandomValue = rnd.Next(0, 10);

RND.接下来(显然(想出 1、3、4、8 或 9。但我只希望它返回 2、5、6、7 或 10。

有什么想法吗?

如何生成不能成为 List<int> 中的值的随机 INT

与往常一样,LINQ 是您的朋友:

[TestMethod]
public void RandomTest()
{
    var except = new[] {1, 2, 3, 5};
    GetRandomExcept(1, 5, except).Should().Be(4);
}
private static int GetRandomExcept(int minValue, int maxValue, IEnumerable<int> except)
{
    return GetRandoms(minValue, maxValue).Except(except).First();
}
private static IEnumerable<int> GetRandoms(int minValue, int maxValue)
{
    var random = new Random();
    while (true) yield return random.Next(minValue, maxValue);
} 

请记住,您永远不应该调用GetRandoms().ToArray().Max().OrderBy()等等,因为您将获得无限循环并永远生成随机。

不过,您可以做的是调用GetRandoms().Take(10).ToArray()以获取数组中接下来的 10 个随机整数。

尝试检查是否可以使用 List 类的 contains(( 方法...简单的解决方案是仅生成值并检查包含并拒绝已在列表中的值,直到获得不在列表中的值。此外,使用 Set 类可能更合适,因为集合不能包含两个相等的元素。

你需要

做什么 从其他集合中采样。假设 P 是预定义的 IntsList,A 是 {1,2...9}。你需要创建一个列表,N = A-P。您将从这组数字中随机抽样。 我想它可以写得更优雅,但请参阅下面的示例。

class Program
{
    static void Main(string[] args)
    {
        List<int> predefinedIntsList = new List<int>() { 1, 3, 4, 8, 9 };
        Random rnd = new Random();
        List<int> newIntsList = new List<int>();
        int upperBound = 10;
        for (int i = 0; i < upperBound; i++)
        {
            if (!predefinedIntsList.Contains(i))
            {
                newIntsList.Add(i);
            }
        }
        for (int i = 0; i < 20; i++)
        {
            int newRandomValueIndex = rnd.Next(0, newIntsList.Count);
            int newRandomValue = newIntsList[newRandomValueIndex];
            Console.WriteLine(newRandomValue);
        }
    }
}

输出

2
0
6
7
5
5
5
7
0
7
6
6
5
5
5
0
6
7
0
7

RND.接下来(显然(想出 1、3、4、8 或 9。但我只想要它 返回 2、5、6、7 或 10。

显然不是,它将返回 0、1、2、3、4、5、6、7、8 或 9 :P的值。如果您希望包含 10 而不是 0,则需要.Next(1, 11)

有两种选择可以工作:要么尝试生成值,直到成功,要么如果范围足够小,则生成范围,标记无法拾取的elemens,并将它们排序到最后一个,然后在[0..possibleToPickElementsCount]之间随机选择。

第一种方法如下所示:

public static class RandomExtensions
{
    public static int Next(this Random random, 
        int minInclusive, 
        int maxExclusive, 
        IList<int> values)
    {
        // this will crash if values contains
        // duplicate values.
        var dic = values.ToDictionary(val => val);
        // this can go into forever loop,
        // think about this a bit.
        for(;;){
            var randomNumber= random.Next(minInclusive, maxExclusive);
            if(!dic.ContainsKey(randomNumber))
                return randomNumber;
        }
    }
}

第二种方法是这样的,但它只是给你一个想法:

public static class RandomExtensions
{
    class NormalizedPair
    {
        public int Value {get;set;}
        public PairStatus Status {get;set;}
        public NormalizedPair(int value){
            Value = value;
        }
        public enum PairStatus {
            Free,
            NotFree
        }
    }
    private static Random _internalRandom = new Random();
    public static int Next(this Random random, 
        int minInclusive, 
        int maxExclusive, 
        IList<int> values)
    {
        var elements = maxExclusive - minInclusive;
        var normalizedArr  = new NormalizedPair[elements];
        var normalizedMinInclusive = 0;
        var normalizedMaxExclusive = maxExclusive - minInclusive;
        var normalizedValues = values
                .Select(x => x - minInclusive)
                .ToList();
        for(var j = 0; j < elements; j++)
        {
            normalizedArr[j] = new NormalizedPair(j){
                Status = NormalizedPair.PairStatus.Free
            };
        }
        foreach(var val in normalizedValues)
            normalizedArr[val].Status = NormalizedPair.PairStatus.NotFree;
        return  normalizedArr
                    .Where(y => y.Status == NormalizedPair.PairStatus.Free) // take only free elements
                    .OrderBy(y => _internalRandom.Next()) // shuffle the free elements
                    .Select(y => y.Value + minInclusive) // project correct values
                    .First(); // pick first.
    }
}

或者,如果您是套装的粉丝:

public static int Next2(this Random random, 
    int minInclusive, 
    int maxExclusive, 
    IList<int> values)
{
    var rangeSet  = new HashSet<int>(
                        Enumerable.Range(
                            minInclusive,
                            maxExclusive - minInclusive));
    // remove gibberish                                 
    rangeSet.ExceptWith(new HashSet<int>(values));

    // this can be swapped out with
    // yates shuffle algorithm
    return rangeSet.OrderBy(x => _internalRandom.Next())
                    .First();
}

与其编写逻辑来确定是否已选择随机数,我更喜欢以随机顺序生成包含项目的第二个列表。

这在 LINQ 中很容易做到

var originalList = new List<int>() { 1, 3, 4, 8, 9 };
Random rnd = new Random();
var secondList = originalList.OrderBy(x=>rnd.NextDouble());

rnd.NextDouble 的调用返回一个随机种子,OrderBy 方法使用该种子对每个 int 值进行排序。

当我需要多次运行随机项目时,每次浏览列表时都会使用新的排序顺序,我将使用Queue来存储项目。 然后根据需要取消项目排队。 当队列为空时,是时候重新填充了。

private Queue<int> _randomizedItems;
  private void  RandomTest()
  {
    var originalList = new List<int>() { 1, 3, 4, 8, 9 };
    Random rnd = new Random();
    var temp = originalList.OrderBy(r=>rnd.NextDouble());
    _randomizedItems = new Queue<int>(temp);
    while (_randomizedItems.Count >0) 
    {
      MessageBox.Show(_randomizedItems.Dequeue().ToString());
    }
  }