Как сгенерировать случайное число, не включая единицу, без использования цикла while?

Допустим, я хочу сгенерировать случайное число от 1 до 100, но не хочу включать 42. Как мне это сделать, не повторяя случайный метод, пока не будет 42.


person dhruvm    schedule 17.12.2015    source источник


Ответы (2)


Обновлено для Swift 5.1

Исключая 1 значение

var nums = [Int](1...100)
nums.remove(at: 42)

let random = Int(arc4random_uniform(UInt32(nums.count)))
print(nums[random])

Исключение нескольких значений

Это расширение Range обеспечивает решение, когда вы хотите исключить более 1 значения.

extension ClosedRange where Element: Hashable {
    func random(without excluded:[Element]) -> Element {
        let valid = Set(self).subtracting(Set(excluded))
        let random = Int(arc4random_uniform(UInt32(valid.count)))
        return Array(valid)[random]
    }
}

Пример

(1...100).random(without: [40,50,60])

Я полагаю, что вычислительная сложность этого второго решения равна O(n), где n — количество элементов, включенных в диапазон.

Здесь предполагается, что вызывающая сторона предоставляет не более n исключенных значений.

person Luca Angeletti    schedule 17.12.2015
comment
на самом деле ваш намного лучше, чем тот, который вы поразили, потому что ваши более гибкие, чтобы исключить различное количество чисел - если это необходимо. - person holex; 17.12.2015
comment
Это определенно более гибко. Однако он также намного тяжелее, поэтому, если вам просто нужно то, что изначально задавали, мой ответ более эффективен. - person Steven Fisher; 04.02.2016
comment
@StevenFisher: Да, я нахожу ваше решение очень элегантным, и я уже проголосовал за него :) - person Luca Angeletti; 04.02.2016
comment
Спасибо, @appzYourLife. Я возражаю не против вашего ответа. Мне это очень нравится, на самом деле. (Классный пример некоторых вещей Swift, которые я еще не использую.) Просто я смотрел на комментарий выше в течение месяца, пытаясь понять, почему он сбивает мои шорты, и я, наконец, понял это. Здесь есть компромисс, а не просто лучше. :) - person Steven Fisher; 05.02.2016
comment
@StevenFisher: согласен, мой ответ действительно касается более общей проблемы, но на точный вопрос, размещенный здесь, ваш ответ очень быстрый и оптимизированный. На самом деле это требует O(1) времени и O(1) места по сравнению с O(n) временем и O(n) пространством, необходимыми моему. - person Luca Angeletti; 05.02.2016
comment
Я получаю Невозможно использовать мутирующий элемент для неизменного значения: вызов функции возвращает неизменное значение, когда я пытаюсь использовать его с Swift5 - person timetraveler90; 27.12.2019
comment
@timetraveler90 Спасибо за комментарий, посмотрю сегодня позже. - person Luca Angeletti; 27.12.2019
comment
@LucaAngeletti спасибо за действительно быстрый ответ - person timetraveler90; 27.12.2019

В appzYourLife есть несколько отличных решений общего назначения, но я хочу решить конкретную проблему простым способом.

Оба этих подхода работают примерно одинаково: сузьте диапазон до генератора случайных чисел, чтобы удалить невозможный ответ (99 ответов вместо 100), затем сопоставьте результат, чтобы он не был недопустимым значением.

Ни один из подходов не увеличивает вероятность исхода по сравнению с другим исходом. То есть, предполагая, что ваша функция случайных чисел совершенно случайна, результат все равно будет случайным (и, например, нет двукратного шанса 43 относительно 5).

Подход 1: Дополнение.

Получите случайное число от 1 до 99. Если оно больше или равно числу, которого вы хотите избежать, добавьте к нему единицу.

func approach1()->Int {
    var number = Int(arc4random_uniform(99)+1)
    if number >= 42 {
        number = number + 1
    }
    return number
}

Например, пытаясь сгенерировать случайное число от 1 до 5, отличное от 3, возьмите случайное число от 1 до 4 и добавьте единицу, если оно больше или равно 3.

  • rand(1..4) дает 1, +0, = 1
  • rand(1..4) дает 2, +0, = 2
  • rand(1..4) дает 3, +1, = 4
  • rand(1..4) дает 4, +1, = 5

Подход 2: Избегание.

Другой простой способ — получить число от 1 до 99. Если оно точно равно числу, которого вы пытаетесь избежать, вместо этого сделайте его 100.

func approach2()->Int {
    var number = Int(arc4random_uniform(99)+1)
    if number == 42 {
        number = 100
    }
    return number
}

Используя этот алгоритм и снова сузив диапазон до 1-5 (избегая при этом 3), мы получаем следующие возможные результаты:

  • rand(1..4) дает 1; разрешено, поэтому Результат = 1
  • rand(1..4) дает 2, разрешено, поэтому Результат = 2
  • rand(1..4) дает 3; не разрешено, поэтому Результат = 5
  • rand(1..4) дает 4, допустимо, поэтому Result = 4
person Steven Fisher    schedule 17.12.2015
comment
да, но тогда разве у 43 больше шансов выпасть? - person Andreas; 20.12.2015
comment
Нет, это не так. ›=, а не ==. Единственный способ получить результат 43 — это случайный бросок 42. Если случайный бросок равен 43, результат превращается в 44 и так далее, пока 99 не станет 100. - person Steven Fisher; 20.12.2015
comment
Мне нравятся оба ответа здесь. Я кое-чему научился у вас, @appzYourLife, так как я еще не очень-то увлекаюсь Swift. :) - person Steven Fisher; 20.05.2016