Home Logical Reasoning
Post
Cancel

Logical Reasoning

Logical Reasoning

I. Understanding the importance of logical reasoning

As we write algorithms, we’ll need to use logical reasoning to create these algorithms.

In simple terms, logical reasoning is the set of steps followed to reach a conclusion. In computational thinking, when we design algorithms, the systematic set of steps we follow are part of the algorithm. The way a computer reads those algorithms depends on how we write that algorithm. There are two types of logical reasoning arguments, which are as follows:

  • Inductive reasoning
  • Deductive reasoning

Before we define those in more depth, let’s look at why logical reasoning is so important and why order matters when we create algorithms.

In order to analyze a problem and provide an algorithm that helps us tackle the problem, we need to understand what logical reasoning is first. Logic can be daunting for some, but we use it every day subconsciously.

Let’s look at a simple example. Say you take a shower every morning and go to work. Well, would you get dressed for work before the shower? No, because that would make absolutely no sense. Logically, you’d have to shower first before you put on your clothes for work. Now, I’ve skipped a ton of steps here, but those steps are logical steps. Other examples of logic include following recipes, using an umbrella if it’s raining (or not), and so on.

Throughout this part, we’ll weave in and out of logical reasoning and designing algorithms using logical operators . A logical operator allows a program to make decisions. We use those too in everyday life without realizing it. For example, if it’s sunny and warm, we may want to go biking, but not if it’s sunny and cold. The and here is a logical operator.

We take a lot of things into consideration when we’re making decisions. In computational thinking, especially in algorithm design, we need to consider those things and provide a way for the program to test those conditions. We will delve deeper into logical operators later in this part. For now, let’s look more closely at the types of logical reasoning and how to apply them.

1. Applying inductive reasoning

When we talk about inductive reasoning, we’re really working backward. Inductive reasoning starts from a conclusion, which may be true or not, and works backward to create the code using the existing data. Let’s look at a simple problem first.

Solving an inductive reasoning sample problem

We have a budget of $150 for buying supplies: art pencils and erasers. The art pencils are $1.75 each and the erasers are $1.50 each.

Remember, in computational thinking, we decompose the problem first, then we identify the pattern, then we generalize that pattern, and then we create the algorithm. So, let’s recognize that pattern.

Let’s look at what we know so far and name some variables:

  • The total budget is $150.
  • The cost of pencils is $1.75 each.
  • The cost of erasers is $1.50 each.
  • Let’s denote the number of pencils by p.
  • Let’s denote the number of erasers by n.

Remember that when we get to that algorithm, we may want to rename those variables. But for now, since we’re going to look at mathematical algorithms first, we’ll keep the simple variables.

We can do this in one inequality. Why an inequality and not an equation? Because our total may not be exactly $150. But it can’t be more than $150 because that’s all the money we have.

Because this is a simple problem, we’re identifying and generalizing that pattern in one move.

So, the number of pencils times the cost plus the number of erasers times the cost is less than or equal to $150:

\[1.75p + 1.50n ≤ 150\]

Now let’s talk about the algorithm design. Maybe this is something I buy regularly because I run art classes. I’m going to go off that scenario. Maybe my employer gives me at most $150, but depending on what I used before, I may need more pencils than erasers and vice versa. So, I need a program that I can use and reuse at the beginning of every term. Was this part of my problem? No, this was an ill-defined problem. So, I’m adapting the problem based on a set of particular needs. In short, I’m defining the problem I want to solve.

1
2
3
4
5
6
Important Note:
As a side note for an inductive and deductive reasoning dilemma, it is
important to understand that conditional statements, such as the if/then
statements we use often in programming, are usually associated with deductive
reasoning. We can go into a debate about whether or not they can be inductive,
but the truth is, inductive reasoning problems will use deductive reasoning. 

So, I want the program to ask me how many pencils I want or I want it to ask me how many erasers I want. It all depends! Let’s look at what the program should do for us. The following steps show us this:

  1. Ask whether your input will be pencils or erasers.
  2. Choose an inequality to use based on the input provided.
  3. Identify how many of the pencils or erasers are possible (given the input).
  4. Give a total cost for the number of pencils and erasers.

Please note that, as always, there are a lot of ways to arrive at the same answers in Python. While some of these programs are longer than what I would normally present, since we’re learning both computational thinking and Python programming, it’s important to show steps that are easy to understand.

For this particular program, we’re going to need to import the math functions so that we can round down. Why do we need to do that? Well, we can’t buy parts of erasers and pencils, only whole pencils and whole erasers. So, if the program says we can buy 19.5 pencils, that really means we can only purchase 19 pencils.

Let’s go back to the problem. Take a look at the following written program:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# We need the math module, so don't forget to import it.
import math
# Ask the user if they will be inputing pencils or erasers first.
item1 = input("Will you be entering pencils or erasers? ")

if item1 == "pencils":
    pencils = int(input("How many pencils will you purchase? "))
    if pencils * 1.75 < 150:
        pencilstotal = pencils * 1.75
        total = 150 - pencilstotal
        total = total / 1.50
        erasers = math.floor(total)
        total2 = pencilstotal + erasers * 1.50
        print("You will be able to purchase " + str(pencils) + " pencils and " + str(erasers) + " erasers for a total cost of $" + str(total2) + ".")
    else:
        print("That's too many pencils.")
elif item1 == "erasers":
    erasers = int(input("How many erasers will you purchase? "))
    if erasers * 1.50 < 150:
        eraserstotal = erasers * 1.50
        total = 150 - eraserstotal
        total = total / 1.75
        pencils = math.floor(total)
        total2 = pencils * 1.75 + eraserstotal
        print("You will be able to purchase " + str(pencils) + " pencils and " + str(erasers) + " erasers for a total cost of $" + str(total2) + ".")
    else:
        print("That's too many erasers.")
else:
    print("Please run the program again and enter erasers or pencils as your input.")
    

Remember that the preceding program will run the lines of code in order (sequentially). So, if a user inputs erasers first, then the first if statement and the nested if statement are ignored. If the user enters pencils first, then the algorithm runs normally from the first if statement and goes through the remaining conditions. Here’s what the program does, in order:

  1. Asks the user to input whether they are buying pencils or erasers.
  2. If the user enters pencils, then the program asks how many pencils they’ll purchase. Then, it calculates the number of erasers they can afford to buy.
  3. If the user enters a number of pencils that is too large, they’ll get a message that they can’t afford that amount.
  4. If the user enters erasers, then the program asks how many erasers they’ll purchase, then calculates the number of pencils the user can afford to buy.
  5. If the user enters a number of erasers that is too large, they’ll get a message that they can’t afford that amount.
  6. If the user enters neither pencils nor erasers, they’ll get a message to run the program again and enter one of those two options.

The preceding is an oversimplified inductive reasoning problem. Some inductive reasoning problems will ask that you look at data, make some probable conclusions, and then write a program to test those conclusions. In the process of learning logical reasoning, we are essentially training ourselves to look at decisions and how to process them in a way that a program can return the output we are looking for.

It is important to note here that there are multiple ways to look at problems and prepare solutions. While I prefer decision trees and flow-charts, other programmers and developers work more mathematically. Yet others like to write down what the program needs to do in simple sentences and/or paragraphs. The point of this process is to allow us to create a program that produces the necessary output and is easy to follow logically by both the programmers and developers and the computer running it.

Now, let’s take a look at deductive reasoning.

2. Applying deductive reasoning

We’re now at the section of this chapter that focuses on deductive reasoning. Even when I was a mathematics student, I found deductive reasoning fascinating. I quickly learned that mathematics taught us how to follow arguments logically in geometry and I fell in love with all things logic and truth tables.

Logic is taught using proofs and inductive and deductive reasoning. Truth tables help us analyze conditions. In truth tables, some things are assumed. For example, a statement is either true or false. The other statement is true or false. A combination of those statements depends on whether or not the statements are true or false.

Alright, that’s a bit complicated. Before I move on to explain deductive reasoning, let’s look at a quick truth table and the logic process it contains.

Truth tables were critical when I first started coding. They helped me understand the coding processes and how to work with conditions. Not every programmer or coder uses these tables, but I find them helpful, even if not used explicitly in the decision-making process. Let’s look at one now.

Let’s say we have a statement or condition p and that condition is True. Let’s say that we have another statement or condition q and that it is also True. In truth tables, we use the symbol ¬ to denote NOT. So, ¬ p ¬ is False and ¬ q is also False. That’s because if p is True, then NOT p is NOT True, in other words, False. The symbol ^ is used for AND, so p AND q is written as p ^ q. The symbol v is used for OR, so p OR q is written as p v q. In table format, our truth table looks as follows:

image

Analyzing a truth table and understanding all the possible conditions can take time, but the process is similar to what we go through in logical reasoning when writing algorithms for problems. Now, let’s take a closer look at deductive reasoning.

Let’s first define what deductive reasoning is. Deductive reasoning is the process of going from a statement or hypothesis to a conclusion. Because deductive reasoning is what we use in algorithmic design, for the most part, we will need to define some terms associated with it. Let’s start with conditional statements.

a. Learning about conditional statements

Conditional statements are if/then statements. Here are a few logical arguments using conditional statements:

  • If it rains, then I’ll use an umbrella.
  • If I drink water, then I won’t be thirsty.
  • If my dog needs to go out, then he stands by the door.
  • If a quadrilateral has four right angles, then it is a rectangle.

All the preceding statements are examples of conditional statements. The first part of the statement is called the hypothesis . The second part of the statement is the conclusion . In the statement If it rains, then I’ll use an umbrella, the hypothesis is it rains and the conclusion is use an umbrella. We do not include if or then in the hypotheses and conclusions.

Here are some of the logical statements we use in Python:

  • if: When using if statements, we ask whether a condition is met, then do something based on that true or false condition.

  • if-else: When using if-else statements, we test one condition and do something, but if that condition is not met, then we do something else.

image

  • if-elif-else: When using if-elif-else statements, we have one condition; if that’s not met, we test another condition—that is, the else if (elif) condition—otherwise, we do something else.

image

b. Understanding nested statements

Another type of logic statement we use in Python has to do with nested statements. In nested conditions, the if statement that is nested is only followed if the previous if statement is True. This is easier to understand with an example. Let’s go back to our if-elif-else statement and add some nested conditions. We had previously asked the user to give a number between 1 and 20. Now, let’s say we want to subdivide the conditions further using the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
number = int(input("Pick a number between 1 and 20. "))
if number < 10:
    if number < 6:
        print("Why such a small number?")
    else:
        print("Well, less than 10 but greater than 5. I'll take it.")
elif number < 21:
    if number < 16:
        print("You like values that are greater than 10, but not too much greater. I guess that's fine.")
    else:
        print("I like larger numbers myself too.")
else:
# Sometimes we make mistakes when providing input in programs. If you choose a number that's not between 0 and 20, the program will print this message.
    print("That number isn't between 0 and 20. Run the program and try again.")

In the preceding code snippet, the code has a message for any time we enter numbers that do not meet the guidelines. For example, the input requested is between 1 and 20. But what happens if the user types 0 or 21, or another number not in that range? Then, the print() statement provides a message that asks the user to run the program again.

As you can see from the preceding test cases, we have more outputs based on the conditions given in the program. While this was a simple number program, we can use similar logic when we are solving more complex problems.

Let’s say you run an online store. The selections a user makes for items are going to be used in similar algorithms, albeit much more complex ones. The algorithm tests the conditions, such as items selected, quantities selected, and so on to apply totals, coupons, and much more. That’s why logic and logical reasoning is so important in programming.

Now, as mentioned previously, the logical processing we use can be different for individual programmers. However, regardless of preference, logical reasoning and logical processing are absolutely necessary when we are writing algorithms. Rather than diving into the writing, we process problems, look at the decision-making and which steps need to happen, and then we design the algorithm. That logical process is critical to creating effective algorithms. We will continue to look at logical reasoning throughout this book as we analyze problems, even if we don’t explicitly state so.

II. Using Boolean logic and operators

Boolean logic refers to the operators, namely, and, or, and not in Python. You’ll recall seeing this in the brief discussion on truth tables earlier in this chapter. As we’ll see next, we use the same logical processing when writing the algorithms, even if the tables are not explicitly stated or used. When solving computational thinking problems, we sometimes have to meet multiple conditions at once. Let’s look at this using just language for now.

Let’s sort some fruit. If the fruit is round and orange, green, or yellow, it will be sorted into group 1 . If the fruit is not round, but is orange, green, or yellow, it will be sorted into group 2 . If the fruit doesn’t match those requirements, it goes into group 3 . Let’s simplify these groups:

  • Group 1 : Round AND (orange OR green OR yellow)
  • Group 2 : Not round AND (orange OR green OR yellow)
  • Group 3 : All other fruit

I know I stated the round condition first. But if you take a look at groups 1 and 2, the fruits need to be tested for those colors for both conditions—that is, if that condition is not met for color, it doesn’t matter whether the fruit is round or not, it goes in group 3. So, here’s what I’d write for an algorithm:

  1. Test whether fruit is orange, green, or yellow.
  2. If yes, test whether round, and sort into group 1 or 2.
  3. If no, sort into group 3.

So, if we had a mandarin orange, that falls under group 1. If we had a banana, it would be in group 2. If we had strawberries, they would be in group 3.

Now, if we were going to write this, we’d need to make sure we’ve added the characteristics of the fruits so that we can test them against something. We will be looking at something like that in further chapters of this book, but for now, to simplify some of the learning, we’ll create a similar algorithm but with numbers.

Before we move on too much, let’s take a quick look at the basic operators in Python:

image

1. The and operator

To understand the and operator better, it’s best to look at a mathematical algorithm. Let’s ask for a number and test whether that number is larger than 100 and a multiple of 2. To test whether a number is a multiple of 2, we use the modulo operator (mod) . The symbol for mod is % in Python. So, looking at the code, if number % 2 == 0, then the number is divisible by 2. If number % 2 == 1, then it is not divisible by 2. We use the equal (==) operator or not equal (!=) operator to complete these conditions:

1
2
3
4
5
6
7
8
9
number = int(input("Give a number between 1 and 200. "))
if number > 99 and number % 2 == 0:
    print("That's a large, even number.")
elif number > 99 and number % 2 != 0:
    print("That's a large, odd number.")
elif number < 100 and number % 2 == 0:
    print("That's a small, even number.")
else:
    print("That's a small, odd number.")

Now, I know we’ve talked about different ways to write algorithms. Did I need to use an AND operator for this one? Probably not. I could have just written it as nested statements, if-elif-else statements, and so on. Some test cases and the results of the algorithm are shown as follows:

When we input 104, we see the following output:

1
2
Give a number between 1 and 200. 104
That's a large, even number.

When we input 80, we see the following output:

1
2
Give a number between 1 and 200. 80
That's a small, even number.

When we input 31, we get the following output:

1
2
Give a number between 1 and 200. 31
That's a small, odd number.

As you can see from the previous test cases, the program tests our cases and provides the printed messages based on the conditions met. Now, let’s take a look at the or operator.

2. The or operator

As we saw in the fruit example earlier in this chapter, we checked whether the color of the fruit was orange, green, or yellow. That’s how an or operator works. We check for something or the other. This time, we’re going to look at some True and False statements. Let’s say that variable A is True and variable B is False. If we were to use an or operator to check the result of A or B, then our answer would be True.

Why is that? Because no matter what, the result will be either True or False, which is a True statement. Confused? Logic can be confusing. Let’s go ahead and test A and B as well as A or B in the following program to help you visualize this:

1
2
3
4
5
6
7
8
9
10
11
12
13
A = True
B = False
C = A and B
D = A or B
if C == True:
    print("A and B is True.")
else:
    print("A and B is False.")
if D == True:
    print("A or B is True.")
else:
    print("A or B is False.")
    

Now, I added some conditions so that we’d get printouts and you could see that the logic I stated was right, but we didn’t need to do all of this. We could have just printed C and D.

When we run this program, this is the result:

1
2
A and B is False.
A or B is True.

As you can see, A and B is False because one of the statements is False, which means the whole thing is False. A or B is True because one of them is True, so the condition is True. Now, let’s look at the last operator (for now), the not operator.

3. The not operator

The not operator lets us test the opposite of things. So, if A is set as True, then not A is False. It’s as simple as that. Let’s look at a few examples through the following code:

1
2
3
4
5
6
A = True
B = False
print(not A)
print(not B)
print(not (A and B))
print(not (A or B))

From the previous code, we’ve talked about the first printed statement here. Since A is True, not A is False. For the second print statement, we expect that result to be True because B is False. Now, we did the A and B and A or B statements previously. We know that A and B is False, so not (A and B) is True. We also know A or B is True, so not (A or B) is False.

Let’s look at what the program prints:

It prints the following for not A:

1
False

It prints the following for not B:

1
True

It prints the following for not (A and B):

1
True

It prints the following for not (A or B):

1
False

In this section, you have learned about a few of the Boolean operators. With Boolean operators, we can write algorithms that test cases for us and provide outputs based on those cases. As mentioned, a program will run based on the instructions we write in the algorithm.

By writing our algorithms using these operators, we can ensure that conditions are applied only in the circumstances we want them to apply. Rather than having a program run on incorrect conditions, we can include statements and prompts to help produce the right outcomes. For example, if an input for distance is accidentally entered as negative, a Boolean statement could have checked conditions and provided the person with feedback within the program, then run again. Using Boolean operators provides clear logical processes and allows better and clearer algorithms.

III. Identifying logic errors

Before we talk too much about logic errors, let’s talk about why it’s important to keep them in mind. In Python, not all errors lead to a failed or crashed program. Some logic errors will allow a program to run entirely without crashing at all or alerting the user of an error. Those errors are hard to identify.

Here are some logic errors that can get us in trouble, but keep in mind that there are many ways to incorporate logic errors into our programs accidentally:

  • Using the wrong variable in an equation or statement
  • Using the wrong operator to test conditions
  • Using the wrong indentation when checking for conditions

The one I am the guiltiest of is switching my variables, but I do also make mistakes in indentation often. Usually, those get identified more often when I try to run the program, because the program may fail to run in some instances.

Let’s take a look at a simple algorithm that contains an error in a formula. In this first algorithm, the goal is to get the total cost after buying a number of orders of fries from a restaurant at a cost of $1.50 each:

1
2
3
4
number = int(input("Type the number of fries you are ordering:"))
cost = 1.50
total = number * number
print("Your total cost is $" + str(total) + ".")

If we run the preceding program, the program will run without problems/errors and show the following output for 12 orders of fries:

1
2
Type the number of fries you are ordering: 12
Your total cost is $144.

Now, if we’re paying attention, we’ll realize that the cost for 12 orders of fries is too high at $144. That’s because there is an error in our algorithm. The algorithm should contain the total = cost * number formula, as follows:

1
2
3
4
number = int(input("Type the number of fries you are ordering:"))
cost = 1.50
total = cost * number
print("Your total cost is $" + str(total) + ".")

Now that we’ve fixed that formula, the output is correct:

1
2
Type the number of fries you are ordering: 12
Your total cost is $18.0.

As you can see, $18.0 seems a lot more reasonable for 12 orders of fries at $1.50 each.

Errors in formulas can be difficult to find, especially if the program runs without alerting to the error. If we have a large algorithm with multiple formulas, finding those errors can become cumbersome and a lengthy process. The best recommendation for this is to test your algorithm at every step of the process you can. That way, finding errors becomes a simpler task.

Let’s now take a look at an error when testing conditions. Much like errors in formula, errors in condition testing may be hard to spot, as the program may just run anyway:

1
2
3
4
5
6
7
8
9
number = int(input("Give a number between 1 and 200. "))
if number > 99 and number % 2 == 0:
    print("That's a large, even number.")
elif number > 99 and number % 2 != 0:
    print("That's a large, odd number.")
elif number < 100 or number % 2 == 0:
    print("That's a small, even number.")
else:
    print("That's a small, odd number.")

In the preceding code, there is an error in the algorithm that causes us to get incorrect feedback when entering some odd numbers. Take a look at the second elif statement. That or will produce an error.

If we run this program, we get an output. Let’s run it with the number 99:

1
2
Give a number between 1 and 200. 99
That's a small, even number.

Now, the problem here is that 99 is not an even number. Somewhere in the algorithm, we introduced an error in the conditions. In this case, instead of using an and operator, we used or:

1
2
elif number < 100 or number % 2 == 0:
    print("That's a small, even number.")

Once we replace the or with and, we can run the program again:

1
2
3
4
5
6
7
8
9
number = int(input("Give a number between 1 and 200. "))
if number > 99 and number % 2 == 0:
    print("That's a large, even number.")
elif number > 99 and number % 2 != 0:
    print("That's a large, odd number.")
elif number < 100 and number % 2 == 0:
    print("That's a small, even number.")
else:
print("That's a small, odd number.")

Using 99 as the input, we get the following output:

1
2
Give a number between 1 and 200. 99
That's a small, odd number.

Running the program with 98 as the input, we get the following:

1
2
Give a number between 1 and 200. 98
That's a small, even number.

As you can see, unless we’re paying attention, we can miss errors in our conditions and logical operators. Because the program is able to run with these errors in our algorithm, catching where exactly we made the mistake is harder to do than when we incorporate errors that stop the program from running.

Finally, let’s take a look at an indentation error using the same code for the condition testing. This time, with an indentation error added, we have the following:

1
2
3
4
5
6
7
8
9
number = int(input("Give a number between 1 and 200. "))
if number > 99 and number % 2 == 0:
    print("That's a large, even number.")
elif number > 99 and number % 2 != 0:
    print("That's a large, odd number.")
    elif number < 100 and number % 2 == 0:
    print("That's a small, even number.")
else:
    print("That's a small, odd number.")

In this case, we can’t run the program. The second elif statement is indented incorrectly. When we try to run the program, we get an Invalid Syntax error message.

Notice that the print() code below the elif statement is also indented incorrectly. Once we fix those two errors, we can run the code, as we did previously in this chapter.

Incorporating errors into our algorithms is a common mistake. As you can see from the previous examples, identifying some of the errors can be hard to do, since the program may be running as if there is no problem.

I may not have caught many errors with conditions in my algorithms, but that may just be because I never realized there was a mistake to begin with. That’s one of the reasons why it’s really important to run various instances of our programs to ensure that the results we get make sense. We will be discussing more errors as we look at programs and computational thinking problems throughout this book. In the meantime, test your programs and test them often. Triple check your math, your indentations, and your logic.

This post is licensed under CC BY 4.0 by the author.