Рандомизация по диапазону чисел

Я пытаюсь заставить эту часть приложения работать, когда пользователь нажимает кнопку, а метка печатает случайно сгенерированное число от 1 до 12. Мне удалось это сделать, но я также хочу, чтобы не повторялись уже напечатанные случайные числа.

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

Я заставил его работать на игровой площадке, но не могу заставить его работать с реальным проектом.

Вот код моего проекта.

    var usedNumbers = [Int]()
    var randomConv = 0

func randomize() {
    lblRandom.text = "\(arc4random_uniform(12) + 1)"
    randomConv = Int(lblRandom.text!)!
}

@IBAction func btnRandomPressed(sender: AnyObject) {
    randomize()
    if usedNumbers.contains(randomConv) {       
        randomize()

    } else {    
        usedNumbers.append(randomConv)

    }

    if usedNumbers.count == 12 {
        btnRandom.hidden = true
    } 
}

А вот код с моей игровой площадки.

var lblRandom = "\(arc4random_uniform(12) + 1)"
var randomConv = 0
var usedNumbers = [Int]()

func randomize() {
lblRandom = "\(arc4random_uniform(12) + 1)"
randomConv = Int(lblRandom)!
}

repeat {
randomize()

if usedNumbers.contains(randomConv) {
    randomize()   
} else {
    usedNumbers.append(randomConv)
    print(lblRandom)
}

} while usedNumbers.count < 12

person Mitchell Ludbey    schedule 05.01.2016    source источник


Ответы (3)


Я не совсем уверен, почему ваш код не работает, но попробуйте этот код, вместо этого он работает.

var usedNumbers = [Int]()
var randomConv = 0

@IBAction func btnRandomPressed(sender: AnyObject) {
     randomConv = Int(arc4random_uniform(12) + 1)

        if usedNumbers.contains(randomConv) {
            // If you find a duplicate you fire the event again and a new number will be randomized
            print("Exists \(randomConv)")
            btn_Pressed.sendActionsForControlEvents(.TouchUpInside)

        } else {
            print(randomConv)
            lblTest.text = String(randomConv)
            usedNumbers.append(randomConv)

       }
}

Обновить
Когда условие usedNumbers.contains(randomConv) равно true, вы можете использовать эту строку для повторного запуска события кнопки: btn_Pressed.sendActionsForControlEvents(.TouchUpInside) — btn_Pressed — это выход ваших кнопок из раскадровки.

Я обновил блок кода, чтобы вы могли увидеть полностью работающий пример.

Обновление 2. Альтернативное решение

func randomize(){
    repeat {
        if (usedNumbers.count == 12){
            return
        }
        randomConv = Int(arc4random_uniform(12) + 1)
    } while usedNumbers.contains(randomConv)

    usedNumbers.append(randomConv)
    lblTest.text = String(randomConv)
}

@IBAction func btnRandomPressed(sender: AnyObject) {
     randomize()
}
person Rashwan L    schedule 05.01.2016
comment
Только что попробовал это, и это очень близко к тому, чего я пытаюсь достичь! Единственная проблема заключается в том, что если случайное число уже напечатано, в приложении ничего не меняется, и пользователю нужно щелкнуть несколько раз, чтобы получить новое число. Есть ли способ гарантировать новый номер при каждом нажатии кнопки? - person Mitchell Ludbey; 06.01.2016
comment
Да есть, проверьте обновление. Вы можете снова программно запустить событие кнопки @MitchellLudbey, таким образом, вы можете нажать оператор if и снова запустить событие. - person Rashwan L; 06.01.2016
comment
Я не фанат этого решения. Вы в основном создали рекурсивную функцию со случайным условием остановки. - person Sebastian Osiński; 06.01.2016
comment
@SebastianOsiński, я добавил еще одно решение, которое не выполняет рекурсивную функцию. Спасибо за ответ. - person Rashwan L; 06.01.2016
comment
@RashwanL Я согласен, что ваше второе решение чище, но, как и предыдущее, оно неэффективно. С каждым новым успешно нарисованным номером ожидаемое количество проходов цикла увеличивается. В худшем случае ваши оба алгоритма требуют бесконечного времени для завершения. - person Sebastian Osiński; 06.01.2016
comment
Да, конечно, но, поскольку у ОП был вопрос, у него было только 12 номеров. Если бы у него было большее число, я бы, конечно, предложил другое решение, и я думаю, что он знает об этом. - person Rashwan L; 06.01.2016

В системе уже есть инструмент для этого: GKShuffledDistribution — одна из многих утилит рандомизации в GameplayKit. Единственное, что он делает в отличие от других классов GKRandom, это именно то, что вы просите — он следит за тем, чтобы не повторялись значения, которые уже были «вытянуты» из его случайного пула.

Вот пример:

import GameplayKit
let shuffle = GKShuffledDistribution(forDieWithSideCount: 12)
for _ in 1...100 { print(shuffle.nextInt()) }

Используйте игровую площадку для графического отображения результатов, и вы заметите, что ни одно число никогда не повторяется сразу (на графике нет горизонтальных линий), а также что ни одно число не повторяется более одного раза за каждые 12 «бросков».

Если вы хотите реализовать подобное поведение самостоятельно, вы можете улучшить свои другие попытки (и другие ответы до сих пор), используя что-то вроде Set или Dictionary<Int, Bool> для хранения уже используемых или еще неиспользованных значений, поскольку они могут реагировать на contains без поиска всю структуру данных.

person rickster    schedule 06.01.2016

Другое решение:

var availableNumbers = Array(1...12)
var randomConv = 0    

@IBAction func btnRandomPressed(sender: AnyObject) {
    if availableNumbers.count > 0 {
        let randIndex = Int(arc4random_uniform(UInt32(availableNumbers.count)))
        randomConv = availableNumbers.removeAtIndex(randIndex)

        someLabel.text = "\(randomConv)"
    }
}

Вы начинаете свою программу с массива доступных чисел для рисования. Затем вы выбираете элемент массива случайным образом, рисуя случайный индекс и удаляя элемент с указанным индексом из массива. Он автоматически изменяет размер вашего массива, и вы уверены, что больше не будете рисовать тот же номер.

person Sebastian Osiński    schedule 05.01.2016