How random is Python’s random?
Computers have a poor reputation for randomness, and with good reason. In the old days, randomness wasn’t taken seriously. In order to get a random number the system might poll some non-random aspect of the hardware. Those whose programmers understood some mathematics might use an algorithm, but they would reset the algorithm every time the computer started up, or, worse, every time a program started up, resulting in the same series of “random” numbers every time a program was run.
Nowadays you shouldn’t have to worry about that. The reason is security: modern security relies heavily on getting truly random numbers out of the computer’s random algorithms. This has resulted in a lot of research into computer-friendly random algorithms. Python’s random functions are able to leverage this research.1 You should have no problem using your computer’s random number generators for gaming purposes nowadays.
The default random number generators in most programming languages are not cryptographically-appropriate random, however.2 The reason for this is that computer programs are bound by predictability: programmers want predictability, even when they say they want randomness.
Imagine, for example, that you’re animating a film clip in a 3D renderer. You carefully place all of the major components of the scene, but you also have a flock of birds flying in the background. You outline the path that the flock will take, but after you render the video, the birds are far too uniform. They look more like a smooth amusement park ride than a flock of individual creatures. You might say they look like a railroaded adventure.
So you add some randomness to each individual bird’s flight path; each bird follows the basic flight path of the flock, but each moves left, right, up and down by some random amount each frame of the video. You don’t want to individually plot every single member of the flock, you just want them to act like birds. A little random variation is fine.3
But when you look at the film, you see that some of the birds are flying through a telephone pole. So you make some minor adjustments to the flight path and try again, and this time it’s great.
So you make your film and you send it off to the producers, and they say, awesome! Let’s make a high-definition version for the theaters. So you re-render the film… and the birds are moving in different directions this time, because it’s random! Every time you re-render the film, the birds move differently.
The solution, and this is often enforced by the renderer, is to require you to “seed” a random number generator that emulates statistical tests for randomness but which produces results from a series of artificially—that is, pseudo—random numbers. Python uses the Mersenne twister. This algorithm produces 219937-1 “random” results before repeating itself. That’s the number “4” followed by 6,001 digits.4 When you ask for a random number, or a random choice within a list, Python “seeds” this long list by creating a random starting point for the algorithm, and then chooses each successive number from the algorithm’s “list”.
By default, you let Python choose your seed for you. But if you give the random number generator a seed, it will produce the same random numbers each time you give it that seed. Thus, you can use randomness to make a better flock without fear of the birds acting differently each time you render the scene. Persistence of Vision uses this method to give you, as a 3D artist, non-random pseudorandomness.
In the examples I’ve provided so far, we have not specified a seed, so the computer chooses a seed randomly. This makes our computer-generated rolls, for all practical purposes, fully random. But you can specify a seed if you want to see this phenomenon in action, and thus get the same random numbers every time.
[toggle code]
- #!/usr/bin/python
- import optparse
- import random
- parser = optparse.OptionParser()
- (options, args) = parser.parse_args()
-
if not args:
- args = [1]
-
for dieCount in args:
- dieCount = int(dieCount)
- total = 0
- dice = []
-
for counter in range(dieCount):
- die = random.randrange(6)+1
- total = total + die
- dice.append(die)
- print total,
-
if dieCount > 1:
- print ':', dice
-
else:
Save this as die6, make it executable, and you can run it as ./die6. You’ll get a random sequence of d6 rolls every time. You can roll up some stats using:
- $ ./die6 3 3 3 3 3 3
- 8 : [1, 5, 2]
- 7 : [4, 2, 1]
- 7 : [1, 5, 1]
- 13 : [5, 4, 4]
- 11 : [5, 2, 4]
- 9 : [2, 4, 3]
Now, add one line to the code:
[toggle code]
- #!/usr/bin/python
- import optparse
- import random
- parser = optparse.OptionParser()
- (options, args) = parser.parse_args()
-
if not args:
- args = [1]
- random.seed(616)
-
for dieCount in args:
- dieCount = int(dieCount)
- total = 0
- dice = []
-
for counter in range(dieCount):
- die = random.randrange(6)+1
- total = total + die
- dice.append(die)
- print total,
-
if dieCount > 1:
- print ':', dice
-
else:
I can now predict what your stats are: 9, 7, 6, 11, 14, and 12.5
This is what makes this particular algorithm unsafe for cryptography: if a hacker can determine where in the sequence you were when you generated your random numbers, they can predict what those numbers are. For gaming purposes, however, this is a non-issue. Unless your players are mathematical savants of unearthly power, they are not going to be able to predict where in that series you are, nor will it matter, because by default you get a different seed each time you “roll the die”.6
That said, if you let them roll their own dice programs, you might consider looking at the code. Here’s a variation that lets you specify your own seed:
[toggle code]
- #!/usr/bin/python
- import optparse
- import random
- parser = optparse.OptionParser()
- parser.add_option('-s', '--seed', type='int')
- (options, args) = parser.parse_args()
-
if not args:
- args = [1]
-
if options.seed:
- random.seed(options.seed)
-
for dieCount in args:
- dieCount = int(dieCount)
- total = 0
- dice = []
-
for counter in range(dieCount):
- die = random.randrange(6)+1
- total = total + die
- dice.append(die)
- print total,
-
if dieCount > 1:
- print ':', dice
-
else:
Seed 631 isn’t a bad set of stats. 635 is also believable with a couple of seventeens and only one dump stat. Seed 3555, on the other hand, is probably not believable. But take a look at 48191: two eighteens, two eights. You could get away with that. On the other hand, seed 122075, probably not.
- $ ./die6 3 3 3 3 3 3 --seed 631
- 10 : [5, 3, 2]
- 14 : [4, 6, 4]
- 18 : [6, 6, 6]
- 12 : [5, 6, 1]
- 15 : [6, 5, 4]
- 14 : [4, 6, 4]
- $ ./die6 3 3 3 3 3 3 --seed 635
- 12 : [3, 5, 4]
- 13 : [2, 5, 6]
- 8 : [1, 2, 5]
- 12 : [4, 6, 2]
- 17 : [6, 6, 5]
- 17 : [6, 6, 5]
- $ ./die6 3 3 3 3 3 3 --seed 3555
- 12 : [6, 4, 2]
- 15 : [6, 4, 5]
- 16 : [6, 4, 6]
- 17 : [5, 6, 6]
- 18 : [6, 6, 6]
- 12 : [4, 3, 5]
- $ ./die6 3 3 3 3 3 3 --seed 48191
- 18 : [6, 6, 6]
- 8 : [1, 3, 4]
- 12 : [3, 5, 4]
- 18 : [6, 6, 6]
- 8 : [1, 2, 5]
- 15 : [6, 6, 3]
- $ ./die6 3 3 3 3 3 3 --seed 122075
- 11 : [4, 5, 2]
- 12 : [3, 6, 3]
- 18 : [6, 6, 6]
- 12 : [6, 5, 1]
- 18 : [6, 6, 6]
- 18 : [6, 6, 6]
But as long as you don’t use the same seed every time, this will be random enough.
As do Perl’s, PHP’s, and JavaScript’s to different degrees.
↑For that, read about random.SystemRandom.
↑I’m ignoring boids for the purpose of this example, because boids don’t have to be random.
↑43, 154, 247, 973, 881, 626, 480, 552, 355, 163, 379, 198, 390, 539, 350, 432, 267, 115, 051, 652, 505, 414, 033, 306, 801, 376, 580, 911, 304, 513, 629, 318, 584, 665, 545, 269, 938, 257, 648, 835, 317, 902, 217, 334, 584, 413, 909, 528, 269, 154, 609, 168, 019, 007, 875, 343, 741, 396, 296, 801, 920, 114, 486, 480, 902, 661, 414, 318, 443, 276, 980, 300, 066, 728, 104, 984, 095, 451, 588, 176, 077, 132, 969, 843, 762, 134, 621, 790, 396, 391, 341, 285, 205, 627, 619, 600, 513, 106, 646, 376, 648, 615, 994, 236, 675, 486, 537, 480, 241, 964, 350, 295, 935, 168, 662, 363, 909, 047, 948, 347, 692, 313, 978, 301, 377, 820, 785, 712, 419, 054, 474, 332, 844, 529, 183, 172, 973, 242, 310, 888, 265, 081, 321, 626, 469, 451, 077, 707, 812, 282, 829, 444, 775, 022, 680, 488, 057, 820, 028, 764, 659, 399, 164, 766, 265, 200, 900, 561, 495, 800, 344, 054, 353, 690, 389, 862, 894, 061, 792, 872, 011, 120, 833, 614, 808, 447, 482, 913, 547, 328, 367, 277, 879, 565, 648, 307, 846, 909, 116, 945, 866, 230, 169, 702, 401, 260, 240, 187, 028, 746, 650, 033, 445, 774, 570, 315, 431, 292, 996, 025, 187, 780, 790, 119, 375, 902, 863, 171, 084, 149, 642, 473, 378, 986, 267, 503, 308, 961, 374, 905, 766, 340, 905, 289, 572, 290, 016, 038, 000, 571, 630, 875, 191, 373, 979, 555, 047, 468, 154, 333, 253, 474, 991, 046, 248, 132, 504, 516, 341, 796, 551, 470, 575, 481, 459, 200, 859, 472, 614, 836, 213, 875, 557, 116, 864, 445, 789, 750, 886, 277, 996, 487, 304, 308, 450, 484, 223, 420, 629, 266, 518, 556, 024, 339, 339, 190, 844, 368, 921, 018, 424, 844, 677, 042, 727, 664, 601, 852, 914, 925, 277, 280, 922, 697, 538, 426, 770, 257, 333, 928, 954, 401, 205, 465, 895, 610, 347, 658, 855, 386, 633, 902, 546, 289, 962, 132, 643, 282, 425, 748, 035, 786, 233, 580, 608, 154, 696, 546, 932, 563, 833, 327, 670, 769, 899, 439, 774, 888, 526, 687, 278, 527, 451, 002, 963, 059, 146, 963, 875, 715, 425, 735, 534, 475, 979, 734, 463, 100, 678, 367, 393, 327, 402, 149, 930, 968, 778, 296, 741, 391, 514, 599, 602, 374, 213, 629, 898, 720, 611, 431, 410, 402, 147, 238, 998, 090, 962, 818, 915, 890, 645, 693, 934, 483, 330, 994, 169, 632, 295, 877, 995, 848, 993, 366, 747, 014, 871, 763, 494, 805, 549, 996, 163, 051, 541, 225, 403, 465, 297, 007, 721, 146, 231, 355, 704, 081, 493, 098, 663, 065, 733, 677, 191, 172, 853, 987, 095, 748, 167, 816, 256, 084, 212, 823, 380, 168, 625, 334, 586, 431, 254, 034, 670, 806, 135, 273, 543, 270, 714, 478, 876, 861, 861, 983, 320, 777, 280, 644, 806, 691, 125, 713, 197, 262, 581, 763, 151, 313, 596, 429, 547, 763, 576, 367, 837, 019, 349, 835, 178, 462, 144, 294, 960, 757, 190, 918, 054, 625, 114, 143, 666, 384, 189, 433, 852, 576, 452, 289, 347, 652, 454, 631, 535, 740, 468, 786, 228, 945, 885, 654, 608, 562, 058, 042, 468, 987, 372, 436, 921, 445, 092, 315, 377, 698, 407, 168, 198, 376, 538, 237, 748, 614, 196, 207, 041, 548, 106, 379, 365, 123, 192, 817, 999, 006, 621, 766, 467, 167, 113, 471, 632, 715, 481, 795, 877, 005, 382, 694, 393, 400, 403, 061, 700, 457, 691, 135, 349, 187, 874, 888, 923, 429, 349, 340, 145, 170, 571, 716, 181, 125, 795, 888, 889, 277, 495, 426, 977, 149, 914, 549, 623, 916, 394, 014, 822, 985, 025, 331, 651, 511, 431, 278, 802, 009, 056, 808, 456, 506, 818, 877, 266, 609, 831, 636, 883, 884, 905, 621, 822, 262, 933, 986, 548, 645, 669, 080, 672, 191, 704, 740, 408, 891, 349, 835, 685, 662, 428, 063, 231, 198, 520, 436, 826, 329, 415, 290, 752, 972, 798, 343, 429, 446, 509, 992, 206, 368, 781, 367, 154, 091, 702, 655, 772, 727, 391, 329, 424, 277, 529, 349, 082, 600, 585, 884, 766, 523, 150, 957, 417, 077, 831, 910, 016, 168, 475, 685, 658, 673, 192, 860, 882, 070, 179, 760, 307, 269, 849, 987, 354, 836, 042, 371, 734, 660, 257, 694, 347, 235, 506, 301, 744, 118, 874, 141, 292, 438, 958, 141, 549, 100, 609, 752, 216, 882, 230, 887, 611, 431, 996, 472, 330, 842, 380, 137, 110, 927, 449, 483, 557, 815, 037, 586, 849, 644, 585, 749, 917, 772, 869, 926, 744, 218, 369, 621, 137, 675, 101, 083, 278, 543, 794, 081, 749, 094, 091, 043, 084, 096, 774, 144, 708, 436, 324, 279, 476, 892, 056, 200, 427, 227, 961, 638, 669, 149, 805, 489, 831, 121, 244, 676, 399, 931, 955, 371, 484, 012, 886, 360, 748, 706, 479, 568, 669, 048, 574, 782, 855, 217, 054, 740, 113, 945, 929, 622, 177, 502, 575, 565, 811, 067, 452, 201, 448, 981, 991, 968, 635, 965, 361, 551, 681, 273, 982, 740, 760, 138, 899, 638, 820, 318, 776, 303, 668, 762, 730, 157, 584, 640, 042, 798, 880, 691, 862, 640, 268, 612, 686, 180, 883, 874, 939, 573, 818, 125, 022, 279, 689, 930, 267, 446, 255, 773, 959, 542, 469, 831, 637, 863, 000, 171, 279, 227, 151, 406, 034, 129, 902, 181, 570, 659, 650, 532, 600, 775, 823, 677, 398, 182, 129, 087, 394, 449, 859, 182, 749, 999, 007, 223, 592, 423, 334, 567, 850, 671, 186, 568, 839, 186, 747, 704, 960, 016, 277, 540, 625, 331, 440, 619, 019, 129, 983, 789, 914, 712, 515, 365, 200, 336, 057, 993, 508, 601, 678, 807, 687, 568, 562, 377, 857, 095, 255, 541, 304, 902, 927, 192, 220, 184, 172, 502, 357, 124, 449, 911, 870, 210, 642, 694, 565, 061, 384, 919, 373, 474, 324, 503, 966, 267, 799, 038, 402, 386, 781, 686, 809, 962, 015, 879, 090, 586, 549, 423, 504, 699, 190, 743, 519, 551, 043, 722, 544, 515, 740, 967, 829, 084, 336, 025, 938, 225, 780, 730, 880, 273, 855, 261, 551, 972, 044, 075, 620, 326, 780, 624, 448, 803, 490, 998, 232, 161, 231, 687, 794, 715, 613, 405, 793, 249, 545, 509, 528, 052, 518, 010, 123, 087, 258, 778, 974, 115, 817, 048, 245, 588, 971, 438, 596, 754, 408, 081, 313, 438, 375, 502, 988, 726, 739, 523, 375, 296, 641, 615, 501, 406, 091, 607, 983, 229, 239, 827, 240, 614, 783, 252, 892, 479, 716, 519, 936, 989, 519, 187, 808, 681, 221, 191, 641, 747, 710, 902, 480, 633, 491, 091, 704, 827, 441, 228, 281, 186, 632, 445, 907, 145, 787, 138, 351, 234, 842, 261, 380, 074, 621, 914, 004, 818, 152, 386, 666, 043, 133, 344, 875, 067, 903, 582, 838, 283, 562, 688, 083, 236, 575, 482, 068, 479, 639, 546, 383, 819, 532, 174, 522, 502, 682, 372, 441, 363, 275, 765, 875, 609, 119, 783, 653, 298, 312, 066, 708, 217, 149, 316, 773, 564, 340, 379, 289, 724, 393, 986, 744, 139, 891, 855, 416, 612, 295, 739, 356, 668, 612, 658, 271, 234, 696, 438, 377, 122, 838, 998, 040, 199, 739, 078, 061, 443, 675, 415, 671, 078, 463, 404, 673, 702, 403, 777, 653, 478, 173, 367, 084, 844, 734, 702, 056, 866, 636, 158, 138, 003, 692, 253, 382, 209, 909, 466, 469, 591, 930, 161, 626, 097, 920, 508, 742, 175, 670, 306, 505, 139, 542, 860, 750, 806, 159, 835, 357, 541, 032, 147, 095, 084, 278, 461, 056, 701, 367, 739, 794, 932, 024, 202, 998, 707, 731, 017, 692, 582, 046, 210, 702, 212, 514, 120, 429, 322, 530, 431, 789, 616, 267, 047, 776, 115, 123, 597, 935, 404, 147, 084, 870, 985, 465, 426, 502, 772, 057, 300, 900, 333, 847, 905, 334, 250, 604, 119, 503, 030, 001, 704, 002, 887, 892, 941, 404, 603, 345, 869, 926, 367, 501, 355, 094, 942, 750, 552, 591, 581, 639, 980, 523, 190, 679, 610, 784, 993, 580, 896, 683, 299, 297, 681, 262, 442, 314, 008, 657, 033, 421, 868, 094, 551, 740, 506, 448, 829, 039, 207, 316, 711, 307, 695, 131, 892, 296, 593, 509, 018, 623, 094, 810, 557, 519, 560, 305, 240, 787, 163, 809, 219, 164, 433, 754, 514, 863, 301, 000, 915, 916, 985, 856, 242, 176, 563, 624, 771, 328, 981, 678, 548, 246, 297, 376, 249, 530, 251, 360, 363, 412, 768, 366, 456, 175, 077, 031, 977, 457, 534, 912, 806, 433, 176, 539, 995, 994, 343, 308, 118, 470, 147, 158, 712, 816, 149, 394, 421, 276, 614, 228, 262, 909, 950, 055, 746, 981, 053, 206, 610, 001, 560, 295, 784, 656, 616, 193, 252, 269, 412, 026, 831, 159, 508, 949, 671, 513, 845, 195, 883, 217, 147, 982, 748, 879, 261, 851, 417, 819, 979, 034, 417, 285, 598, 607, 727, 220, 866, 677, 680, 426, 090, 308, 754, 823, 803, 345, 446, 566, 305, 619, 241, 308, 374, 452, 754, 668, 143, 015, 487, 710, 877, 728, 011, 086, 004, 325, 892, 262, 259, 413, 968, 285, 283, 497, 045, 571, 062, 757, 701, 421, 761, 565, 262, 725, 153, 407, 407, 625, 405, 149, 931, 989, 494, 459, 106, 414, 660, 534, 305, 378, 576, 709, 862, 520, 049, 864, 880, 961, 144, 869, 258, 603, 473, 714, 363, 659, 194, 013, 962, 706, 366, 851, 389, 299, 692, 869, 491, 805, 172, 556, 818, 508, 298, 824, 954, 954, 815, 796, 063, 169, 517, 658, 741, 420, 159, 798, 754, 273, 428, 026, 723, 452, 481, 263, 569, 157, 307, 213, 153, 739, 781, 041, 627, 653, 715, 078, 598, 504, 154, 797, 287, 663, 122, 946, 711, 348, 158, 529, 418, 816, 432, 825, 044, 466, 692, 781, 137, 474, 494, 898, 385, 064, 375, 787, 507, 376, 496, 345, 148, 625, 306, 383, 391, 555, 145, 690, 087, 891, 955, 315, 994, 462, 944, 493, 235, 248, 817, 599, 907, 119, 135, 755, 933, 382, 121, 706, 191, 477, 185, 054, 936, 632, 211, 157, 222, 920, 331, 148, 502, 487, 563, 303, 118, 018, 805, 685, 073, 569, 841, 580, 518, 118, 710, 778, 653, 953, 571, 296, 014, 372, 940, 865, 270, 407, 021, 924, 383, 167, 290, 323, 231, 567, 912, 289, 419, 486, 240, 594, 039, 074, 452, 321, 678, 019, 381, 871, 219, 092, 155, 460, 768, 444, 573, 578, 559, 513, 613, 304, 242, 206, 151, 356, 457, 513, 937, 270, 939, 009, 707, 237, 827, 101, 245, 853, 837, 678, 338, 161, 023, 397, 586, 854, 894, 230, 696, 091, 540, 249, 987, 907, 453, 461, 311, 923, 963, 852, 950, 754, 758, 058, 205, 625, 956, 600, 817, 743, 007, 191, 746, 812, 655, 955, 021, 747, 670, 922, 460, 866, 747, 744, 520, 875, 607, 859, 062, 334, 750, 627, 098, 328, 593, 480, 067, 789, 456, 169, 602, 494, 392, 813, 763, 495, 657, 599, 847, 485, 773, 553, 990, 957, 557, 313, 200, 809, 040, 830, 036, 446, 492, 219, 409, 934, 096, 948, 730, 547, 494, 301, 216, 165, 686, 750, 735, 749, 555, 882, 340, 303, 989, 874, 672, 975, 455, 060, 957, 736, 921, 559, 195, 480, 815, 514, 035, 915, 707, 129, 930, 057, 027, 117, 286, 252, 843, 197, 413, 312, 307, 617, 886, 797, 506, 784, 260, 195, 436, 760, 305, 990, 340, 708, 481, 464, 607, 278, 955, 495, 487, 742, 140, 753, 570, 621, 217, 198, 252, 192, 978, 869, 786, 916, 734, 625, 618, 430, 175, 454, 903, 864, 111, 585, 429, 504, 569, 920, 905, 636, 741, 539, 030, 968, 041, 471 if bc is correct.
↑This assumes we are using a similar version of Python.
↑There is a different issue regarding whether all possible outcomes are achievable.
↑
- Maximal Length of List to Shuffle with Python random.shuffle?
- “If your list is such that there are 2**19937 or more permutations, some of these will never be obtained by shuffling the list. You’d (again, conceptually) generate all permutations of the list, then generate a random number x, and pick the xth permutation. Next time, you generate another random number y, and pick the yth permutation. And so on. But, since there are more permutations than you’ll get random numbers (because, at most after 2**19937-1 generated numbers, you’ll start getting the same ones again), you’ll start picking the same permutations again.”
- Mersenne twister at Wikipedia
- “The Mersenne twister is a pseudorandom number generator developed in 1997 by Makoto Matsumoto and Takuji Nishimura. It provides for fast generation of very high-quality pseudorandom numbers, having been designed specifically to rectify many of the flaws found in older algorithms.”
- Pseudorandom number generator at Wikipedia
- “Careful mathematical analysis is required to have any confidence a pseudorandom number generator generates numbers that are sufficiently ‘random’ to suit the intended use.”
- Python random
- “This module implements pseudo-random number generators for various distributions.”
- Python: Random is barely random at all?
- “The Birthday Paradox applies where given value can occur more than once during the period of the generator—and therefore duplicates can happen within a sample of values. The effect of the Birthday Paradox is that the real likelihood of getting such duplicates is quite significant and the average period between them is smaller than one might otherwise have thought. This dissonance between the perceived and actual probabilities makes the Birthday Paradox a good example of a cognitive bias, where a naive intuitive estimate is likely to be wildly wrong.”
- Scripting Persistence of Vision
- POV’s built-in scripting feature allows you to both simplify your scene creation and make much more complex scenes. Using scripting to make complex and redundant three dimensional objects.
More Programming for Gamers
- Are my dice random?
- My d20 appears to have been rolling a lot of ones, a disaster if I were playing D&D but a boon for Gods & Monsters. Is my die really random, or is it skewed towards a particular result? Use the ‘R’ open source statistics tool to find out.
- Programming for Gamers: Choosing a random item
- If you can understand a roleplaying game’s rules, you can understand programming. Programming is a lot easier.
- Easier random tables
- Rather than having to type --table and --count, why not just type the table name and an optional count number?
- Programming a Roman thumb
- Before we move on to more complex stuff with the “random” script, how about something even simpler? Choose or die, Bezonian!
- Multiple tables on the same command
- The way the “random” script currently stands, it does one table at a time. Often, however, you have more than one table you know you’re going to need. Why not use one command to rule them all?
- 12 more pages with the topic Programming for Gamers, and other related pages
More randomness
- While sorrowful dogs brood: The TRS-80 Model 100 Poet
- Random poetry: a BASIC random primer and a READ/DATA/STRINGS primer, for the TRS-80 Model 100.
- Are my dice random?
- My d20 appears to have been rolling a lot of ones, a disaster if I were playing D&D but a boon for Gods & Monsters. Is my die really random, or is it skewed towards a particular result? Use the ‘R’ open source statistics tool to find out.