Joseph Astrahan
Joseph Astrahan

Reputation: 9072

Swift 3: How to Calculate Random Number with Favor Towards A Bias

Lets say I'm calculating random numbers 1 through 100. I want the numbers to be random it chooses but with a bias I can set where it's more likely to select the center. So if I do the random samples lets say a thousand times there will be an obvious correlation that the center numbers were chosen more often. The amount it chooses the center should be based on a number I can set in my didHitChanceOf function. What is the best way to do this?

The current code I have does not do this and is even accross the board in it randomness

Current Non-Biased Random Number Code (Swift 3)

extension Int
{
    static func random(range: ClosedRange<Int> ) -> Int
    {
        var offset = 0

        if range.lowerBound < 0   // allow negative ranges
        {
            offset = abs(range.lowerBound)
        }

        let mini = UInt32(range.lowerBound + offset)
        let maxi = UInt32(range.upperBound   + offset)

        return Int(mini + arc4random_uniform(maxi - mini)) - offset
    }
}

func didHitChanceOf(chance: Double) -> Bool{
        let random = Int.random(range: 0...100)
        if(Double(random) < chance){ //If the conversion rate is 20%, then only 20% of the time will the random number be less than the conversion rate.
            return true
        }else{
            return false
        }
    }
var adwordsClicks = 500
let adwordsConversionRate = 20
var adwordsConversions = 0
for _ in 0...adwordsClicks {
            if(didHitChanceOf(chance: adwordsConversionRate) == true){
                adwordsConversions = adwordsConversions + 1
            }
        }

Upvotes: 3

Views: 312

Answers (1)

Code Different
Code Different

Reputation: 93181

You can use GKGaussianDistribution (aka normal distribution) from GameKit to do that. You will need 2 parameters: mean (the "center" that you desire) and deviation (how far out it should spread from the center):

import GameKit

func random(count: Int, in range: ClosedRange<Int>, mean: Int, deviation: Int) -> [Int] {
    guard count > 0 else { return [] }

    let randomSource = GKARC4RandomSource()
    let randomDistribution = GKGaussianDistribution(randomSource: randomSource, mean: Float(mean), deviation: Float(deviation))

    // Clamp the result to within the specified range
    return (0..<count).map { _ in
        let rnd = randomDistribution.nextInt()

        if rnd < range.lowerBound {
            return range.lowerBound
        } else if rnd > range.upperBound {
            return range.upperBound
        } else {
            return rnd
        }
    }
}

Usage and test:

let arr = random(count: 1_000_000, in: 0...100, mean: 70, deviation: 10)

let summary = NSCountedSet(array: arr)
for i in 0...100 {
    print("\(i): \(summary.count(for: i))")
}

You can see that values around 70 have the highest counts

Upvotes: 7

Related Questions