## Random numbers in Python

One of the most important topics in today’s science and computer simulation is random number generation and Monte Carlo simulation methods. In the simplest scenario for your research, you may need to generate a sequence of uniformly distributed random numbers in Python. There are several approaches to handle such random number generation problems in Python. Here is one, via Python’s standard `random`

module:

```
import random as rnd
rnd.random() # generates a random number in the half open interval [0,1)
```

```
0.012519922307372311
```

```
rnd.<press tab>
rnd.BPF rnd.Random rnd.betavariate rnd.gauss rnd.normalvariate rnd.randrange rnd.shuffle rnd.weibullvariate
rnd.LOG4 rnd.SG_MAGICCONST rnd.choice rnd.getrandbits rnd.paretovariate rnd.sample rnd.triangular
rnd.NV_MAGICCONST rnd.SystemRandom rnd.expovariate rnd.getstate rnd.randint rnd.seed rnd.uniform
rnd.RECIP_BPF rnd.TWOPI rnd.gammavariate rnd.lognormvariate rnd.random rnd.setstate rnd.vonmisesvariate
```

As you see in the list of available methods in `random`

, you can generate random numbers from a wide variaty of univariate probability distributions, e.g.,

```
rnd.betavariate(0.5,0.5) # Beta variate with the input parameters
```

```
0.9281984408820623
```

```
rnd.expovariate(1) # random variable from exponential distribution with mean 1.
```

```
2.546912414260747
```

```
rnd.gammavariate(1,1) # random variable from gamma distribution with parameters 1,1.
```

```
0.5364897808236537
```

Recall that if you need help on a method or function in Python, you could use `help()`

,

```
help(rnd.weibullvariate)
```

```
Help on method weibullvariate in module random:
weibullvariate(alpha, beta) method of random.Random instance
Weibull distribution.
alpha is the scale parameter and beta is the shape parameter.
```

To generate `float`

random numbers between the given input bounds,

```
rnd.uniform(50,100) # generate a random float between 50 and 100
```

```
65.59688328558263
```

**Warning:**Always make sure to import modules with unique names since different modules with similar component names may overwrite each other’s objects and methods in your active session of Python. For example

`import random`

followed by `from numpy import *`

will cause the `random`

module to be overwritten by `numpy.random`

module.Also pay attention to sublte differences between similar functions, with the same names, but in different modules. For example,

```
import numpy as np
np.random.randint(1,6,1)
```

will draw a random integer from the interval $[1,6)$ excluding the value $6$ (the third input, $1$, indicates how many numbers has to be drawn randomly by the function). However,

```
import random as rnd
rnd.randint(1,6)
```

will draw a random integer form the interval $[1,6]$. Also note that `randint()`

from module `random`

is a scalar function, whereas the numpy’s version is vectorized.

## The deterministic aspect of randomness in Python

There is a truth about random numbers and random number generators and algorithms, not only in Python, but in all programming languages, and that is, **true random numbers do not exist in the world of programming**. What we call a sequence of random numbers, is simply a sequence of numbers that we, the user, to the best of our knowledge, don’t know how it was generated, and therefore, **the sequence looks random to us, but not the to the developer of the algorithm!**. To prove this, type the following code in a Python session,

```
import numpy as np
np.random.seed(42)
np.random.randint(1,6,6)
```

```
array([4, 5, 3, 5, 5, 2])
```

```
np.random.randint(1,6,6)
```

```
array([3, 3, 3, 5, 4, 3])
```

```
np.random.seed(42)
np.random.randint(1,6,6)
```

```
array([4, 5, 3, 5, 5, 2])
```

You notice that every time the random function is called, it generates a new sequence of random numbers, apparently completely random. But as soon as the function `np.random.seed(42)`

is called, it appears that the random number generator also restarts from the beginning, generating the same sequence of random numbers as it did before.

You can even test the same code on a different computer, and as long as you set the seed of the random number generator to a specific value (here 42), `np.random.seed(42)`

, you will the same sequence of random numbers. So after all, random numbers are not random at all, as they can be generated deterministically, however, they mimic the behavior of true random numbers. The ability to set the seed for a random number generator is actually very useful since it enables us to replicate the work of code, exactly it has been done in the past. In particular, this is very useful for code debugging. However, beware of cases where you need to get a different result, every time you run the code. If you set the random seed of the random generator to a fixed value, right at the beginning of the code, you will never get a random behavior.

## Drawing a random element from a list

Suppose you have the following list,

```
import numpy as np
mylist = np.linspace(0,100,51)
mylist
```

```
array([ 0., 2., 4., 6., 8., 10., 12., 14., 16.,
18., 20., 22., 24., 26., 28., 30., 32., 34.,
36., 38., 40., 42., 44., 46., 48., 50., 52.,
54., 56., 58., 60., 62., 64., 66., 68., 70.,
72., 74., 76., 78., 80., 82., 84., 86., 88.,
90., 92., 94., 96., 98., 100.])
```

and now you wanted to draw a random element from the above list. You could do,

```
import random as rnd
rnd.choice(mylist)
```

```
80.0
```

This will give a random element from the list. You could also generate a random shuffling of the list by,

```
import random as rnd
rnd.shuffle(mylist)
mylist
```

```
array([ 98., 12., 76., 60., 46., 22., 24., 92., 66.,
16., 6., 34., 14., 8., 18., 50., 30., 74.,
4., 2., 38., 90., 70., 56., 94., 80., 32.,
20., 10., 44., 72., 84., 0., 78., 100., 88.,
86., 96., 48., 52., 62., 64., 26., 36., 40.,
54., 68., 58., 82., 42., 28.])
```

## Summary of some important random functions in Python

As you may have noticed, since none of the random functions are builtin, things can get really confusing very easily, by simply mixing numpy’s random module with Python’s random module. The following helps to clarify some of the most important differences between these two modules.

Purpose | random module | numpy.random module |
---|---|---|

random uniform numbers in `[0,1)` |
`random()` |
`random(N)` (vectorized) |

random uniform numbers in `[a,b)` |
`uniform(a,b)` |
`uniform(a,b,N)` (vectorized) |

random integers in `[a,b]` |
`randint(a,b)` |
`randint(a,b+1,N)` (vectorized) `random_integers(a,b+1,N)` (vectorized) |

random Gaussian deviate with parameters `[\mu, \sigma]=[m,s]` |
`gauss(m,s)` |
`normal(m,s,N)` (vectorized) |

setting random number generator seed `i` |
`seed(i)` |
`seed(i)` |

shuffling list mylist | `shuffle(mylist)` |
`shuffle(mylist)` |

choose a random element from mylist | `choice(mylist)` |
-- |