How to Generate a Random Number in Swift Without Repeating the Previous Random Number

How to generate a random number in Swift without repeating the previous random number?

Store the previous generated number in a variable and compare the generated number to the previous number. If they match generate a new random number. Repeat the generation of new numbers until they don't match.

var previousNumber: UInt32? // used in randomNumber() 

func randomNumber() -> UInt32 {
var randomNumber = arc4random_uniform(10)
while previousNumber == randomNumber {
randomNumber = arc4random_uniform(10)
}
previousNumber = randomNumber
return randomNumber
}

Random number without repeat in swiftUI

This approach uses an extra observable class. It copies the 6 numbers into a temporary array. On tap one random number is removed from the array (this number is being displayed) so each number is only used once. If the array is empty the cycle starts over.

class Random : ObservableObject {
var array = [Int]()

@Published var randomNumber = 0

func next() {
if array.isEmpty { array = [1,2,3,4,5,6] }
let index = Int.random(in: 0..<array.count)
randomNumber = array.remove(at: index)
}
}

struct RanNum: View {

@StateObject private var random = Random()

var body: some View {
VStack{

Text(random.randomNumber == 0 ? "" : "\(random.randomNumber)")
.padding()
.multilineTextAlignment(.center)
.background(Color.white.opacity(0.5))
.cornerRadius(10)
.frame(width: 390, alignment: .center)

Button(action: {
random.next()
}) {
Image(systemName: "arrow.counterclockwise.circle.fill")
.foregroundColor(.black)
.font(.system(size: 30))
.padding()
}
}
}
}

How to generate random number without repeating in Swift

You can store last random number or last fact in a variable and check it in your randomFact function. Like this:

var lastRandomNumber = -1

func randomFact() -> String {
let randomNumber = GKRandomSource.sharedRandom().nextInt(upperBound: facts.count)

if randomNumber == lastRandomNumber {
return randomFact()
} else {
lastRandomNumber = randomNumber
return facts[randomNumber]
}
}

How to randomly generate numbers that don't repeat?

How does one generate a random number in Apple's Swift language?

check this answer, you can then write an easy function to check your array or data structure for duplicate numbers.

How do I generate a random number not including one without using a while loop?

Updated for Swift 5.1

Excluding 1 value

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

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

Excluding multiple values

This extension of Range does provide a solution when you want to exclude more than 1 value.

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

Example

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

I believe the computation complexity of this second solution is O(n) where n is the number of elements included in the range.

The assumption here is the no more than n excluded values are provided by the caller.

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

How to generate random numbers without repetition in Swift?

You can create a function that returns a random-number generating closure, like this:

func randomSequenceGenerator(min: Int, max: Int) -> () -> Int {
var numbers: [Int] = []
return {
if numbers.isEmpty {
numbers = Array(min ... max)
}

let index = Int(arc4random_uniform(UInt32(numbers.count)))
return numbers.remove(at: index)
}
}

To use it, first call the generator once to create the random sequence generator, then call the return value as many times as you like. This will print out random values between one and six, cycling through all of them before starting over:

let getRandom = randomSequenceGenerator(min: 1, max: 6)
for _ in 1...20 {
print(getRandom())
}

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

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
}


Related Topics



Leave a reply



Submit