Generating Random Numbers With Swift

How does one generate a random number in Swift?

Swift 4.2+

Swift 4.2 shipped with Xcode 10 introduces new easy-to-use random functions for many data types.
You can call the random() method on numeric types.

let randomInt = Int.random(in: 0..<6)
let randomDouble = Double.random(in: 2.71828...3.14159)
let randomBool = Bool.random()

Generate list of unique random numbers in Swift from range

There are 2 problems here:

  1. You don't generate enough numbers. You need to keep generating random numbers until your set is large enough:

    func getRandomNumbers(maxNumber: Int, listSize: Int)-> [Int] {
    var randomNumbers = Set<Int>()
    while randomNumbers.count < listSize {
    let randomNumber = Int(arc4random_uniform(UInt32(maxNumber+1)))
    randomNumbers.insert(randomNumber)
    }
    return randomNumbers
    }
  2. You're biasing your random numbers by putting them in the ordering Set chooses, which is highly predictable. You should append your numbers to an array (to keep them in order that they are generated), while still leveraging a set in parallel, for its fast deduplication:

    func getRandomNumbers(maxNumber: Int, listSize: Int)-> [Int] {
    precondition(listSize < maxNumber, "Cannot generate a list of \(listSize) unique numbers, if they all have to be less than \(maxNumber)")

    var randomNumbers = (array: [Int](), set: Set<Int>())

    while randomNumbers.set.count < listSize {
    let randomNumber = Int(arc4random_uniform(UInt32(maxNumber+1)))
    if randomNumbers.set.insert(randomNumber).inserted { // If the number is unique
    randomNumbers.array.append(randomNumber) // then also add it to the arary
    }
    }

    return randomNumbers.array
    }

How to generate different random number in a range in Swift?

A Set is simpler, it guarantees unique values

var randomIndex = Set<Int>()

while randomIndex.count < 3 {
randomIndex.insert(Int.random(in: 1 ... 10))
}

Another approach is to create an array of the values, pick a random element and remove this element from the array

var randomValues = Set<Int>()
var valueArray = Array(1...10)
for _ in 0..<3 {
guard let randomElement = valueArray.randomElement() else { break }
randomValues.insert(randomElement)
valueArray.remove(at: valueArray.firstIndex(of: randomElement)!)
}

Regarding is there any way to optimize my approach the for loop in your code is extremely expensive.

First of all the recommended syntax for an index based loop is

for i in 0 ..< generatedValue.count {

But actually you don't need the index so Fast Enumeration is better

for value in generatedValue {
if randomValue != value {

The most expensive part of the loop is that the entire array is always iterated even if the value was already found. At least add a break statement to exit the loop

for value in generatedValue {
if randomValue != value {
randomIndex.append(randomValue)
c += 1
break
}
}

But there is still a more efficient syntax

if !generatedValue.contains(randomValue) {
randomIndex.append(randomValue)
c += 1
}

Generating random numbers with Swift

let randomIntFrom0To10 = Int.random(in: 1..<10)
let randomFloat = Float.random(in: 0..<1)

// if you want to get a random element in an array
let greetings = ["hey", "hi", "hello", "hola"]
greetings.randomElement()

How to Generate a New Random Number Once Every time Content View Loads?

Approach #1:

MyView is created once, when ContentView renders.

Inside MyView, randomInt is set when the view is first created and then never modified. When you press the button, myMessage is set, but randomInt is never changed. If you wanted randomInt to change, you'd want to say something like:

randomInt = Int.random(in: 1...100)

inside your button action:

Approach #2:

You're creating a new randomInt variable in the local scope by declaring let randomInt = inside the view body.

Instead, if I'm reading your initial question correctly, I think you'd want something using onAppear:

struct MyView: View {

@State var randomInt = 0
@State var myMessage: String = ""

var body: some View {
VStack {
Text(String(randomInt))

Button("Press Me") {
myMessage = String(randomInt)
}

Text(myMessage)
}.onAppear {
randomInt = Int.random(in: 1...100)
}
}
}

You'll see that with this, every time you go back in the navigation hierarchy and then revisit MyView, there's a new value (since it appears again). The button triggering a re-render doesn't re-trigger onAppear

Approach #3:

MyView gets created on the first render of the parent view (ContentView). Unless something triggers a refresh of ContentView, you wouldn't generate a new random number here.


In conclusion, I'm a little unclear on what the initial requirement is (what does it mean for a View to 'load'? Does this just mean when it gets shown on the screen?), but hopefully this describes each scenario and maybe introduces the idea of onAppear, which seems like it might be what you're looking for.


Addendum: if you want the random number to be generated only when ContentView loads (as the title says), I'd create it in ContentView instead of your MyView and pass it as a parameter.


struct ContentView: View {
@State var randomInt = Int.random(in: 1...100)

var body: some View {

NavigationView {

NavigationLink(destination: MyView(randomInt: $randomInt)) {
Text("Link to MyView")
}
}
}
}

struct MyView: View {

@Binding var randomInt : Int
@State var myMessage: String = ""

var body: some View {

Text(String(randomInt))

Button("Press me to refresh only myMessage") {
myMessage = String(randomInt)
}

Button("Press me to change the randomInt as well") {
randomInt = Int.random(in: 1...100)
myMessage = String(randomInt)
}

Text(myMessage)
}

}

Generate Array of Unique Random Numbers within Inclusive Range

You could make your live much easier using a Set to store all random numbers until you reach the expected number of randoms:

func uniqueRandoms(numberOfRandoms: Int, minNum: Int, maxNum: UInt32) -> [Int] {
var uniqueNumbers = Set<Int>()
while uniqueNumbers.count < numberOfRandoms {
uniqueNumbers.insert(Int(arc4random_uniform(maxNum + 1)) + minNum)
}
return uniqueNumbers.shuffled()
}

print(uniqueRandoms(numberOfRandoms: 5, minNum: 0, maxNum: 10))

func uniqueRandoms(numberOfRandoms: Int, minNum: Int, maxNum: UInt32, blackList: Int?) -> [Int] {
var uniqueNumbers = Set<Int>()
while uniqueNumbers.count < numberOfRandoms {
uniqueNumbers.insert(Int(arc4random_uniform(maxNum + 1)) + minNum)
}
if let blackList = blackList {
if uniqueNumbers.contains(blackList) {
while uniqueNumbers.count < numberOfRandoms+1 {
uniqueNumbers.insert(Int(arc4random_uniform(maxNum + 1)) + minNum)
}
uniqueNumbers.remove(blackList)
}
}
return uniqueNumbers.shuffled()
}

uniqueRandoms(numberOfRandoms: 3, minNum: 0, maxNum: 10, blackList: 8) // [0, 10, 7]

how to generate random numbers from 1 to 4 and 8 in swift

Put your sides into an array and use randomElement() to perform the roll:

let sides = [1, 2, 3, 4, 8]
let roll = sides.randomElement()!

if possible I need 4 and 8 numbers to come 20% more often

That means, 4 and 8 should come up 6 times for every time the others come up 5. So put 5 of each of 1-3 in your array, and 6 each of 4 and 8:

let sides = [1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8]
let roll = sides.randomElement()!

Note: It is perfectly safe and appropriate to force unwrap the result of randomElement() with ! here because randomElement() only returns nil when the collection is empty, and we know it’s not.


Also, check out MartinR's randomNumber(probabilities:) function.

You'd use his function like this:

let sides = [1, 2, 3, 4, 8]
let probabilities = [1.0, 1.0, 1.0, 1.2, 1.2]

let rand = randomNumber(probabilities: probabilities)
let roll = sides[rand]

Generate random number of 9 digits in Swift

import Foundation

func random9DigitString() -> String {
let min: UInt32 = 100_000_000
let max: UInt32 = 999_999_999
let i = min + arc4random_uniform(max - min + 1)
return String(i)
}

I'm trying to make basic random numbers..swift

The code appears to be trying to add a random number to an array as a side-effect of when a button is pressed to display the contents of the random number array.

The error message is unclear, but what is happening is the compiler has spotted a state mutation in the code whilst laying out the UI that could cause a race condition.

To get things working, the code needs to separate out UI layout from state update so that they occur in separate cycles.

Here's one example of how that might be done:

import SwiftUI

struct ContentView: View {
@State var randomArray: [Int] = []
var arrayString: String {
if randomArray.count == 0 {
return "No numbers"
} else {
return randomArray.map { "\($0)" }.reduce("") { $0 + " " + $1 }
}
}

var body: some View {
VStack {
Text("My random numbers = \(arrayString)")
Button("New random number") {
randomArray.append(Int.random(in: 1 ... 45))
}
}
}
}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

Good luck and have fun.

PS: If learning Swift, the Hacking With Swift website is an excellent reasource

How to make a list of random numbers that never repeat (consecutively) in swift ui

First, I'd move your numbers into their own model (I'm using NumberModel) so that they can have unique IDs, which will make them more convenient to use in the List/ForEach (especially if they end up changing values over time). This will also allow you to just use ForEach(numbers) and not deal with the indices at all.

You don't need @State variables for previousNumber and randomNumber because they're just used within onAppear. I've refactored your onAppear a bit, but you could definitely go further with it.

struct NumberModel : Identifiable {
var id = UUID()
var number : Int

var stringValue : String {
number.description
}
}

struct ContentView: View {
@State var numbers: [NumberModel] = []
var body: some View {
List{
ForEach(numbers) { i in
Text(i.stringValue)
}
}
.navigationTitle("numbers")
.onAppear() {
var previousNumber : Int? = nil
for _ in 0 ..< 100 {
while true {
let randomNumber = Int.random(in: 0...9)
guard randomNumber != previousNumber else {
continue
}
previousNumber = randomNumber
numbers.append(NumberModel(number: randomNumber))
break
}
}
}
}
}


Related Topics



Leave a reply



Submit