簡體   English   中英

如何生成不能為列表中值的隨機INT <int> ?

[英]How to generate random INT that cannot be a value in a List<int>?

我正在嘗試執行以下操作。

假設我有一個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.Next(顯然)給出了1、3、4、8或9。但是我只希望它返回2、5、6、7或10。

有任何想法嗎?

和往常一樣,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()方法。簡單的解決方案是僅生成值,然后檢查包含和拒絕List中已經存在的值,直到獲得不存在的值為止。 另外,使用Set類可能更合適,因為一個集合不能包含兩個相等的元素。

您需要從其他集合中采樣的內容。 假設P是您的預定義的IntsList,而A是{1,2 ... 9}。 您需要創建一個列表, N = AP 您將從這組數字中隨機抽樣。 我想可以更優雅地編寫它,但請參見下面的示例。

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.Next(顯然)給出了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)

有兩種選擇可以起作用:或者嘗試生成直到成功的值,或者如果范圍足夠小,則生成范圍,標記無法選擇的元素,然后將它們排序為最后一個,然后在[ 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());
    }
  }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM