When random ain't random

The BMITime project in chapt. 21 uses random() % [employees count] to get a random index for the employees array. However, subsequent runs get the same assets assigned to the same employees – so the number is random only within one run, not with multiple runs.

For example, here is part of the output using just the random() function as in the book, with some additional logging of the values, the same values repeat, run after run…

2014-01-21 19:25:28.209 Chap18_BMITime[20710:303] randNum = 1804289383;	randomIndex = 3
2014-01-21 19:25:28.210 Chap18_BMITime[20710:303] randNum = 846930886;	randomIndex = 6
2014-01-21 19:25:28.210 Chap18_BMITime[20710:303] randNum = 1681692777;	randomIndex = 7
2014-01-21 19:25:28.210 Chap18_BMITime[20710:303] randNum = 1714636915;	randomIndex = 5
2014-01-21 19:25:28.211 Chap18_BMITime[20710:303] randNum = 1957747793;	randomIndex = 3
2014-01-21 19:25:28.211 Chap18_BMITime[20710:303] randNum = 424238335;	randomIndex = 5
2014-01-21 19:25:28.211 Chap18_BMITime[20710:303] randNum = 719885386;	randomIndex = 6
2014-01-21 19:25:28.211 Chap18_BMITime[20710:303] randNum = 1649760492;	randomIndex = 2
2014-01-21 19:25:28.212 Chap18_BMITime[20710:303] randNum = 596516649;	randomIndex = 9
2014-01-21 19:25:28.212 Chap18_BMITime[20710:303] randNum = 1189641421;	randomIndex = 1
2014-01-21 19:25:28.212 Chap18_BMITime[20710:303] Employees: (
    "<Employee 0: $0 in assets>",
    "<Employee 1: $503 in assets>",
    "<Employee 2: $469 in assets>",
    "<Employee 3: $768 in assets>",
    "<Employee 4: $0 in assets>",
    "<Employee 5: $836 in assets>",
    "<Employee 6: $819 in assets>",
    "<Employee 7: $384 in assets>",
    "<Employee 8: $0 in assets>",
    "<Employee 9: $486 in assets>"
)
. . .

I tried setting a different seed value for each iteration in the for loop, using srandom(i). Here is the output – different from above, but again repeating on multiple runs:

2014-01-21 19:26:58.678 Chap18_BMITime[20720:303] randNum = 577655601;	randomIndex = 1
2014-01-21 19:26:58.679 Chap18_BMITime[20720:303] randNum = 1804289383;	randomIndex = 3
2014-01-21 19:26:58.680 Chap18_BMITime[20720:303] randNum = 1505335290;	randomIndex = 0
2014-01-21 19:26:58.680 Chap18_BMITime[20720:303] randNum = 1205554746;	randomIndex = 6
2014-01-21 19:26:58.680 Chap18_BMITime[20720:303] randNum = 1968078301;	randomIndex = 1
2014-01-21 19:26:58.680 Chap18_BMITime[20720:303] randNum = 590011675;	randomIndex = 5
2014-01-21 19:26:58.680 Chap18_BMITime[20720:303] randNum = 290852541;	randomIndex = 1
2014-01-21 19:26:58.681 Chap18_BMITime[20720:303] randNum = 1045618677;	randomIndex = 7
2014-01-21 19:26:58.681 Chap18_BMITime[20720:303] randNum = 757547896;	randomIndex = 6
2014-01-21 19:26:58.681 Chap18_BMITime[20720:303] randNum = 444454915;	randomIndex = 5
2014-01-21 19:26:58.681 Chap18_BMITime[20720:303] Employees: (
    "<Employee 0: $384 in assets>",
    "<Employee 1: $1220 in assets>",
    "<Employee 2: $0 in assets>",
    "<Employee 3: $367 in assets>",
    "<Employee 4: $0 in assets>",
    "<Employee 5: $938 in assets>",
    "<Employee 6: $887 in assets>",
    "<Employee 7: $469 in assets>",
    "<Employee 8: $0 in assets>",
    "<Employee 9: $0 in assets>"
)
. . .

To get truly random values with multiple runs, use srandomdev() before the loop that creates the ten assets. For example:

        // Initialize a state array for use with random(), to get true random values that do not repeat on subsequent runs.
        srandomdev();
        
        // Create 10 assets, and randomly assign them to employees.
        for (int i = 0; i < 10; i++) {
            BNRAsset *asset = [[BNRAsset alloc] init];
            
            NSString *currentLabel = [NSString stringWithFormat:@"Laptop %d", i];
            asset.label = currentLabel;
            asset.resaleValue = 350 + i * 17;
            
            // Get a random number between 0 and 9, inclusive.
            // srandom(i); // Set the initial seed value for the call to random(). Repeats on subsequent runs.
            long randNum = random();
            NSUInteger randomIndex = randNum % [employees count];
            NSLog(@"randNum = %ld;\trandomIndex = %lu", randNum, (unsigned long)randomIndex);
            
            // Find that employee.
            BNREmployee *randomEmployee = [employees objectAtIndex:randomIndex];
            
            // Assign the asset.
            [randomEmployee addAsset:asset];
        }
____________________________Output using srandomdev(): True random values.

2014-01-21 19:30:41.613 Chap18_BMITime[20741:303] randNum = 1656913153;	randomIndex = 3
2014-01-21 19:30:41.615 Chap18_BMITime[20741:303] randNum = 1244619469;	randomIndex = 9
2014-01-21 19:30:41.615 Chap18_BMITime[20741:303] randNum = 1426723296;	randomIndex = 6
2014-01-21 19:30:41.615 Chap18_BMITime[20741:303] randNum = 437041327;	randomIndex = 7
2014-01-21 19:30:41.615 Chap18_BMITime[20741:303] randNum = 2015308048;	randomIndex = 8
2014-01-21 19:30:41.616 Chap18_BMITime[20741:303] randNum = 1224372094;	randomIndex = 4
2014-01-21 19:30:41.616 Chap18_BMITime[20741:303] randNum = 726615580;	randomIndex = 0
2014-01-21 19:30:41.616 Chap18_BMITime[20741:303] randNum = 1250304383;	randomIndex = 3
2014-01-21 19:30:41.616 Chap18_BMITime[20741:303] randNum = 220853434;	randomIndex = 4
2014-01-21 19:30:41.617 Chap18_BMITime[20741:303] randNum = 1558241678;	randomIndex = 8
2014-01-21 19:30:41.617 Chap18_BMITime[20741:303] Employees: (
    "<Employee 0: $452 in assets>",
    "<Employee 1: $0 in assets>",
    "<Employee 2: $0 in assets>",
    "<Employee 3: $819 in assets>",
    "<Employee 4: $921 in assets>",
    "<Employee 5: $0 in assets>",
    "<Employee 6: $384 in assets>",
    "<Employee 7: $401 in assets>",
    "<Employee 8: $921 in assets>",
    "<Employee 9: $367 in assets>"
)
. . .
____________________________Using srandomdev() again: Different random values.

2014-01-21 19:33:11.411 Chap18_BMITime[20752:303] randNum = 1068223855;	randomIndex = 5
2014-01-21 19:33:11.412 Chap18_BMITime[20752:303] randNum = 93766855;	randomIndex = 5
2014-01-21 19:33:11.413 Chap18_BMITime[20752:303] randNum = 1264514690;	randomIndex = 0
2014-01-21 19:33:11.413 Chap18_BMITime[20752:303] randNum = 808032816;	randomIndex = 6
2014-01-21 19:33:11.413 Chap18_BMITime[20752:303] randNum = 1747323470;	randomIndex = 0
2014-01-21 19:33:11.414 Chap18_BMITime[20752:303] randNum = 435037921;	randomIndex = 1
2014-01-21 19:33:11.414 Chap18_BMITime[20752:303] randNum = 1629484606;	randomIndex = 6
2014-01-21 19:33:11.414 Chap18_BMITime[20752:303] randNum = 1802865269;	randomIndex = 9
2014-01-21 19:33:11.414 Chap18_BMITime[20752:303] randNum = 1196674998;	randomIndex = 8
2014-01-21 19:33:11.415 Chap18_BMITime[20752:303] randNum = 1115165860;	randomIndex = 0
2014-01-21 19:33:11.415 Chap18_BMITime[20752:303] Employees: (
    "<Employee 0: $1305 in assets>",
    "<Employee 1: $435 in assets>",
    "<Employee 2: $0 in assets>",
    "<Employee 3: $0 in assets>",
    "<Employee 4: $0 in assets>",
    "<Employee 5: $717 in assets>",
    "<Employee 6: $853 in assets>",
    "<Employee 7: $0 in assets>",
    "<Employee 8: $486 in assets>",
    "<Employee 9: $469 in assets>"
)
. . .

I arrived here because I noticed the same thing you did. While you answered the question of how to truly randomize things, I’m curious as to why they weren’t actually random in the first place. Anyone?

On why random is not random:

The modulo operator (%) gives you the remainder after division. Looking at the code below,

NSUInteger randomIndex = random() % [employees count];

the random number is always divided by 10 because there are 10 objects in ‘employees’ array. The remainder after division by 10 is always a single digit in between 0 and 9 - the units digit. This explains why we see the duplicate results - 5, 15, 25, 25 all have the remainder 5 after the division by 10. So actually, the number generated by random() is in fact random. It’s the remainder after division by 10 that are duplicates.

One of the reasons random() is repeatable (unless you use srandomdev() ) is so you can debug code without having each run using a different “random” sequence. That way if you see differences from run-to-run you know it’s due to your code and not related to the “random” numbers. Once everything’s debugged you’d probably want to add srandomdev() (unless it really doesn’t matter, as in this BNR example).