Generate a Swift array of nonrepeating random numbers - ios

I'd like to generate multiple different random numbers in Swift. Here is the procedure.
Set up an empty array
Generate a random number
Check if the array is empty
a. If the array is empty, insert the random number
b. If the array is not empty, compare the random number to the numbers in array
i. If the numbers are the same, repeat 2
ii. if the numbers are not the same, insert the random number and repeat 2
import UIKit
//the random number generator
func randomInt(min: Int, max:Int) -> Int {
return min + Int(arc4random_uniform(UInt32(max - min + 1)))
}
var temp = [Int]()
for var i = 0; i<4; i++ {
var randomNumber = randomInt(1, 5)
if temp.isEmpty{
temp.append(randomNumber)
} else {
//I don't know how to continue...
}
}

If you use your method the problem is, that you will create a new random-number each time. So you possibly could have the same random-number 4 times and so your array will only have one element.
So, if you just want to have an array of numbers from within a specific range of numbers (for example 0-100), in a random order, you can first fill an array with numbers in 'normal' order. For example with for loop etc:
var min = 1
var max = 5
for var i = min; i<= max; i++ {
temp.append(i)
}
After that, you can use a shuffle method to shuffle all elements of the array with the shuffle method from this answer:
func shuffle<C: MutableCollectionType where C.Index == Int>(var list: C) -> C {
let count = countElements(list)
for i in 0..<(count - 1) {
let j = Int(arc4random_uniform(UInt32(count - i))) + i
swap(&list[i], &list[j])
}
return list
}
Ater that you can do something like that:
shuffle(temp) // e.g., [3, 1, 2, 4, 5]

The construct you’re looking for with your approach might be something like:
var temp: [Int] = []
while temp.count < 4 {
var randomNumber: Int
do {
randomNumber = randomInt(1, 5)
} while contains(temp, randomNumber)
temp.append(randomNumber)
}
This will be fine for tiny ranges like yours, but for larger ranges it will be very slow, because for the last few numbers you are waiting for the random number to hit precisely the remaining handful of possibilities. I just tried generating from a range of 200 in a playground and it took 9 seconds.
If you want a random selection of numbers with guaranteed coverage over a range, you could generate it like by taking that range and shuffling it, like this:
func shuffle<S: SequenceType>(source: S) -> [S.Generator.Element] {
var rangen = GeneratorOf { arc4random() }
let a = Array(Zip2(rangen, source))
return a.sorted { $0.0 < $1.0 }.map { $0.1 }
}
let min = 1, max = 5
shuffle(min...max)
If you want a selection of n non-repeating random numbers from a range 0..<m, there’s a particularly pretty algorithm to do this that generates an ascending sequence of random numbers from that range:
func randomGeneratorOf(#n: Int, #from: Int) -> GeneratorOf<Int> {
var select = UInt32(n)
var remaining = UInt32(from)
var i = 0
return GeneratorOf {
while i < from {
if arc4random_uniform(remaining) < select {
--select
--remaining
return i++
}
else {
--remaining
++i
}
}
return nil
}
}
Which you could use like so:
let engines = [
"Duck","Emily","Gordon","Henry", "Mavis",
"Belle","James","Edward","Thomas","Toby"
]
let picks = Array(randomGeneratorOf(n: 3, from: engines.count))
for engine in PermutationGenerator(elements: engines, indices: picks) {
println(engine)
}

Below is my suggestion.
I like this way since it is short and simple :)
let totalCount: Int = 150 //Any number you asssign
var randomNumArray: [Int] = []
var i = 0
while randomNumArray.count < totalCount {
i++
let rand = Int(arc4random_uniform(UInt32(totalCount)))
for(var ii = 0; ii < totalCount; ii++){
if randomNumArray.contains(rand){
print("do nothing")
} else {
randomNumArray.append(rand)
}
}
}

Related

Integers Larger than Int64

I'm attempting to get a user input number and find the sum of all the digits. I'm having issues with larger numbers, however, as they won't register under an Int64. Any idea as to what structures I could use to store the value? (I tried UInt64 and that didn't work very well with negatives, however, I'd prefer something larger than UInt64, anyways. I'm having a hard time implementing a UInt128 from Is there a number type with bigger capacity than u_long/UInt64 in Swift?)
import Foundation
func getInteger() -> Int64 {
var value:Int64 = 0
while true {
//we aren't doing anything with input, so we make it a constant
let input = readLine()
//ensure its not nil
if let unwrappedInput = input {
if let unwrappedInt = Int64(unwrappedInput) {
value = unwrappedInt
break
}
}
else { print("You entered a nil. Try again:") }
}
return value
}
print("Please enter an integer")
// Gets user input
var input = getInteger()
var arr = [Int] ()
var sum = 0
var negative = false
// If input is less than 0, makes it positive
if input < 0 {
input = (input * -1)
negative = true
}
if (input < 10) && (input >= 1) && (negative == true) {
var remain = (-1)*(input%10)
arr.append(Int(remain))
input = (input/10)
}
else {
var remain = (input%10)
arr.append(Int(remain))
input = (input/10)
}
}
// Adds numbers in array to find sum of digits
var i:Int = 0
var size:Int = (arr.count - 1)
while i<=size {
sum = sum + arr[i]
i = (i+1)
}
// Prints sum
print("\(sum)")
You can use a string to perform the operation you describe. Loop through each character and convert it to an integer and add to the sum. Be careful to handle errors.

Getting "Argument passed to call that takes no arguments" when trying to use arc4random [duplicate]

I need to generate a random number.
It appears the arc4random function no longer exists as well as the arc4random_uniform function.
The options I have are arc4random_stir(), arc4random_buf(UnsafeMutablePointer<Void>, Int), and arc4random_addrandom(UnsafeMutablePointer<UInt8>, Int32).
I can't find any docs on the functions and no comments in the header files give hints.
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()
You could try as well:
let diceRoll = Int(arc4random_uniform(UInt32(6)))
I had to add "UInt32" to make it work.
Just call this function and provide minimum and maximum range of number and you will get a random number.
eg.like randomNumber(MIN: 0, MAX: 10) and You will get number between 0 to 9.
func randomNumber(MIN: Int, MAX: Int)-> Int{
return Int(arc4random_uniform(UInt32(MAX-MIN)) + UInt32(MIN));
}
Note:- You will always get output an Integer number.
After some investigation I wrote this:
import Foundation
struct Math {
private static var seeded = false
static func randomFractional() -> CGFloat {
if !Math.seeded {
let time = Int(NSDate().timeIntervalSinceReferenceDate)
srand48(time)
Math.seeded = true
}
return CGFloat(drand48())
}
}
Now you can just do Math.randomFraction() to get random numbers [0..1[ without having to remember seeding first. Hope this helps someone :o)
Update with swift 4.2 :
let randomInt = Int.random(in: 1..<5)
let randomFloat = Float.random(in: 1..<10)
let randomDouble = Double.random(in: 1...100)
let randomCGFloat = CGFloat.random(in: 1...1000)
Another option is to use the xorshift128plus algorithm:
func xorshift128plus(seed0 : UInt64, _ seed1 : UInt64) -> () -> UInt64 {
var state0 : UInt64 = seed0
var state1 : UInt64 = seed1
if state0 == 0 && state1 == 0 {
state0 = 1 // both state variables cannot be 0
}
func rand() -> UInt64 {
var s1 : UInt64 = state0
let s0 : UInt64 = state1
state0 = s0
s1 ^= s1 << 23
s1 ^= s1 >> 17
s1 ^= s0
s1 ^= s0 >> 26
state1 = s1
return UInt64.addWithOverflow(state0, state1).0
}
return rand
}
This algorithm has a period of 2^128 - 1 and passes all the tests of the BigCrush test suite. Note that while this is a high-quality pseudo-random number generator with a long period, it is not a cryptographically secure random number generator.
You could seed it from the current time or any other random source of entropy. For example, if you had a function called urand64() that read a UInt64 from /dev/urandom, you could use it like this:
let rand = xorshift128plus(urand64(), urand64())
for _ in 1...10 {
print(rand())
}
let MAX : UInt32 = 9
let MIN : UInt32 = 1
func randomNumber()
{
var random_number = Int(arc4random_uniform(MAX) + MIN)
print ("random = ", random_number);
}
In Swift 3 :
It will generate random number between 0 to limit
let limit : UInt32 = 6
print("Random Number : \(arc4random_uniform(limit))")
My implementation as an Int extension. Will generate random numbers in range from..<to
public extension Int {
static func random(from: Int, to: Int) -> Int {
guard to > from else {
assertionFailure("Can not generate negative random numbers")
return 0
}
return Int(arc4random_uniform(UInt32(to - from)) + UInt32(from))
}
}
This is how I get a random number between 2 int's!
func randomNumber(MIN: Int, MAX: Int)-> Int{
var list : [Int] = []
for i in MIN...MAX {
list.append(i)
}
return list[Int(arc4random_uniform(UInt32(list.count)))]
}
usage:
print("My Random Number is: \(randomNumber(MIN:-10,MAX:10))")
Another option is to use GKMersenneTwisterRandomSource from GameKit. The docs say:
A deterministic pseudo-random source that generates random numbers
based on a mersenne twister algorithm. This is a deterministic random
source suitable for creating reliable gameplay mechanics. It is
slightly slower than an Arc4 source, but more random, in that it has a
longer period until repeating sequences. While deterministic, this is
not a cryptographic random source. It is however suitable for
obfuscation of gameplay data.
import GameKit
let minValue = 0
let maxValue = 100
var randomDistribution: GKRandomDistribution?
let randomSource = GKMersenneTwisterRandomSource()
randomDistribution = GKRandomDistribution(randomSource: randomSource, lowestValue: minValue, highestValue: maxValue)
let number = randomDistribution?.nextInt() ?? 0
print(number)
Example taken from Apple's sample code: https://github.com/carekit-apple/CareKit/blob/master/CareKitPrototypingTool/OCKPrototyper/CareKitPatient/RandomNumberGeneratorHelper.swift
I'm late to the party 🤩🎉
Using a function that allows you to change the size of the array and the range selection on the fly is the most versatile method. You can also use map so it's very concise. I use it in all of my performance testing/bench marking.
elements is the number of items in the array
only including numbers from 0...max
func randArr(_ elements: Int, _ max: Int) -> [Int] {
return (0..<elements).map{ _ in Int.random(in: 0...max) }
}
Code Sense / Placeholders look like this.
randArr(elements: Int, max: Int)
10 elements in my array ranging from 0 to 1000.
randArr(10, 1000) // [554, 8, 54, 87, 10, 33, 349, 888, 2, 77]
you can use this in specific rate:
let die = [1, 2, 3, 4, 5, 6]
let firstRoll = die[Int(arc4random_uniform(UInt32(die.count)))]
let secondRoll = die[Int(arc4random_uniform(UInt32(die.count)))]
Lets Code with Swift for the random number or random string :)
let quotes: NSArray = ["R", "A", "N", "D", "O", "M"]
let randomNumber = arc4random_uniform(UInt32(quotes.count))
let quoteString = quotes[Int(randomNumber)]
print(quoteString)
it will give you output randomly.
Don't forget that some numbers will repeat! so you need to do something like....
my totalQuestions was 47.
func getRandomNumbers(totalQuestions:Int) -> NSMutableArray
{
var arrayOfRandomQuestions: [Int] = []
print("arraySizeRequired = 40")
print("totalQuestions = \(totalQuestions)")
//This will output a 40 random numbers between 0 and totalQuestions (47)
while arrayOfRandomQuestions.count < 40
{
let limit: UInt32 = UInt32(totalQuestions)
let theRandomNumber = (Int(arc4random_uniform(limit)))
if arrayOfRandomQuestions.contains(theRandomNumber)
{
print("ping")
}
else
{
//item not found
arrayOfRandomQuestions.append(theRandomNumber)
}
}
print("Random Number set = \(arrayOfRandomQuestions)")
print("arrayOutputCount = \(arrayOfRandomQuestions.count)")
return arrayOfRandomQuestions as! NSMutableArray
}
look, i had the same problem but i insert
the function as a global variable
as
var RNumber = Int(arc4random_uniform(9)+1)
func GetCase(){
your code
}
obviously this is not efficent, so then i just copy and paste the code into the function so it could be reusable, then xcode suggest me to set the var as constant so my code were
func GetCase() {
let RNumber = Int(arc4random_uniform(9)+1)
if categoria == 1 {
}
}
well thats a part of my code so xcode tell me something of inmutable and initialization but, it build the app anyway and that advice simply dissapear
hope it helps

How can I perform an Array Slice in Swift?

var mentions = ["#alex", "#jason", "#jessica", "#john"]
I want to limit my array to 3 items, so I want to splice it:
var slice = [String]()
if mentions.count > 3 {
slice = mentions[0...3] //alex, jason, jessica
} else {
slice = mentions
}
However, I'm getting:
Ambiguous subscript with base type '[String]' and index type 'Range'
Apple Swift version 2.2 (swiftlang-703.0.18.8 clang-703.0.31)
Target: x86_64-apple-macosx10.9
The problem is that mentions[0...3] returns an ArraySlice<String>, not an Array<String>. Therefore you could first use the Array(_:) initialiser in order to convert the slice into an array:
let first3Elements : [String] // An Array of up to the first 3 elements.
if mentions.count >= 3 {
first3Elements = Array(mentions[0 ..< 3])
} else {
first3Elements = mentions
}
Or if you want to use an ArraySlice (they are useful for intermediate computations, as they present a 'view' onto the original array, but are not designed for long term storage), you could subscript mentions with the full range of indices in your else:
let slice : ArraySlice<String> // An ArraySlice of up to the first 3 elements
if mentions.count >= 3 {
slice = mentions[0 ..< 3]
} else {
slice = mentions[mentions.indices] // in Swift 4: slice = mentions[...]
}
Although the simplest solution by far would be just to use the prefix(_:) method, which will return an ArraySlice of the first n elements, or a slice of the entire array if n exceeds the array count:
let slice = mentions.prefix(3) // ArraySlice of up to the first 3 elements
We can do like this,
let arr = [10,20,30,40,50]
let slicedArray = arr[1...3]
if you want to convert sliced array to normal array,
let arrayOfInts = Array(slicedArray)
You can try .prefix().
Returns a subsequence, up to the specified maximum length, containing the initial elements of the collection.
If the maximum length exceeds the number of elements in the collection, the result contains all the elements in the collection.
let numbers = [1, 2, 3, 4, 5]
print(numbers.prefix(2)) // Prints "[1, 2]"
print(numbers.prefix(10)) // Prints "[1, 2, 3, 4, 5]"
General solution:
extension Array {
func slice(size: Int) -> [[Element]] {
(0...(count / size)).map{Array(self[($0 * size)..<(Swift.min($0 * size + size, count))])}
}
}
Can also look at dropLast() function:
var mentions:[String] = ["#alex", "#jason", "#jessica", "#john"]
var slice:[String] = mentions
if mentions.count > 3 {
slice = Array(mentions.dropLast(mentions.count - 3))
}
//print(slice) => ["#alex", "#jason", "#jessica"]
I came up with this:
public extension Array {
func slice(count: Int) -> [some Collection] {
let n = self.count / count // quotient
let i = n * count // index
let r = self.count % count // remainder
let slices = (0..<n).map { $0 * count }.map { self[$0 ..< $0 + count] }
return (r > 0) ? slices + [self[i..<i + r]] : slices
}
}
You can also slice like this:
//Generic Method
func slice<T>(arrayList:[T], limit:Int) -> [T]{
return Array(arrayList[..<limit])
}
//How to Use
let firstThreeElements = slice(arrayList: ["#alex", "#jason", "#jessica", "#john"], limit: 3)
Array slice func extension:
extension Array {
func slice(with sliceSize: Int) -> [[Element]] {
guard self.count > 0 else { return [] }
var range = self.count / sliceSize
if self.count.isMultiple(of: sliceSize) {
range -= 1
}
return (0...range).map { Array(self[($0 * sliceSize)..<(Swift.min(($0 + 1) * sliceSize, self.count))]) }
}
}

Randomly choosing an item from a Swift array without repeating

This code picks a random color from a array of pre-set colors. How do I make it so the same color doesn't get picked more than once?
var colorArray = [(UIColor.redColor(), "red"), (UIColor.greenColor(), "green"), (UIColor.blueColor(), "blue"), (UIColor.yellowColor(), "yellow"), (UIColor.orangeColor(), "orange"), (UIColor.lightGrayColor(), "grey")]
var random = { () -> Int in
return Int(arc4random_uniform(UInt32(colorArray.count)))
} // makes random number, you can make it more reusable
var (sourceColor, sourceName) = (colorArray[random()])
Create an array of indexes. Remove one of the indexes from the array and then use that to fetch a color.
Something like this:
var colorArray = [
(UIColor.redColor(), "red"),
(UIColor.greenColor(), "green"),
(UIColor.blueColor(), "blue"),
(UIColor.yellowColor(), "yellow"),
(UIColor.orangeColor(), "orange"),
(UIColor.lightGrayColor(), "grey")]
var indexes = [Int]();
func randomItem() -> UIColor
{
if indexes.count == 0
{
print("Filling indexes array")
indexes = Array(0..< colorArray.count)
}
let randomIndex = Int(arc4random_uniform(UInt32(indexes.count)))
let anIndex = indexes.removeAtIndex(randomIndex)
return colorArray[anIndex].0;
}
The code above creates an array indexes. The function randomItem looks to see if indexes is empty. if it is, it populates it with index values ranging from 0 to colorArray.count - 1.
It then picks a random index in the indexes array, removes the value at that index in the indexes array, and uses it to fetch and return an object from your colorArray. (It doesn't remove objects from the colorArray. It uses indirection, and removes objects from the indexesArray, which initially contains an index value for each entry in your colorArray.
The one flaw in the above is that after you fetch the last item from indexArray, you populate it with a full set of indexes, and it's possible that the next color you get from the newly repopulated array will be the same as the last one you got.
It's possible to add extra logic to prevent this.
based on the fact, that arc4random_uniform generate not only random, but also uniformly distributed numbers
import Foundation // arc4random_uniform
class Random {
var r:UInt32
let max: UInt32
init(max: UInt32) {
self.max = max
r = arc4random_uniform(max)
}
var next: UInt32 {
var ret: UInt32
repeat {
ret = arc4random_uniform(max)
} while r == ret
r = ret
return r
}
}
// usage example
let r = Random(max: 5)
for i in 0..<10 {
print(r.r, r.next) // there will never be a pair of the same numbers in the
// generated stream
}
/*
2 4
4 0
0 3
3 0
0 3
3 4
4 1
1 3
3 4
4 3
*/
simple test for different k and stream length of one milion
class Random {
var r:UInt32
let max: UInt32
init(max: UInt32) {
self.max = max
r = arc4random_uniform(max)
}
var next: (UInt32, Int) {
var i = 0
var ret: UInt32
repeat {
ret = arc4random_uniform(max)
i += 1
} while r == ret
r = ret
return (r,i)
}
}
for k in 3..<16 {
let r = Random(max: UInt32(k))
var repetition = 0
var sum = 0
for i in 0..<1000000 {
let j = r.next
repetition = max(repetition, j.1)
sum += j.1
}
print("maximum of while repetition for k:", k, "is", repetition, "with average of", Double(sum) / Double(1000000) )
}
prints
maximum of while repetition for k: 3 is 15 with average of 1.499832
maximum of while repetition for k: 4 is 12 with average of 1.334008
maximum of while repetition for k: 5 is 9 with average of 1.250487
maximum of while repetition for k: 6 is 8 with average of 1.199631
maximum of while repetition for k: 7 is 8 with average of 1.167501
maximum of while repetition for k: 8 is 7 with average of 1.142799
maximum of while repetition for k: 9 is 8 with average of 1.124096
maximum of while repetition for k: 10 is 6 with average of 1.111178
maximum of while repetition for k: 11 is 7 with average of 1.099815
maximum of while repetition for k: 12 is 7 with average of 1.091041
maximum of while repetition for k: 13 is 6 with average of 1.083582
maximum of while repetition for k: 14 is 6 with average of 1.076595
maximum of while repetition for k: 15 is 6 with average of 1.071965
finaly, here is more Swifty and functional approach based on the same idea
import Foundation
func random(max: Int)->()->Int {
let max = UInt32(max)
var last = arc4random_uniform(max)
return {
var r = arc4random_uniform(max)
while r == last {
r = arc4random_uniform(max)
}
last = r
return Int(last)
}
}
let r0 = random(8)
let r1 = random(4)
for i in 0..<20 {
print(r0(), terminator: " ")
}
print("")
for i in 0..<20 {
print(r1(), terminator: " ")
}
/*
4 5 4 3 4 0 5 6 7 3 6 7 5 4 7 4 7 2 1 6
0 3 0 1 0 2 3 1 2 0 1 0 1 0 1 3 0 3 0 2
*/
Fill an array with the colors and shuffle it with a Fisher-Yates shuffle. Then use the element at an end, remove it, and insert it at a random position at least n positions from the end.
For example, say my array has 10 elements. I shuffle it and take the last. I want at least 2 values to be chosen before I see it again so I generate a random position in the range 0...8 and insert it there.
var colorArray = [
(UIColor.redColor() , "red" ),
(UIColor.greenColor() , "green" ),
(UIColor.blueColor() , "blue" ),
(UIColor.yellowColor() , "yellow"),
(UIColor.orangeColor() , "orange"),
(UIColor.lightGrayColor(), "grey" )].shuffle() // shuffle() is from my link above
let spacing = 2 // Pick at least 2 colors before we see it again
if let randomColor = colorArray.popLast() {
colorArray.insert(randomColor,
atIndex: Int(arc4random_uniform(UInt32(colorArray.count - spacing))))
}
One case, described here: https://github.com/dimpiax/GenericSequenceType
Another is functional:
func getRandomItem<T>(arr: [T]) -> (unique: Bool) -> T {
var indexes: [Int]!
return { value in
let uniqIndex: Int
if value {
if indexes?.isEmpty != false {
indexes = [Int](0.stride(to: arr.count, by: 1))
}
uniqIndex = indexes.removeAtIndex(Int(arc4random_uniform(UInt32(indexes.count))))
}
else {
uniqIndex = Int(arc4random_uniform(UInt32(arr.count)))
}
return arr[uniqIndex]
}
}
let generate = getRandomItem(colorArray)
generate(unique: true).0 // greenColor
generate(unique: true).0 // redColor
generate(unique: true).0 // lightGrayColor
Try it it's work for me and 100% tested
let arrString = ["1","2","3","4","5","6"]
var selectedIndix = -1
#IBAction func btnClick(_ sender: Any) {
let randomElementIndex = randomElementString()
}
Call this function when button clicked
func randomElementString() -> Int{
let randomm = Int(arc4random_uniform(UInt32(arrString.count)))
if selectedIndix == randomm{
return randomElementString()
}else{
selectedIndix = randomm
return randomm
}
}
OUTPUT:-
5121242316513126
How about running a while loop with the condition:
while(self.source.backgroundColor == sourceColor) {
// get a new random sourceColor
}
This will keep looping until a new random color has been selected.
edit
Additional Note: The point was the while loop. There are ways to safeguard from an infinite loop, it's up to the coder to find the right solution. I don't think SO is a place to write other's code but instead to offer suggestions .. mine is a start.
But since my answer was given such a negative rating, i'll push instead of nudge in the right direction.
The other answers are unnecessarily bloated. And? The one I offered above offers a less than desirable time complexity. So, here's my new answer (in meta code):
// array of all background colors
var arrayOfColors = [..]
// get a random index
var randomIndex = arc4random(size of arrayOfColors)
// select new background color
var newBGColor = arrayOfColors[randomIndex]
// old background color
var oldBGColor = self.source.backgroundColor
// remove new color from array (so that it's excluded from choices next time)
arrayOfColors.removeAtIndex(randomIndex)
// set the new color to the background
self.source.backgroundColor = newBGColor
// add current color back into the pool of potential colors
arrayOfColors.addObject(oldBGColor)

Random number from an array without repeating the same number twice in a row?

I am making a game using Swift and SpriteKit where i move an object to random locations based on an array.
The array that is made up of CGPoints:
let easyArray = [CGPointMake(0,0), CGPointMake(126.6,0), CGPointMake(253.4,0), CGPointMake(0,197.5), CGPointMake(126.7,197.5), CGPointMake(253.4,197.5), CGPointMake(0,395), CGPointMake(126.7,395), CGPointMake(253.4,395)]
I use this function to generate a random number:
func randomNumber(maximum: UInt32) -> Int {
var randomNumber = arc4random_uniform(maximum)
while previousNumber == randomNumber {
randomNumber = arc4random_uniform(maximum)
}
previousNumber = randomNumber
return Int(randomNumber)
}
I used this to move the object based on the random number generated:
let greenEasy = randomNumberNew(9)
let moveSelector = SKAction.moveTo(easyArray[greenEasy], duration: 0)
selector.runAction(moveSelector)
I have done some reading online and found that the "While" condition should make it so that the same random number isn't generate twice in a row. But it still happens.
Can anyone please help me on how to make it so i don't get the same number twice in a row?
The code below doesn't random the same number.
var currentNo: UInt32 = 0
func randomNumber(maximum: UInt32) -> Int {
var randomNumber: UInt32
do {
randomNumber = (arc4random_uniform(maximum))
}while currentNo == randomNumber
currentNo = randomNumber
return Int(randomNumber)
}
I think Larme's suggestion is pretty clever, actually.
easyArray.append(easyArray.removeAtIndex(Int(arc4random_uniform(UInt32(easyArray.count)-1))))
selector.runAction(SKAction.moveTo(easyArray.last!, duration: 0))
I would recommend to not use while() loops with randomizers.
Theoretically it can cause infinite loops in worst case scenario, in more positive scenario it will just take few loops before you get desired results.
Instead I would advice to make an NSArray of all values, remove from this NSArray last randomized element and randomize any of other existing elements from such an array - that is guarantee result after only one randomize iteration.
It can be easily achieved by making NSArray category in Objective-C:
- (id) randomARC4Element
{
if(self.count > 0)
{
return [self objectAtIndex:[self randomIntBetweenMin:0 andMax:self.count-1]];
}
return nil;
}
- (int)randomIntBetweenMin:(int)minValue andMax:(int)maxValue
{
return (int)(minValue + [self randomFloat] * (maxValue - minValue));
}
- (float)randomFloat
{
return (float) arc4random() / UINT_MAX;
}
If you can use linq then you can select a random value that doesn't match the last value. Then for any left over values you can loop through and find valid places to insert them.
It's not the most efficient but it works.
public static List<int> Randomize(List<int> reps, int lastId) {
var rand = new Random();
var newReps = new List<int>();
var tempReps = new List<int>();
tempReps.AddRange(reps);
while (tempReps.Any(x => x != lastId)) {
var validReps = tempReps.FindAll(x => x != lastId);
var i = rand.Next(0, validReps.Count - 1);
newReps.Add(validReps[i]);
lastId = validReps[i];
tempReps.Remove(validReps[i]);
}
while (tempReps.Any()) {
var tempRep = tempReps.First();
bool placed = false;
for (int i = 0; i < newReps.Count; i++) {
if (newReps[i] == tempRep) {
continue;
}
else if ((i < newReps.Count - 1) && (newReps[i + 1] == tempRep)) {
continue;
}
else {
newReps.Insert(i + 1, tempRep);
placed = true;
break;
}
}
if (placed) {
tempReps.Remove(tempRep);
}
else {
throw new Exception("Unable to randomize reps");
}
}
return newReps;
}

Resources