Выбирайте из диапазона, но исключайте определенные числа

Можно ли выбрать случайное число из заданного диапазона (1-90), но исключить определенные числа. Исключенные числа динамически создаются, но говорят, что они 3, 8 и 80.

Мне удалось создать генератор случайных чисел, но не смог определить какие-либо функции, которые позволили бы мне выполнить мои требования.

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

Номера, которые должны быть исключены, являются ранее сгенерированными числами. Итак, если случайное число равно единице, это будет добавлено в список исключенных номеров.

Ответ 1

Используя некоторые удобные методы расширения здесь, вы можете создать диапазон чисел и выбирать случайным образом из этой ярости. Например, с помощью этих методов расширения:

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();

Ответ 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

Ответ 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));

Ответ 4

Последнее обновление, которое подразумевает, что каждое значение можно выбрать только один раз, затрудняет задачу.

  • Создайте набор значений в пределах диапазона.
  • Случайно перемешать коллекцию.
  • Чтобы "случайно" выбрать элемент, просто верните первый элемент в коллекции и удалите его из коллекции.

Ответ 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

Убедитесь, что excludedNumbers является HashSet для лучшей производительности.

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;
}

И тестовая функция, чтобы убедиться, что она работает (более 100 тыс. элементов)

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");
}