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
What Does an Exclamation Mark Mean in the Swift Language
Waiting Until the Task Finishes
How to Get the Count of a Swift Enum
Xcode 11 & Ios13, Using Uikit Can't Change Background Colour of Uiviewcontroller
Swift Dictionary Get Key For Value
Check If 'Any' Value Is Object
Convert String to Url (Why Is Resulting Variable Nil)
Ios 11 Custom Navbar Goes Under Status Bar
Uitextview With Hyperlink Text
Any Way to Replace Characters on Swift String
Swift - How to Convert String to Double
How to Set Associated Objects in Swift
How to Make a Weak Protocol Reference in 'Pure' Swift (Without @Objc)
Why Can't I Pass a Protocol.Type to a Generic T.Type Parameter
Binary Operator * Cannot Be Applied to Operands of Type Int and Double