One of the first things I want to talk about is randomization.
And this is a concept that we're super familiar with.
Randomization is really, really important when we want to create computer programs that have a degree
of unpredictability.
Now the biggest category of that is of course, games.
Right?
Can you imagine if you had to play Tetris,
and every single time the block that fell down was like predictable? You always knew that it was going
to be a T, and then it was going to be an L, like there would be no fun in that, right?
So how do we create this randomness for our programs? Now in nature and in our everyday lives,
it's really easy to create randomness.
If you splash some paint on a canvas, that's going to be pretty random.
If you take a look at the TV static from the analog TVs, that's also a whole bunch of randomness there.
But when we're talking about computers, then these machines are what we would call deterministic.
They will perform repeatable actions in a fully predictable way.
So how do we wrangle these machines that operate basically on 1's and 0's to get them to create some
random numbers?
Well, there's a whole bunch of maths that can be applied to create what are called Pseudo Random Number
Generators.
And the one that Python uses is something called the Mersenne Twister.
And if you really want to read about it, then you can have a look on Wikipedia and take a look under
the Algorithmic detail.
But to be honest, it's a little bit too much information for anybody, unless you're really interested
in these types of number generators.
But what I recommend, though, is a free video from the Khan Academy where they explain Pseudorandom
Number Generators and it's really well produced, and it tells you a lot of different aspects of pseudorandom
number generation.
So have a look at that,
if you're interested to learn a little bit more about Pseudorandom Number Generators.
We've talked a lot about randomness, but let's see it in action.
Let's write some code and produce some random numbers.
We know that we want to get hold of some random numbers, but we don't really want to implement the
Mersenne Twister ourselves because it's horrendously complicated, using lots and lots of maths,
and probably it'll take us months to write the code for.
So how can we get random numbers?
If you think about it, it's a little bit like searching for something in Google.
The Google algorithm is horrendously complicated, and it's taken them years and years to refine and
perfect to such an extent where I think Google knows me better than myself,
because when I search for something, it always gives me the most relevant results.
And if you're questioning the complexity of the Google algorithm, just go to Bing and try searching
for something and see how relevant the results are.
So they have this super complex algorithm, which we're never going to get a chance to see, let alone
understand.
And yet, every day we're able to search things on Google and use this Google algorithm to get the things
that we want, like the air speed velocity of a laden swallow.
Now we can do the same thing when we want to generate random numbers.
Because this is such frequently needed functionality, the Python team have already created a Random
Module, and you can get a direct link to this documentation just by clicking on the link in the Description
box here. And you can see that it contains a whole bunch of functions that you can use that allows
you to generate random integers.
So random whole numbers, or random floating point numbers.
So let's see how we can tap into this Random Module.
We first have to import it.
So we write, "import random".
And now we're able to use this Random Module in our Python code.
So we can look into the documentation to figure out how we can generate random whole numbers,
to random integers using this Random Module from Python.
So if you scroll down on this documentation file and you go to the section, Functions for integers,
then you can see a whole bunch of different things that we can do to generate random whole numbers.
Now the one that we're going to look into is random.randint().
And this is going to generate a random integer between a and b.
And these are the numbers that we have to provide to set that range.
So the way that randint(a, b) works is that it will generate a random number that is greater than or equal
to a and less than or equal to b.
So basically, between this range a and b that we provide, but inclusive of both a and b.
So let's give it a go.
If we go into our code file and we use the Random Module.
So we first write, "random".
This refers to this module that we just imported which contains all the code for generating random numbers.
And then we write a full stop, which in programming is normally called a dot.
So random.
and then we're going to use the name of that function that we found, randint.
So this randint needs some open parentheses,
and you can see from the code hints it's asking us for a which is an integer, and b which is an integer,
and we already know this is the range that we want.
So let's go ahead and give it a range.
And I want a random whole number between 1 and 10.
So in this case I've got 1 as the first starting value.
So it's going to give me a random number that could include 1,
and then it goes up to 10 and including 10.
So it means that we will get random numbers between 1 and 10.
So let's go ahead and save this into a variable which we'll call, random_integer.
And now I'm going to print this random_integer.
And every single time I run it, you'll notice that I'll get a different number,
or maybe sometimes the same, but it's a random number between the range that I've specified.
Now, I've mentioned that the Random M odule is a Python module.
So what exactly is a module?
Well, you've seen that we've mostly been writing our code all on the same page in a sort of script
style,
right?
And everything just kind of gets executed from top to bottom.
But sometimes your code will get so long because you're trying to create something complicated, and
it's no longer possible to understand what's going on in such a large piece of code.
What people will do in that case is to split the code up into individual modules, where each module
is responsible for a different bit of functionality of your program, and if you have a complex project
with many, many modules, then you could have collaboration on your project, right?
Lots of people could be working on it, each person working on a different thing. Say if a factory was
building a car, it wouldn't make sense for one person to build the entire car from wheels to axles
to the chassis.
That's crazy.
Instead, you have different people working on different modules, like the Tire module, or the Chassis
Module, or the Engine Module, and then once they put everything together, you end up with the final
car or in our case, the final program.
So we know that the Random Module is a module that the Python team created to make it easier for us
to generate our random numbers without needing to get into the complexities of all of the math that's
required to Generate Pseudorandom Numbers.
But how can we create our own modules, and how do modules work anyways?
Well, it's actually very simple.
If you open up the file inspector over here and you go into today's task, Random Module and you right-click
click on it, you can go into New and we're going to create a new Python file.
Now we're going to create our Python file and call it my_module, so that I can show you how to create
your very own module.
Now let's say inside my_module, I decide to create a variable called my_favorite number,
and it happens to be 3.1415.
Now, if I want to use this number that I've created in my_module, all I have to do is to go back into
the task.py, which is the main entry point, at least in this course format.
And normally when you're writing code in Python, it might be called something like main.py or app.py,
but either way, there's usually a primary file that you use where you write most of your starting and
core code.
Now, if I want to use the code in my_module, all I have to do is, same as the Random Module,
I have to import it.
So I'll import my_module into this task.py file.
And then I can use it by simply printing,
for example, my_module.my_favorite_number.
And you can see it's exactly the same way as the Random Module,
we imported it and then we used a function inside it called, randint.
And now if I go ahead and print, you can see the,
my_favorite_number is printed by using and tapping into this module.
Now coming back to our main task of generating random numbers, let's take a look how we can generate
random floating point numbers.
So I'm going to go ahead and delete my module and all the related code.
You can keep yours if you want just for revision, but I want to keep everything as clean as possible.
So I'm also going to comment out this section where we've got the random_integer as well.
So how do we create random floating point numbers?
Well, let's take a look at the documentation.
If you scroll down to this area called Real-valued distributions, you can see that one way of generating
random floating point numbers is using the random.random() function.
And this is one of the core functions that we use a lot when we're dealing with random numbers in Python,
because it is one of the most adaptive functions.
It generates a random floating point number between 0 and 1.
Now if you look at these symbols, you'll see that the random number that's generated can be greater
or equal to zero.
So that means zero is included in the possibility,
but it is less than one.
So one is not um in the generated random numbers.
Now let's try that out.
So we will create a random_number_0_to_1.
And this is going to be set to random.random.
And notice that it's actually exactly the same, spelled the same, same casing,
but the first one refers to the Module, and when you hover over it, it should show you it's the Module
random, and the second one refers to the function random.
And this is something that doesn't take any inputs,
and all you need to do is to add an empty set of parentheses to activate it.
When we're looking at functions such as the randint(), we always see a set of parentheses, and it's
been used to put in inputs like what to print or what is the prompt for input, or what is the range
for a random integer,
but remember that some functions don't take any inputs at all, but they still need the set of parentheses
in order to carry out the action that the function is supposed to perform.
So let's go ahead and print this new random_number_0_to_1, and you will see that every time I press
the print button, it generates a new floating point number,
and it's between 0 inclusive and up to 1, not inclusive.
So you can use this random number generator, random.random() to expand it.
So what do I mean by that?
If we multiply this by 10, then you'll see that the numbers that are generated are going to be between
0 to 10 and not inclusive of 10, because we're basically multiplying the lower range 0 by 10,
which is 0, and the upper range by 10, 1 * 10 is 10.
So then we've shifted this random number generator to create floating point numbers between 0 and
whatever it is that we desire.
So you can have a play around with that, but you might also discover another function if you look through
the documentation, which is a Random Floating Point Number Generator.
So I'm going to call this variable, random_float.
And I'm going to use the random.uniform() function.
And this function will return a random floating point number such that the number is greater than or
equal to the lower bound and less than or equal to the upper bound.
So basically it's going to generate numbers between a and b, inclusive of a and b.
So to use that we would say, random.uniform()
and then we can provide a number say 1 to 10 again.
And then let's print this random_float...and let me comment out the other part.
And now when I run this you can see it almost does the same thing.
The only difference is it's not entirely sure depending on rounding, whether if you will get 10 or
not in the random_float.
But if you want to be really sure of the range,
so this is known as a semi-open range, where you have random.random() that always includes 1 but may
never include 1, and you can multiply it and do whatever you like with it reliably knowing that.
But because this one can vary depending on the rounding of the upper bound, then you may get the upper
bound,
you may not.
So these are all really minute differences.
I recommend to use random.random() if you can, and you understand how it works to extend the range.
Now it's time to do some practice and see what you can actually do with random numbers,
and I want you to create a heads or tail program.
So the idea is that you should be able to comment out all the existing code if you have any, or delete
it if you wish.
And you're going to create a program that either prints out, "Heads", or it prints out "Tails", and whether
if it prints out Heads or Tails will entirely depend on a random number that you generate.
So think about how you can do that.
Pause the video and give this a go.
All right.
So how do we do this?
Well we need some conditions, right?
We need to know that we can generate two random numbers;
one will represent heads, and one will represent tails.
Now if we want the numbers to occur at the same rate then we could for example, generate a random number
that is either 1 or 0.
So how can we do that?
Well, we can use this randint() function that we learned earlier.
So making sure that we've already got "import random" written up here.
And don't worry if it goes gray, it does that because it's not being used.
We can now use it.
And you can see it turns it into normal syntax highlighting color.
And then we can say random.randint(),
and we want a random number between 0 and 1.
So therefore we're going to get 0,
or we're going to get 1,
and we're going to set this to equal our random_heads_or_tails.
Now you can of course call this whatever it is you wish,
but essentially what we have here is a random number that is either 1 or 0.
And we can verify this just by running this a few times.
And you can see it's 1 or it's 0.
So now instead of printing it out let's use an if statement and check if the random_heads_or_tails is
equal to 0,
well in that case, let's go ahead and print, "Heads",
but otherwise the only other option is Tails.
So let's go ahead and put print("Tails") into the else statement.
So when it's 0 it's going to print "Heads" for any other situation which is only 1,
then it's going to print "Tails".
So now let's hit Run.
You can see we have now a random coin toss.
So because it's random you might see the same thing occur lots of times,
but hopefully over a hundred times you will see it's roughly a 50/50 split.
So I hope you had fun creating this program using your newfound knowledge.
And once you're ready, head over to the next lesson and we'll continue the day's lessons.