Computational Thinking
Part 1: Understanding computational thinking
In its most basic definition, computational thinking is a problem-solving process. Much like with design thinking, the scientific method, and other similar methods, there are a number of steps we go through to find solutions. For example, the scientific method has seven steps. Please keep in mind that there are multiple interpretations of the scientific method out there and some differ in terms of the number of steps. For the purposes of this discussion, we will use these seven steps:
- Question
- Hypothesis
- Materials
- Experiment
- Results
- Conclusion
- Communication of findings
The establishment of the scientific method is a highly debated topic, but most researchers agree that it dates back to the 10th century.
The scientific method made it possible to observe the natural world, create hypotheses to test observations, and develop tests and results through an established process. The method itself has some basis in philosophers such as Plato and Aristotle, who promoted empirical research. However, their methodology was not as developed as what we call the scientific method today.
The computational thinking elements are similar to the scientific method. Computational thinking uses fewer steps to tackle problems associated with programming, where the scientific method is used for experiments. With computational thinking, we generalize the algorithms, while in the scientific method, we are able to reproduce results and generalize the conclusions from samples to populations.
In modern times, we have developed other methodologies depending on the fields of study we pursue and technologies we have developed. Two examples of that are the design thinking process and computational thinking.
Design thinking has five steps or stages:
- Empathize
- Define
- Ideate
- Prototype
- Test
We use these aforementioned stages to understand the needs of a client, class, problem, situation, or other circumstance that we need to address. Empathizing with the needs of the user helps us identify and define the problem. The ideation and prototype stages are where we create possible solutions. Testing the possible solutions is the next required step in finding the best possible one. After all the stages, we can go back through the cycle if we need to, since the goal of the design thinking process is not perfection, so additional work can always be carried out. The goal of design thinking is to provide a solution that is effective and plausible for a defined problem. This is not the only viable solution, nor is it perfect.
In computational thinking, we use a similar process that has four elements:
- Decomposition
- Pattern recognition
- Abstraction
- Algorithm design
As with the design thinking process, the problem is not clearly defined in computational thinking. These problems are sometimes referred to as ill-defined. We are presented with a set of circumstances and we define or decompose that problem before we start ideating, or creating possible solutions based on patterns we can see. When we think about the computational thinking process, what we are really doing is trying to figure out how we can get a computer to follow a set of steps in order to solve the problem we have been presented with.
Let’s take a look at a simple computational thinking problem.
Problem 1 - Conditions
Let’s imagine that a raffle at a radio station has contestants select one of two possible winning structures: $250 or the height, in quarters, of the contestant in cash.
A computational thinking problem can be as vague as Problem 1, where no question is even being asked. You are given a set of conditions and it is your job to determine what the problem is and find solutions for that problem you yourself have defined. If you think about it, there is no perfect answer for this problem, but there are ways for you to create conditions that work to determine which option is indeed best, depending on the height of the contestant.
To decompose this problem, we need to look at what is stated and take into consideration what is not stated. We need rules.
Simply stated, a winner will choose a monetary payout: either $250 in cash or the equivalent of their height in quarters. Those things are stated. But what isn’t stated is also important:
- What is the timeline for the raffle? How many winners are there?
- Do we want to track how much we have spent after each contestant has chosen?
- Do we want to use a baseline for comparison purposes?
There are other things that may come to mind, but for now, let’s stick to these questions. We are going to assume that the raffle doesn’t have a set start or end date and that the radio station may choose multiple winners in a given day – or none at all. These are some of the considerations we will look at when figuring out patterns, generalizing them, and designing the algorithms.
Given all the information about payouts, we still do not have a way to figure out when the payout is greater. Is it best to choose the $250? Or is it best to choose the height in quarters? Can we create an algorithm that tells us which option is best somehow? Yes, we can create an algorithm that addresses the entire problem.
The pattern for this problem will always be the same: the amount is set for the cash value and the height of a quarter is set, so we can always use math to figure out what the height in quarters converts to money-wise based on someone’s height.
We can clearly state the winnings based on each choice if we know a few things. This includes the choice of cash or choice of height in quarters . If height in quarters is chosen, we need the following:
- The contestant’s height
- The thickness of the quarter
What happens next is part of both pattern and abstraction . We do not know the choice until each contestant decides, but we can find out what each quarter’s thickness is ahead of time. It will be needed later for our algorithm. Each quarter is approximately 0.069 inches, or 1.75 millimeters, thick.
Looking at our problem, we can state the winnings in two ways. The following expressions included for the height in quarters winnings are mathematical algorithms . They show the steps needed in order to determine the total winnings given the height of the contestant.
Note that in order to use the customary algorithms, height would need to be given in customary units. In order to use the metric algorithm, height would need to be given in metric units. If a contestant chooses the cash, then the total winnings are simply $250. If the contestant chooses the height in quarters, then the algorithms for both customary and metric units are as follows:
- Total winnings (customary): (ℎ ÷ 0.069) × $0.25
- Total winnings (metric): (ℎ ÷ 1.75) × $0.25
I like a gamble that is not high stakes. So, I’m going to say that I want to test this out and use my own height. So instead of taking $250, I choose to find out what my height would be in quarters. I am 5’10” tall. Let’s figure out how many inches that is. Since we have 12 inches in a foot, the algorithm for the total height is as shown:
\[5 x 12 = 60 inches\]But I said I am 5’10”, so we will need to add those 10 extra inches:
\[60 + 10 = 70 inches\]Now, let’s use the mathematical algorithm we defined earlier, (ℎ ÷ 0.069) × $0.25, in order to find out how much I’d win:
\[(70 ÷ 0.069) × 0.25 ≈ 253.62\]I used the ≈ symbol instead of = because ≈ means this is an approximation. Since I rounded off, I wanted to make sure I showed it was the best approximation, not the exact number
Maybe you are done with the problem now, but in computational thinking, we have to go through abstraction and design an algorithm that will apply to all instances. We can create a very simple program that uses simple input from a user, or we can create a more complex program that provides not just the basic total, but maybe the sums, a graphic, or whatever else we find relevant to our scenario and that applies to all cases.
We will be designing those algorithms more once we have learned about each part of the computational thinking process in more depth. We will even go back to this problem to show how to create that algorithm for the computer to run for us. We can create an algorithm that lets us use someone’s height to make a decision about which winnings to use.
Or, as mentioned earlier, we could write a baseline using $250 as the winnings for every contestant and then input what each contestant has chosen in order to see whether we are below or above the $250 baseline. We can aggregate those results, which means to continue adding them to see where we end up once the radio station stops the raffle. We could even have a graphic that shows us where we are over time, if contestants were choosing differently the longer the radio station ran the raffle, and so on.
Part 2: Decomposing problems
Decomposition is the process of breaking down data. It can include a number of processes or steps necessary in order to solve the problem. By decomposing the problem, we can identify the components, or smaller parts, of the problem before we generalize the pattern.
Through decomposition, we can identify and solve one case in order to then generalize those steps to all possible instances of the problem. In order to really understand decomposition, we will need to go back to our problem stated earlier, which, simply stated, is asking the question: Will my height result in more money if I take my height in quarters or should I take a $250 payout? We can state that we want to know one instance and do that problem mathematically one time, such as solving the problem for my own height only. However, we may need the information for other instances. We could create a program that just identifies which option, $250 or your height in quarters, would be best. Or we could take into consideration some of the following scenarios, which would mean a different algorithm:
- We could check the option given the height but also add each item to a list in order to track all decisions.
- We could also need the array and the sum of the elements in that list to track spending throughout the contest.
- We could also compare the sum to a baseline, using $250 as a base for each of the individuals.
- We could also use all of the elements, such as the list, the sum, the comparison, and a visual graphic display to better understand and track the results.
As you can see, the algorithm will depend on what it is exactly we want to track or answer for this problem. Our algorithm could be a simple yes or no type of problem, where we’d just check which option is best, or it could be a much more robust algorithm, with data and visual representations for the data tracked. Now let’s take a look at how we work to find patterns in our problems.
Part 3: Recognizing patterns
Pattern recognition is the process of finding similarities, or patterns, once we go through the decomposition of problems. In Problem 1, we were shown a problem where a contestant would win $250 or choose to take their height in quarters. This will be the same for every contestant. The only difference is that the total value changes depending on the height of the person.
In this section, let’s take a look at a different problem in order to better understand pattern recognition.
Problem 2 - Mathematical algorithms and generalization
Imagine you are preparing a party for a soccer team. This is a community team, so there are always between 12 and 15 children that stop by. You want to place an order for the food you will need. You know it will cost you $12 per child from the catering company you will be using. Now, let’s break down the problem:
- Decomposition: I know we have between 12 and 15 children. We also know there is a cost of $12 per child. Our problem can be thought of as a question: How can we estimate the cost?
- Pattern recognition: You know the number of children, k, is between 12 and 15. You know it is going to cost $12. If I had 5 children, for example, the cost would be $12 × 5 = $60.
- Pattern generalization: The number of children is not known, so we will use the variable k for that unknown value. That way, we can find out the total cost no matter how many children we have. We are generalizing from one case, 5 children, to all cases, k children.
- Algorithm design: We will write the mathematical algorithm for now. The total cost will be given by the equation $12*𝑘 = 𝑇, where T is the total cost and k is the number of children.
As you can see from the preceding problem, pattern recognition is important in order to find a generalized pattern and write our algorithm. Now, let’s look more closely at pattern generalization.
Part 4: Generalizing patterns
Once we have recognized our pattern, we need to go through pattern generalization and abstraction . That is, we want to make sure that the solution we come up with can be used for multiple instances of the problem we have identified. Pattern generalization can be something as simple as writing a basic linear mathematical algorithm, like we did for the cost of a party, where the cost per child was $12. So, the cost for any number k of children would be given by 12k. But pattern generalization can be much more than that.
If we go back to Problem 1, where you could choose $250 or you could choose your height in quarters, our pattern generalization would allow us to check for anyone’s height against the $250 in order to determine whether you would get more money by choosing the cash option or by choosing the quarters.
Abstraction lets us focus on the things we need and discard things we do not need in order to create the best algorithm for our problem. Now, depending on what we decide we need, we can add or remove some conditions.
For example, if I am a contestant, I only really want to know what option gives me more money. I do not care about total wins, who’s choosing $250, who’s choosing height, and so on. But if I’m the radio station, I may want to know the sum, the comparison to the baseline, and much more. I would have to choose that baseline and maybe even graphically show what has happened over time. That is all part of the abstraction process. When you are solving a computational thinking problem, you are also determining what matters and what does not matter to your solution and algorithm.
In the simplest form of this problem, if you are a contestant, you want to know what your best possible case for winnings is. If you choose $250 but your height makes it so that your height in quarters is more than $250, you would want to know. If you are working at the radio station, you may want to track more than just each winning individually. Abstraction allows you to adapt to all cases, from doing one mathematical problem to creating an algorithm that could track all the choices from all the contestants. Let’s look now at how we can create those algorithms.
Part 5: Designing algorithms
First, let’s take a look at Problem 1. Here, we had a situation where you can win $250 or your height in quarters. Assuming it is you who’s competing, you would want to know which option gives you the most in winnings.
Let’s take a look again at our mathematical algorithms from earlier section:
- Total winnings (customary): (ℎ ÷ 0.069) × $0.25
- Total winnings (metric): (ℎ ÷ 1.75) × $0.25
Remember, if you are using your height in customary units, you’ll use the first algorithm. If you are using metric units, you’ll want to adapt the program accordingly.
When we are programming, we need to define our variables. In this case, h is the variable we are using for height. But think about it; your height may not change if you’re an adult, but for the sake of argument, we will assume it won’t always be the same. So, we will need whoever wants to know what the best option is, $250 or their height in quarters, to input their height so that the program will provide them with the answer.
Input is something the user can enter. So, when we define our variable, we are going to ask for input. A good practice in Python and any other language is not to just ask for the input with no guidance. That is, we want to tell the user the question they are answering. For example, I can write the following code to ask a user for their height input:
1
h = input("Enter your height in inches: ")
The preceding code will ask the user to enter some input. It also asks that the user enter the information in inches. If you were using metric units, you would state that instead.
We also saved the information as the variable h. But we haven’t done anything with that variable yet.
We can just do the basic math and print out the value we get based on height:
1
2
3
h = input("Enter your height in inches: ")
total = (int(h)/0.069)*0.25
print(total)
Notice in the preceding snippet that we used int(h) in the definition of the total variable. We converted the h value to an integer so we could perform a mathematical operation using that variable. When we asked for the input, the variable was saved as a string, which is why we need to convert it to be able to use it.
Running the previous code with my height, which is 70 inches, yields the following result:
1
253.62318840579707
It would look much better if we rounded that answer, and Python has a way for us to do that easily. If we adjust the print code shown as follows, our answer will result in 253.62:
1
2
3
h=input("Enter your height in inches: ")
total = (int(h)/0.069)*0.25
print(round(total,2))
But sometimes we want the code to do more. Let’s remove that print command and create some conditions. In the next few lines, we will use the value provided to make some comparisons. For example, we can ask the computer to check some things for us. There are three possibilities:
- Our height could yield exactly the same as $250.
- Our height could yield less than $250.
- Our height could yield more than $250.
Now, I will ask the computer to tell me what to do based on those conditions. We will need an if-elif, else statement for this. These are conditions that we will test in order to receive better output. We will test whether the total is the same as $250. Else, if the total is less than $250, we will want the computer to do something (that is our elif statement). Finally, in all other cases, we will use the else command:
1
2
3
4
5
6
7
8
9
10
h=input("Enter your height in inches: ")
total = (int(h)/0.069)*0.25
total = round(total,2)
if total == 250:
print("Your height in quarters is the same as $250.")
elif total > 250:
total = str(total)
print("Your height in quarters is more than $250. It is $" + total)
else:
print("You're short, so choose the $250.")
As you can see, we have three algorithms that provide us with the same kind of information. One is more robust than the other two, but how complex or simple our algorithm is depends on what we need from it. If you were holding this raffle again later on, you might have forgotten what the algorithm was, how you wrote it, or what everything meant. However, with the last code, you get a lot of information just by running it, which is more helpful than the first two.
Also keep in mind that we ran this as if we were the contestants. While that is helpful, you may want to consider what changes you would make if you were the radio station. You could write some code that saves all the instances that are run so that you can then check and add all the winnings. You could even calculate that sum through code.
Now, let’s take a look at a few more problems and respective algorithms in order to get more comfortable with the computational thinking process.
Part 6: Additional problems
Throughout this section, we are going to take a look at additional problems. For Problem 2, we will go right into the algorithm, as we went through the other steps in the problem earlier in this chapter. The next problem will have the entire computational thinking process as well.
Problem 2 - Children’s soccer party
Earlier in this chapter, we were planning a party for a soccer team, where there was a cost of $12 per child. We stated that the number of children was unknown, so we will use the variable k to designate the unknown quantity. We also stated that we had a mathematical algorithm, T = 12k, that gave us the total cost, T, of k children. Let’s add a condition here. If we had a budget of $200, we’d want to know if we are over, under, or right on that budget.
1
2
3
4
5
6
7
8
k = int(input("How many children are coming to the party? "))
T = 12 * k
if T == 200:
print("You are right on budget, at " + str(T))
elif T <= 200:
print("You are under budget, at " + str(T))
else:
print("You are over budget, at " + str(T))
As you can see, the program provides us with some information about the total and whether we are over or under budget. As with any algorithm, this isn’t the only way we could write the program in order to get this information. Try your hand at different algorithms to solve this simple problem or add a few conditions of your own and code them. Practice and adding conditions will allow you to get more comfortable designing and writing algorithms.
Problem 3 - Savings and interest
Now we have a new problem. A bank pays compound interest at a rate of x% per month. What will be the payout after a number of years if you deposit any given amount?
Let’s decompose this problem. First of all, we know interest is compounded monthly. Let’s talk about compound interest. The interest on an investment is the percentage that is paid out in a time period. Compound interest means that the interest pays out on the initial amount plus interest each time. Compound interest is a pattern . In fact, a formula exists for it.
The thing I do not know is what percentage the bank is paying out, or the amount deposited, or the number of years it’ll be deposited for. So, we will need to write a program that addresses all of the possibilities. This is pattern generalization . What we do know is that the interest is compounded monthly. There is actually a mathematical formula for this:
\[A = P (1 + \frac{r}{n})^{nt}\]Let’s talk about the terms from the preceding equation:
- A is the total amount.
- P is the principal amount, that is, the initial deposit.
- r is the interest rate (keep in mind that for 3%, the interest is written as 0.03, for example).
- n is the number of times interest is compounded per year.
- t is the number of years the deposit goes untouched
Because there is a mathematical algorithm, we can now create a program for this using the formula. However, we will need to make sure whoever runs the program knows what it is we are asking for with regard to all the inputs. We are asking for a few things:
- What amount is being deposited?
- At what rate is the bank paying?
- For how many years will the money be deposited?
We do know the n in the formula. That n is 12 because this is monthly compound interest. That means it will compound 12 times each year. So, n = 12.
The preceding screenshot shows us the Python program for compound interest. Notice the comment, which is preceded by the # symbol. It states that we needed to convert the rate to use the formula. We would have otherwise obtained an incorrect total. In addition, we used float here because we need to use decimals. The integers, or int, would not give us the information we needed. Also, we rounded the total to two decimal places. That is because we use two decimal places when we talk about money.
1
2
3
4
5
6
7
8
9
P = float(input("How much are you planning on depositing? "))
r = float(input("At what monthly compound rate will it be paid out? "))
t = float(input("How many years will the money be deposited?"))
#Convert the rate to a decimal for the formula by dividing by 100
r = r/100
A = P * (1 + r/12)**(12*t)
A = round(A, 2)
print("Total after " + str(t) + " years: ")
print(A)
Using the code from the compound interest algorithm, we can run any possible instance for compound interest if we have the initial amount, the rate, and the number of years we will be depositing the amount for. The output of the program given an initial deposit of $1,000 at a rate of 4.5% for 10 years.
Having this calculator program allows us to only calculate for interest compounded monthly. We can create a new program to calculate interest compounded at any rate: monthly, annually, bi-monthly, and so on. You can try playing with the code in order to create your calculators. These are helpful if you want to know what to expect when investing money or depositing in savings accounts. For example, you can determine how long it would take for your deposits to reach a certain amount. Say you wanted $50,000 for a college education for your children. In this case, you could figure out how much you would need to deposit in order to have that amount in 18 years, when they’d most likely be ready to go to college.