Learning Swift — Day 150 to 151

Hacking with Swift — Learning Project 35 Random Numbers

Setting up

Create a new iOS playground and call it “Random Numbers”. Save it somewhere sensible.

Generating random numbers without GameplayKit

This is how modern Swift (post 4.2) manages it:

let int1 = Int.random(in: 0...10)
let int2 = Int.random(in: 0 ..< 10)
let double1 = Double.random(in: 1000...10000)
let float1 = Float.random(in: -100...100)

Old-fashioned randomness

The arc4random() function returns numbers between 0 and 4.294.967.295 …

To generate a random number within a specific range we can use the modulo operator like this:

arc4random() % 6

To generate a number between 0 and 5!

This generates the modulo bias or the pigeonhole principle, which causes some numbers to appear more frequently than others.

I read the suggested article about modulo bias and, from what I understood, if the maximum number you can get from your random number generation + 1 modulo the modulo you have chosen is not zero then we have it. Why is this a problem? No idea and it is not explained in there.

I read also the article on the pigeonhole principle but still I cannot make the connection with this code… anyways …

To avoid this we can use:

arc4random(uniform(6))

Which avoid that problem. If we want a more specific range things get more complicated:

func RandomInt(min: Int, max: Int) -> Int {
    if max < min { return min }
    
    return Int(arc4random_uniform(UInt32((max - min) + 1))) + min
}

This makes sure that if we inputted an upper bound that is greater than the lower one we just return the lower one. Then it generates a random number between 0 and the difference between upper and lower bound plus one and adds back the lower bound afterwards.

Generating random numbers with GameplayKit: GKRandomSource

Import GameplayKit into your project. Try this:

GKRandomSource.sharedRandom().nextInt()

This produces also negative numbers between -2.147.483.648 and 2.147.483.647.

Or, if you want just positive numbers like before:

GKRandomSource.sharedRandom().nextInt(upperBound: 6))

Choosing a random number source: GKARC4RandomSource and other GameplayKit options.

Three options there are here: GKLinearCongruentialRandomSource, fast and not so random, GKMersenneTwisterRandomSource, highly random but slow, GKARC4RandomSource, somewhere in the middle. Here are two examples:

let arc4 = GKARC4RandomSource()
arc4.nextInt(upperBound: 20)

let mersenne = GKMersenneTwisterRandomSource()
mersenne.nextInt(upperBound: 20)

Arc4 based random sources have repeatable initial sequences. If used for obfuscation we should drop N values from the start, where N should be any number larger than 768 to ensure the initial sequence is flushed.

Shaping GameplayKit random numbers: GKRandomDistribution, GKShuffledDistribution and GKGaussianDistribution.

Here is the description of the GKRandomDistribution class:

Summary

A random distribution is a random source itself with a specific mapping from the input source to the output values. The distribution is uniform, meaning there is no bias towards any of the possible outcomes.

Declaration

For the D&D passionates like me out there, this class allows us to replicate die throws, which are completely random and shielded from outside sources!

let d6 = GKRandomDistribution.d6()
d6.nextInt()

let d20 = GKRandomDistribution.d20()
d20.nextInt()

…and, just to be sure, I have immediately created all the other dice:

let d4 = GKRandomDistribution(lowestValue: 1, highestValue: 4)
let d8 = GKRandomDistribution(lowestValue: 1, highestValue: 8)
let d10 = GKRandomDistribution(lowestValue: 1, highestValue: 10)
let d12 = GKRandomDistribution(lowestValue: 1, highestValue: 12)

d4.nextInt()
d8.nextInt()
d10.nextInt()
d12.nextInt()

If you want a specific random source you can use this:

let rand = GKMersenneTwisterRandomSource()
let distribution = GKRandomDistribution(randomSource: rand, lowestValue: 10, highestValue: 20)
print(distribution.nextInt())

If we want sequences to repeat less frequently we should use the GKShuffledDistribution class, thus described:

Summary

A shuffled distribution tries to make sure individual samples are not clustered whilst retaining a uniform distribution of values over time. This is often referred to as fair or less random, as the predictability of the outcomes in a series is vastly increased, yet the distribution of values is uniform.

Declaration

Discussion

Do not use with distributions ranging more than 256 between lowest and highest as the shuffling sequence is stored internally in memory.

As you can read, this allows each number to be equally represented but, as it is stored in memory, it will hinder performance!

let shuffled = GKShuffledDistribution.d6()
print(shuffled.nextInt())
print(shuffled.nextInt())
print(shuffled.nextInt())
print(shuffled.nextInt())
print(shuffled.nextInt())
print(shuffled.nextInt())

This will print each number once, leaving out no one, but the order is not guaranteed.

If you want results to “naturally” form a bell curve where they near the mean average, then you should use GKGaussianDistribution. To demonstrate this I tried to write some test code for it, in this form:

let gaussian = GKGaussianDistribution(lowestValue: 1, highestValue: 20)
var ones = 0, twos = 0, threes = 0, fours = 0, fives = 0, sixes = 0, sevens = 0, eights = 0, nines = 0, tens = 0, elevens = 0, twelves = 0, thirteens = 0, fourteens = 0, fifteens = 0, sixteens = 0, seventeens = 0, eighteens = 0, nineteens = 0, twenties = 0

for _ in 1...100_000 {
    let d20roll = gaussian.nextInt()
    
    switch d20roll {
    case 1: ones += 1
    case 2: twos += 1
    case 3: threes += 1
    case 4: fours += 1
    case 5: fives += 1
    case 6: sixes += 1
    case 7: sevens += 1
    case 8: eights += 1
    case 9: nines += 1
    case 10: tens += 1
    case 11: elevens += 1
    case 12: twelves += 1
    case 13: thirteens += 1
    case 14: fourteens += 1
    case 15: fifteens += 1
    case 16: sixteens += 1
    case 17: seventeens += 1
    case 18: eighteens += 1
    case 19: nineteens += 1
    case 20: twenties += 1
        
    default: print("Invalid roll")
    }
}

Which gave me this output:

Ones: 185;
Twos: 366;
Threes: 761;
Fours: 1518;
Fives: 2847;
Sixes: 4703;
Sevens: 6965;
Eights: 9220;
Nines: 11190;
Tens: 12457;
Elevens: 12416;
Twelves: 11182;
Thirteens: 9041;
Fourteens: 6777;
Fifteens: 4630;
Sixteens: 2836;
Seventeens: 1516;
Eighteens: 834;
Nineteen: 406;
Twenties: 150.

To check, I ran it a second time and the results were pretty similar:

Ones: 175;
Twos: 379;
Threes: 762;
Fours: 1518;
Fives: 2871;
Sixes: 4576;
Sevens: 6852;
Eights: 9195;
Nines: 11248;
Tens: 12435;
Elevens: 12329;
Twelves: 11236;
Thirteens: 9195;
Fourteens: 6875;
Fifteens: 4659;
Sixteens: 2774;
Seventeens: 1602;
Eighteens: 827;
Nineteen: 344;
Twenties: 148.

As far as I know, many good quality D&D dices are weighted to accommodate this principle.

Shuffling an array with GameplayKit: arrayByShufflingObjects(in:)

Here Paul suggests that there is an extension to the Array type that shuffles an array in place, in this form:

extension Array {
    mutating func shuffleInPlace() {
        for i in 0 ..< (count - 1) {
            let j = Int(arc4random_uniform(UInt32(count - 1))) + i
            swapAt(i, j)
        }
    }
}

I changed the name of the method because the Swift Standard Library has already a method called shuffle() and I do not know if they are the same. When you look at this proposal, though, you see something strange: (count - 1) and, later, + i. Try to thing of an array of 10 elements, indexes 0 to 9. We loop over it, and let’s say we get to index 5, and j becomes a random number between 0 and 8, plus 5. If we get something like 5 or more, then we have a “Fatal error: Index out of range”.

I wonder if this is a typo or what … I tried to see what the definition of shuffle() was in the Standard Library and I expected to see some code in there, but no, just a definition. Very strange … also it seems that shuffle() has been deprecated in 4.1 and now we should use compactMap()but I quite sincerely don’t know how to use that. Let’s skip this thing, eh?!

We are then shown the GameplayKit offer on this matter:

let lotteryBalls = [Int](1...49)
let shuffledBalls = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: lotteryBalls)
print(shuffledBalls[0])
print(shuffledBalls[1])
print(shuffledBalls[2])
print(shuffledBalls[3])
print(shuffledBalls[4])
print(shuffledBalls[5])

This prints, for me, 29, 3, 10, 47, 24, 12.

The most interesting part here to me was the array initialiser, [Int](1...49).

We can also make random number generation be predictable by giving it a precise seed (which is not explained what it is and by looking at Wikipedia if you are not a mathematician you will probably not gain too much from it). In this way the numbers generated will always be the same.

I don’t know why, but all these things that are important for people who want to cheap and to screw others are really non-attractive to me… completely non attractive…

Wrap up

As I said for the previous project, also this one is a bit lacklustre … there is some space for great things but many lines of codes either do not compile or are left there in the void.

I really wish the quality I found in the first 30 projects would have been kept for the following ones, but sadly, at least by now, this is not the case.


If you like what I’m doing here please consider liking this article and sharing it with some of your peers. If you are feeling like being really awesome, please consider making a small donation to support my studies and my writing (please appreciate that I am not using advertisement on my articles).

If you are interested in my music engraving and my publications don’t forget visit my Facebook page and the pages where I publish my scores (Gumroad, SheetMusicPlus, ScoreExchange and on Apple Books).

You can also support me by buying Paul Hudson’s books from this Affiliate Link.

Anyways, thank you so much for reading!

Till the next one!

Published by Michele Galvagno

Professional Musical Scores Designer and Engraver Graduated Classical Musician (cello) and Teacher Tech Enthusiast and Apprentice iOS / macOS Developer Grafico di Partiture Musicali Professionista Musicista classico diplomato (violoncello) ed insegnante Appassionato di tecnologia ed apprendista Sviluppatore iOS / macOS

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: