After learning more about numbers it is time to investigate the most elementary part of mathematics, arithmetic. Arithmetic consists of the study of the more fundamental properties of numbers as well as how they work under the operations of addition, subtraction, multiplication, and division but it's not limited to those. Since I and hopefully everyone reading this already know all the basic mathematical operations on integers we are going to divide our integers into fractions to view them in a new light that can show us a new way of working with numbers.
While I restrain myself from calling any of these operations basic they are on the easier side of math to get the hang of. As a developer, I would say you really would need to have a good grasp of what we will go through in this post as it acts as a very powerful toolkit for us developers when developing software which we will quickly notice as we get our hands dirty.
Fractions
While we did go through fractions in the previous post it is often something that not I get stuck on or have forgotten the details of over the years. Starting things off we will examine them in greater detail.
A fraction is interesting, it is like an instruction telling you to divide and like so: .
Multiplication of fractions
To multiply two fractions, all you have to do is to multiply the numerators followed by multiplying the denominators:
When multiplying with an integer remember that any integer is a fraction with the denominator of 1 meaning that for example this means that when you’re multiplying a fraction with an integer you can simply just multiply the integer with the numerator and leave the denominator alone.
A common way of visualizing this is to draw rectangles for each of the two fractions, one as rows and one as columns.
How this works is that you first draw your two fractions, one as rows and one as columns, I have highlighted columns in one and rows in the other. Then what happens is that I’ve drawn a third rectangle where I highlighted the overlapping boxes in black, those are our numerator while our denominator will be the total number of boxes.
Division of fractions
If we take the example of dividing with you first want to flip so that it becomes then simply perform multiplication on them like so:
You can use the same on-paper method displayed above by flipping the values of the second fraction first.
This may seem weird at first but there is a logical explanation for it. If you first think of dividing an integer by 2. You are taking half of the number, which is the same as multiplying the integer with .
If you then attack if from the other side and think of dividing a number by , what you are doing is finding out how many halves that go into that number. Since the number of 1s in a number is the same as the number itself it means dividing a number by (finding out how many times 0.5 fits in the number) means you’re multiplying it by 2.
Addition and subtraction of fractions
Both addition and subtraction are not as simple as division and multiplication. As long as you have the same denominator you are free to both subtract and add to the fractions as you please, you just leave the denominator alone and perform normal addition or subtraction on the numerator.
However, if you are dealing with fractions of different denominators you will stumble into problems because the units you are trying to add or subtract are different.
The interesting part is that every fraction can be represented in an infinite number of ways so if you multiply the numerator and denominator by any number you are still left with the same fraction. This works because every number divided by itself is 1.
For example both and is equal to 1. With this knowledge, let’s say that you want to calculate here it’s much easier if you recognize that simply by multiplying both the numerator and denominator by 2. Now suddenly all you’re left with is a simple addition to turn it into . This technique works even though the two fractions are not as easily converted between each other, for example, . Here you can utilize something known as cross-multiplication where you are multiplying the numerator and denominator of each fraction by the denominator of the opposite fraction. For example:
Why this works should make sense by now, we are making the two fractions have a common denominator. A problem when doing this however is that you often end up with a larger fraction than you want. A way of solving this is by looking deeper into factors.
Factors
To solve the problem of our fractions becoming too big we need to reduce the numbers into their smallest forms and this can be done by looking at the factors of the numbers. A factor is simply an integer that can exactly be divided into another target integer. For example, 1, 2, 4, and 8 are all factors of the number 8.
If we move back a bit and consider the fraction of you may see that both numbers may be divided by 2 to arrive at and here we can divide it by 3 to get . Or we can skip the extra steps and recognize that can be divided by 6 directly. Skipping the extra steps can be done by finding the greatest common divisor also known as GCD that will allow you to find the value to divide the numerator and denominator with in order to reduce the fraction to its smallest form*.*
Greatest common divisor
Finding the GCD can be done in multiple ways, while there are simple ways to find it it was earlier common to find them using the prime factors of two numbers. This is not the optimal approach but it allows us to also touch on the subject of prime numbers hence we will first describe this approach. A prime number is simply a number whose only factors are 1 and itself. This means that the number can only be evenly divided by either 1 or itself, examples are 2, 3, 5, 7, … and so on. Prime numbers are a very deeply studied subject, they are a key subject in for example cryptography.
There are several algorithms to generate a list of prime numbers, a very common one is called “Eratosthenes ‘Sieve” which allows you to generate a list of prime numbers less than some number $N$. To perform this you perform these steps:
- Start with a list of all the numbers in the range of 2 to $N$.
- Take the first number of the list and delete all the numbers in the list that are divisible by this number.
- Remove the same number from the list and add it to the list of primes.
- Repeat this process until the first number of the list is greater than .
- All the remaining numbers are primes.
Step four of this process works because if any of the products are greater than
then the final product will become greater than
. An implementation in C may look something like this:
void
sieve(int *Numbers, int N)
{
int MaxN = floor(sqrt(N));
for (int I = 0; I < MaxN; ++I)
{
if (Numbers[I] != -1)
{
for (int J = 2 * Numbers[I] - 2; J < N; J += Numbers[I])
{
Numbers[J] = -1;
}
}
}
}
If we were to return to the subject of prime factors. A prime factor of a number is as you now might figure out, a prime number that is a factor of . A very interesting property is that every number can be represented as a product of prime numbers, for example, . This is also known as “The Fundamental Theorem of Arithmetic”.
If we are to use these prime factors as a way of getting the GCD of a number we can do so by finding the primes that produce the number we are interested in and using those to divide the numerator and denominator of your fraction. However, this might be very tedious as you would have to identify common primes for both to produce the GCD. For example lets take the number first we would have to identify that and we have in common hence our GCD in this case is and we can reduce the fraction to .
There is a better way that also is much faster and does not involve primes. This method is known as “Euclid’s Algorithm”. Euclid’s algorithm is very interesting as it relies on the observation that if you have three numbers , and such that and if some number is a factor of both and , it must be a factor of as well.
This allows us to construct a different function for getting the by performing this simple number of steps:
- Divide by and get the remainder .
- You now know that for some number .
- If then is a factor of so the result is . Otherwise, repeat the process using the numbers and .
This can very trivially be implemented in C like so:
int gcd(int n, int m) {
int r = n % m;
if (r == 0) {
return m;
}
return gcd(m, r);
}
The Modulo operator
In this example, we are using the modulo operator which allows us to get the remainder of a division. Modulo is a very important tool in your toolbox and we will soon investigate some very useful cases where it can make our lives easier. But first let's briefly talk about how it works.
In very simple terms it is just the remainder of a division, in practice what these means are if we are dividing numbers that are not evenly divided, part of the divided number is discarded as we are working with integers. For me this was most obviously explained by presenting it like this:
5 % 1 = 0 // 5 / 1 = 5. Remainder is 0.
5 % 2 = 1 // 5 / 2 = 2. Remainder is 1.
5 % 3 = 2 // 5 / 3 = 2. Remainder is 2.
5 % 4 = 1 // 5 / 4 = 1. Remainder is 1.
5 % 5 = 0 // 5 / 5 = 1. Remainder is 0.
I hope you can spot the pattern here. Something that I found very interesting when I first understood this was that the result from the modulo operation on the same number almost works in a circular fashion where we can see the number looping around in a way.
Check if number is even or odd
One of the most basic usages for the modulo operator in a programmer's daily life is to perform checks on whether a number is even or odd. This can trivially be done for any number
by
. What happens here is that it will always return either 1 or 0. Even numbers because they are always divisible by 2 will return 0 while odd numbers will always have the remainder of 1. If we were to create a simple C function of this it would look like this:
int
IsEven(int n)
{
return (n % 2 == 0);
}
Cycling through data
Sometimes you only have a fixed number of options or things you want to loop through, this is also a perfect use case for modulo. Let's say we want to organize which plant is going to be watered on which weekday.
char *Weekdays[] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday" };
int NumberOfDays = 5;
int NumberOfPlats = 10;
for (int I = 0; I < NumberOfPlants; ++I)
{
int DayIndex = I % NumberOfDays;
char *Weekday = Weekdays[DayIndex];
printf("Plant #%d should be watered on %s", I, Weekday);
}
Proportions
Something else about fractions that we should consider is looking at the numerator and denominator as a ratio. A proportion by itself is almost the same thing as a fraction but used in very different circumstances. We usually write rations using a colon such as a ratio 4:3 which is equivalent to . Two things are in the ratio of 4:3 if one of the two is times bigger than the other.
In the real world, we can easily see this on paper for example the standard A4 size remains the same proportion if you scale it to double of its size. This is something that we have already covered in this post because scaling up like that is the same as multiplying the numerator and denominator by the same number as the fraction itself remains unchanged.
In game development which I am currently invested in this can be useful when scaling things, for example, if you were to find the new height of an object after its width have been changed:
int
NewHeight(int OriginalWidth, int OriginalHeight, int NewWidth)
{
return (NewWidth * OriginalHeight / OriginalWidth);
}
This type of math allows you to simply map between different sizes of rectangles. And does not only have to be the size proportions but also the coordinates of objects for example. In games this is common when scaling for different resolutions, in fact all monitor resolutions have their own aspect ratios which works in the same way where for example 1920x1440, 1600x1200 and 1440x1080 are all 4:3 aspect ratios.
When talking about rations there are interesting things such as the golden ratio and the Fibonacci sequence that can be interesting to dive into but I will not talk about them in this post.
Something I recently stumbled upon without specifically studying math was when I was working on my UI system for my game engine and was building the slider and scrolling logic. A slider in a user interface is a graphical representation of a range of numbers. On your PC you have a volume slider for controlling the volume or a slider for controlling the brightness of your monitor.
These standard sliders often represent a value between 0 and 100 but are not restricted to. For you to create your slider you first need to know the bounds (the number at the start and the end of the line). When building the user interface system I also have to know the positions of these. You can find the proportion of the slider by finding the size of the total range divided by the total length of the slider itself:
To then get the value represented by a specific point on the slider you can find how far the value has been moved along the slider by multiplying it with the proportion we got from the previous calculation:
In my game engine the real calculation looks something like:
This is very similar to the previous calculation except I do not need the specific proportion to retrieve my value as I normalize it hence I get away with just the range between the min and max value.
For me, this post has been pretty valuable because I got to touch fractions in a completely different way than what I was taught in school. I can see how some problems may be clearer to see if you look at them as fractions instead of whole numbers. Since I wanted to make a series on math from start to finish I do understand that these first posts may seem a bit unorganized and abstract but this will change once we move into algebra in the next post.
If you are interested and want to hear more from me, subscribe to my email list.
You can also follow my work on theese other places:
💻GITHUB 🐦TWITTER 🙏PATREON 🗨 DISCORD 📹 YOUTUBE
Top comments (0)