An Introduction To Computer Science and Problem Solving: What Is in This Chapter ?
An Introduction To Computer Science and Problem Solving: What Is in This Chapter ?
Since computers are used everywhere, you can get involved with
computers from just about any field of study. However, there are
specific fields that are more computer-related than others. For example. the fields of electrical
engineering and computer systems engineering primarily focus on the design and
manufacturing of computer hardware, while the fields of software engineering and computer
science primarily focus on the design and implementation of software.
• System Software: is designed to operate the computer’s hardware and to provide and
maintain a platform for running applications. (e.g., Windows, MacOS, Linux, Unix, etc..)
• Application Software: is designed to help the user perform one or more related
specific tasks. Depending on the work for which it was designed, an application can
manipulate text, numbers, graphics, or a combination of these elements.
(e.g., office suites, web browsers, video games, media players, etc…)
-2-
COMP1405/1005 – An Introduction to Computer Science and Problem Solving Fall 2011
The area of software design is huge. In this course, we will investigate the basics of creating
some simple application software. If you continue your degree in computer science, you will
take additional courses that touch upon the other areas of system software and middleware.
Software is usually written to fulfill some need that the general public, private industry or
government needs. Ideally, software is meant to make it easier for the user (i.e., the person
using the software) to accomplish some task, solve some problem or entertain him/herself.
Regardless of the user’s motivation for using the software, many problems will arise when
trying to develop the software in a way that produces correct results, is efficient ad robust,
easy to use and visually appealing. That is where computer science comes in:
So, computer science is all about taking in information and then performing some
computations & analysis to solve a particular problem or produce a desired result, which
depends on the application at hand.
Computer science is similar to mathematics in that both are used as a means of defining and
solving some problem. In fact, computer-based applications often use mathematical models
as a basis for the manner in which they solve the problem at hand.
Instead, computers have their own languages that they understand. Each of these
languages is known as a programming language.
A programming language “boils down to” a set of words, rules and tools that
are used to explain (or define) what you are trying to accomplish. There are many different
programming languages just as there are many different "spoken" languages.
-3-
COMP1405/1005 – An Introduction to Computer Science and Problem Solving Fall 2011
There are also other types of programming languages such as functional programming
languages and logic programming languages. According to the Tiobe index (i.e., a good site
for ranking the popularity of programming languages), as of February 2011 the 10 most
actively used programming languages were (in order of popularity):
For many years, we used JAVA as the basis in this course, due to its popularity as well as its
ease of use. However, JAVA does have some drawbacks for new programmers, pertaining to
some overhead in getting started with the language.
The Computer Sciences Accreditation Board (CSAB) identifies four general areas that it
considers crucial to the discipline of computer science:
• theory of computation
- investigates how specific computational problems can be solved efficiently
However, in addition, they also identify other important fields of computer science:
There are aspects of each of the above fields can fall under the general areas mentioned
previously. For example, within the field of database systems you can work on theoretical
computations, algorithms & data structures, and programming methodology.
As you continue your studies in computer science, you will be able to specialize in one or more
of these areas that interest you. This course, however, is meant to be an introduction to
programming computers with an emphasis on problem solving.
This is your first programming course here in the School of Computer Science at Carleton.
You have some more core programming courses coming up after this one. Here is a break-
down of how this course fits in with your first 2 years of required programming courses:
Of course, there are other computer science courses as well. These are just the core courses
that nearly everyone is required to take. After this course is over, you should understand how
to write computer programs. In the winter term, you will take COMP1406/1006 which is a
more detailed course focused on Object-Oriented programming in JAVA. Together, these two
courses give you a solid programming background and you will be able to learn other
computer languages easily afterwards … since they all have common features. If you want to
do well in this course, attend all lectures and tutorials and do your assignments.
-5-
COMP1405/1005 – An Introduction to Computer Science and Problem Solving Fall 2011
The Processing community has written over seventy libraries to help you produce applications
that incorporate:
Processing allows you to export applets for use on the web or standalone applications for the
PC, Mac or Linux operating systems. To start processing, just double-click on the processing
application icon that is in the processing folder that you downloaded:
-6-
COMP1405/1005 – An Introduction to Computer Science and Problem Solving Fall 2011
these are kinds “inner steps” that belong to that structure. Some people will us brackets to
indicate what is in or out of a control structure as follows:
repeat {
get a new light bulb
put it in the lamp
} until (lamp works or no more bulbs left)
repeat 3 times {
unplug lamp
plug into different socket
}
The point is that there are a variety of ways to write pseudocode. The important thing
to remember is that your algorithm should be clearly explained with no ambiguity as to
what order your steps are performed in.
Whether using a flow chart of pseudocode, you should test your algorithm by manually
going through the steps in your head to make sure that you did not forget a step or a
special situation. Often, you will find a flaw in your algorithm because you forgot about
a special situation that could arise. Only when you are convinced that your algorithm
will solve your problem, should you go ahead to the next step.
Consider our previous example of finding the average of a set of n grades stored in a
file. What would the pseudocode look like ? Here is an example of what it might look
like if we had the example of n numeric grades x1 ... xn that were loaded from a file:
Algorithm: DisplayGrades
- 20 -
COMP1405/1005 – An Introduction to Computer Science and Problem Solving Fall 2011
It would be wise to run through the above algorithm with a real set of numbers. Each
time we test an algorithm with a fixed set of input data, this is known as a test case.
You can create many test cases. Here are some to try:
For now, we will not discuss the details of how to produce the above source code. In
fact, the source code would vary depending on the programming language that was
used. Learning a programming language may seem difficult at first, but it will become
easier with practice.
The computer requires precise instructions in order to understand what you are asking it
to do. For example, if you removed one of the semi-colon characters ( ; ) from the
program above, the computer would become confused as to what you are doing
because the ( ; ) is what it understands to be the end of an instruction. Leaving one of
them off will cause your program to generate what is known as a compile error.
- 21 -
COMP1405/1005 – An Introduction to Computer Science and Problem Solving Fall 2011
The longer your program becomes, the more likely you will have multiple compile errors.
You need to fix all such compile errors before continuing on to the next step.
When you run your program, if all is well, you should see the
correct output. It is possible however, that your program works
correctly for some set of data input but not for all. If the output of your program is
incorrect, it is possible that you did not convert your algorithm properly into a proper
program. It is also possible that you did not produce a proper algorithm back in step 3
that handles all situations that could arise. Maybe you performed some instructions out
of sequence. Whatever happened, such problems with your program are known as
bugs.
You should fix as many bugs in your program as you can find. To find bugs effectively,
you should test your program with many test cases (called a test suite). It is also a
good idea to have others test your program because they may think up situations or
input data that you may never have thought of. The process of finding and fixing errors
in your code is called debugging and it is often a very time-consuming “chore” when it
comes to being a programmer. If you take your time to carefully follow problem solving
steps 1 through 3, this should greatly reduce the amount of bugs in your programs and
it should make debugging much easier.
For example, if the result of your program is a long list of numbers, but your intent was to
determine a pattern in the numbers or to identify some feature from the data, then simply
producing a list of numbers may not suffice. There may be a need to display the information
in a way that helps you visualize or interpret the results with respect to the problem. Perhaps
a chart or graph is needed.
It is also possible that when you examine your results, you realize that you need additional
data to fully solve the problem. Or, perhaps you need to adjust the results to solve the
problem more efficiently (e.g., your game is too slow).
It is important to remember that the computer will only do what you told it to do. It is up to you
to interpret the results in a meaningful way and determine whether or not it solves the original
problem. It may be necessary to re-do some of the steps again, perhaps going as far back as
step 1 again, if data was missing.
So there you have it. Those are the 6 steps that you should follow in order to solve problems
using computers. Throughout the course, you should try to use this approach for all of your
assignments. It is a good idea to practice problem solving to make sure that you understand
the process. Below are some practice exercises that will help you practice the first 3 steps of
the problem solving process. Later, you will gain experience with steps 4 through 6.
PRACTICE EXERCISES
Formulate a model and then develop an algorithm for each of the following problems. In each
case, start with a simple algorithm and then try to think about situations that can realistically go
wrong and make appropriate adjustments to the algorithm. Keep in mind that there is no
“right” answer to these problems. Everyone will have a unique solution.
- 23 -
COMP1405/1005 – An Introduction to Computer Science and Problem Solving Fall 2011
1. go to kitchen
2. open refrigerator
3. choose a drink
4. drink it
However, we could have come up with a more abstract (i.e., less detailed) algorithm as
follows:
1. get a drink
2. drink it.
Each of these algorithmic solutions solves the problem. So then, which one is best ? The
answer is not always easy. As a rule of thumb, we want to produce the simplest possible
algorithm that is easily understandable. While it is important to provide enough detail to be
able to properly describe the problem solution, it is also important not to get hung up on too
much detail so that the algorithm becomes cluttered and overly complicated.
- 24 -
COMP1405/1005 – An Introduction to Computer Science and Problem Solving Fall 2011
So, likely, the first of the three algorithmic solutions here would suffice as an adequate solution
to the problem. This idea of coming up with a clear algorithm without too much details is
known as abstraction.
We also see the idea of abstraction in 3D video game engines which hide details of objects
further away as they are not necessary until the game character moves closer to those objects.
It helps to speed up the game and reduce clutter while allowing the game player to focus on
the more important objects nearby:
Æ ÆÆ Æ ÆÆ Æ ÆÆ Æ Æ Abstraction Æ Æ ÆÆ Æ ÆÆ Æ ÆÆ Æ
We have just been discussing a kind of abstraction that allows our algorithm steps to be either
quite abstract (i.e., high level) or more detailed (i.e., low level). This kind of abstraction is
known as control abstraction.
- 25 -
COMP1405/1005 – An Introduction to Computer Science and Problem Solving Fall 2011
Often, in the cases where details are necessary, it is still possible to provide a higher-level
algorithm, while allowing the more specific details to be described in a sub-algorithm or sub-
program. As a result, we are able to describe an algorithm at multiple layers of abstraction.
For example, in our thirst-quenching scenario, we could use the details of our third very-
specific algorithmic solution but hide the more detailed portions in a sub-algorithm as follows:
AlgorithmX: QuenchThirst
1. get off couch
2. walk to kitchen
3. open refrigerator
4. perform SubAlgorithm1
5. close refrigerator
6. drink it
SubAlgorithm1: GetDrink
1. if there is a carton of lemonade or orange juice then {
2. take the carton
3. close refrigerator
4. go to the cupboard
5. open cupboard
6. take a glass
7. close cupboard
8. pour lemonade or juice into glass
9. go to refrigerator
10. open refrigerator
11. put carton in refrigerator
}
12. otherwise if there is a soda then {
13. take soda
14. open soda
}
Notice how the main algorithm is quite simple now with much less detail. SubAlgorithm1 is
not a stand-alone algorithm in that it cannot solve the problem on its own.
For example, it assumes that the person is standing in front of the refrigerator and that the
refrigerator door is open. We could also lessen these restrictions, for example, by
incorporating steps 3 and 5 of the main AlgorithmX into SubAlgorithm1 and moving them out
of the main algorithm. Notice the changes:
- 26 -
COMP1405/1005 – An Introduction to Computer Science and Problem Solving Fall 2011
AlgorithmX: QuenchThirst
1. get off couch
2. walk to kitchen
3. perform SubAlgorithm1
4. drink it
SubAlgorithm1: GetDrink
1. open refrigerator
2. if there is a carton of lemonade or orange juice then {
3. take the carton
4. close refrigerator
5. go to the cupboard
6. open cupboard
7. take a glass
8. close cupboard
9. pour lemonade or juice into glass
10. go to refrigerator
11. open refrigerator
12. put carton in refrigerator
}
13. otherwise if there is a soda then {
14. take soda
15. open soda
}
16. close refrigerator
Now we have abstracted out a little further by hiding some details within SubAlgorithm1. We
could also perform additional layers of abstraction by abstracting within SubAlgorithm1:
AlgorithmX: QuenchThirst
1. get off couch
2. walk to kitchen
3. perform SubAlgorithm1
4. drink it
SubAlgorithm1: GetDrink
1. open refrigerator
2. if there is a carton of lemonade or orange juice then {
3. perform SubAlgorithm2
}
4. otherwise if there is a soda then {
5. take soda
6. open soda
}
7. close refrigerator
- 27 -
COMP1405/1005 – An Introduction to Computer Science and Problem Solving Fall 2011
SubAlgorithm2: PourDrink
1. take the carton
2. close refrigerator
3. go to the cupboard
4. open cupboard
5. take a glass
6. close cupboard
7. pour lemonade or juice into glass
8. go to refrigerator
9. open refrigerator
10. put carton in refrigerator
Notice how SubAlgorithm1 became more abstract and easier to read. We can abstract out
in this manner as often as we feel it to be necessary. When do we decide to stop this kind of
abstraction ? Well, there is no fixed rule. However, when a sub-algorithm seems to be small
enough to understand or when it relates to a well-defined real-life group of actions, then it is
probably a good idea not to break it down any further.
However, it is possible that there is a portion of the algorithm that may be used by other
algorithms. For example, what if we wanted an algorithm to place a glass on the kitchen table
as part of getting prepared for dinner guests ? In that situation, you may realize that you will
need to perform the same steps again as listed in lines 3 through 6 of SubAlgorithm2.
Hence, it may be a good idea to create a SubAlgorithm3 as follows:
SubAlgorithm3: GetGlass
1. go to the cupboard
2. open cupboard
3. take a glass
4. close cupboard
Then of course we could make use of this SubAlgorithm3 within SubAlgorithm2 as well as
within our new algorithm for setting the table as follows:
SubAlgorithm2: PourDrink
1. take the carton
2. close refrigerator
3. perform SubAlgorithm3
4. pour lemonade or juice into glass
5. go to refrigerator
6. open refrigerator
7. put carton in refrigerator
- 28 -
COMP1405/1005 – An Introduction to Computer Science and Problem Solving Fall 2011
AlgorithmY: SetTableFor4
1. walk to kitchen
2. repeat 4 times {
3. perform SubAlgorithm3
4. place glass on table
5. perform SubAlgorithm4 // similar algorithm to get plate from cupboard
6. place plate on table
7. perform SubAlgorithm5 // similar algorithm to get fork & knife from drawer
8. place knife and fork on table
}
9. go back onto couch
Hopefully you see now how practical and powerful abstraction can be in making algorithms
simpler, more readable and ultimately more understandable.
In computer science we give a special name to the sub-algorithms. They are sometimes
called modules, functions or procedures. In fact, it is not a good idea to simply number all
the sub-algorithms but instead to give them meaningful names. As standard convention,
when naming a function or procedure, you should use letters, numbers and underscore (i.e., _)
characters but not any spaces or punctuation. Also, the first character in the name should be
a lower case letter. If multiple words are used as the name, each word except the first should
be capitalized. Lastly, we often use parentheses (i.e., ()) after the function or procedure name
to identify it as a sub-algorithm. We’ll see why later.
You should choose meaningful names that are not too long. Here are some meaningful
names that we could use for our sub-algorithms:
AlgorithmX: QuenchThirst
1. get off couch
2. walk to kitchen
3. chooseDrink()
4. drink it
and ..
- 29 -
COM
MP1405/1005 – An Introduc
ction to Comp
puter Science
e and Problem
m Solving Fall 2011
AlgorithmY Y: SetTablleFor4
1. waalk to kitchen
2. peat 4 time
rep es {
3. getGlas ss()
4. place glaass on tablee
5. getPlatee()
6. place plaate on table
e
7. getUten nsils()
8. place knnife and forkk on table
}
9. go back onto couch
Hennce each of these sub--algorithms go off and come backk (i.e., returrn) with som me kind of
obje
ect (i.e., drin
nk, glass, plate
p or uten
nsils). Whe en a sub-allgorithm comes back with
w some kind k
of ob
bject (such as the glas ss or plate) or value (ssuch as a numerical re
esult), we ca
all the sub--
algo
orithm a fun nction.
- 30
3 -
COMP1405/1005 – An Introduction to Computer Science and Problem Solving Fall 2011
Algorithm: SolvePuzzle
1. pour out all pieces on the table
2. flip all pieces over to show the picture side
3. separate the edge pieces from the inside pieces
4. solve edge pieces
5. repeat until there are no more easily identifiable pieces {
6. select pieces that form an easily identifiable part of the picture
7. put these selected pieces together
8. try to fit picture portion to border or to other picture portions
}
9. repeat until no more pieces remain {
10. pick piece up and try to fit it somewhere
11. if its location is unclear, put piece back in pile
}
Notice how the seemingly difficult problem is broken down into well-defined stages (or steps).
By solving the edge pieces first, it gives the person a better idea as to the size of the whole
image and it gives a feel for how the other parts of the puzzle will fit together.
In fact, steps 5 through 8 can be made into more specific steps if we had more information
about the puzzle. For example, consider how you would solve this image:
- 31 -
COMP1405/1005 – An Introduction to Computer Science and Problem Solving Fall 2011
Algorithm: SolvePuppies
1. pour out all pieces on the table
2. flip all pieces over to show the picture side
3. separate the edge pieces from the inside pieces
4. solve edge pieces
5. solve the puppies
6. solve the sign
7. solve the grass part
8. solve the gate
9. solve the hay
10. solve the barn
Notice how specific we can be now with our algorithm because we know the exact picture that
we are trying to build. Of course, the order in which we solve the particular parts of the image
is unimportant, but do you see how breaking a problem down into simpler, smaller procedures
can make it easy ?
Programming computers should be done in the same way. Always break your problem down
into simple pieces that you understand. Keep breaking it down until you have a simple
problem that you can understand.
This strategy of breaking down the problem into smaller pieces is often called divide and
conquer and it represents the fundamental principle for problem solving.
It is not a difficult strategy. As mentioned, we do this every day naturally in real life. Even
children can do this.
Algorithm1: DrawSimpleHouse
1. draw a square frame
2. draw a triangular roof
3. draw a door
- 32 -
COMP1405/1005 – An Introduction to Computer Science and Problem Solving Fall 2011
these are kinds “inner steps” that belong to that structure. Some people will us brackets to
indicate what is in or out of a control structure as follows:
repeat {
get a new light bulb
put it in the lamp
} until (lamp works or no more bulbs left)
repeat 3 times {
unplug lamp
plug into different socket
}
The point is that there are a variety of ways to write pseudocode. The important thing
to remember is that your algorithm should be clearly explained with no ambiguity as to
what order your steps are performed in.
Whether using a flow chart of pseudocode, you should test your algorithm by manually
going through the steps in your head to make sure that you did not forget a step or a
special situation. Often, you will find a flaw in your algorithm because you forgot about
a special situation that could arise. Only when you are convinced that your algorithm
will solve your problem, should you go ahead to the next step.
Consider our previous example of finding the average of a set of n grades stored in a
file. What would the pseudocode look like ? Here is an example of what it might look
like if we had the example of n numeric grades x1 ... xn that were loaded from a file:
Algorithm: DisplayGrades
- 20 -
COMP1405/1005 – An Introduction to Computer Science and Problem Solving Fall 2011
It would be wise to run through the above algorithm with a real set of numbers. Each
time we test an algorithm with a fixed set of input data, this is known as a test case.
You can create many test cases. Here are some to try:
For now, we will not discuss the details of how to produce the above source code. In
fact, the source code would vary depending on the programming language that was
used. Learning a programming language may seem difficult at first, but it will become
easier with practice.
The computer requires precise instructions in order to understand what you are asking it
to do. For example, if you removed one of the semi-colon characters ( ; ) from the
program above, the computer would become confused as to what you are doing
because the ( ; ) is what it understands to be the end of an instruction. Leaving one of
them off will cause your program to generate what is known as a compile error.
- 21 -
COMP1405/1005 – An Introduction to Computer Science and Problem Solving Fall 2011
The longer your program becomes, the more likely you will have multiple compile errors.
You need to fix all such compile errors before continuing on to the next step.
When you run your program, if all is well, you should see the
correct output. It is possible however, that your program works
correctly for some set of data input but not for all. If the output of your program is
incorrect, it is possible that you did not convert your algorithm properly into a proper
program. It is also possible that you did not produce a proper algorithm back in step 3
that handles all situations that could arise. Maybe you performed some instructions out
of sequence. Whatever happened, such problems with your program are known as
bugs.
You should fix as many bugs in your program as you can find. To find bugs effectively,
you should test your program with many test cases (called a test suite). It is also a
good idea to have others test your program because they may think up situations or
input data that you may never have thought of. The process of finding and fixing errors
in your code is called debugging and it is often a very time-consuming “chore” when it
comes to being a programmer. If you take your time to carefully follow problem solving
steps 1 through 3, this should greatly reduce the amount of bugs in your programs and
it should make debugging much easier.
For example, if the result of your program is a long list of numbers, but your intent was to
determine a pattern in the numbers or to identify some feature from the data, then simply
producing a list of numbers may not suffice. There may be a need to display the information
in a way that helps you visualize or interpret the results with respect to the problem. Perhaps
a chart or graph is needed.
It is also possible that when you examine your results, you realize that you need additional
data to fully solve the problem. Or, perhaps you need to adjust the results to solve the
problem more efficiently (e.g., your game is too slow).
It is important to remember that the computer will only do what you told it to do. It is up to you
to interpret the results in a meaningful way and determine whether or not it solves the original
problem. It may be necessary to re-do some of the steps again, perhaps going as far back as
step 1 again, if data was missing.
So there you have it. Those are the 6 steps that you should follow in order to solve problems
using computers. Throughout the course, you should try to use this approach for all of your
assignments. It is a good idea to practice problem solving to make sure that you understand
the process. Below are some practice exercises that will help you practice the first 3 steps of
the problem solving process. Later, you will gain experience with steps 4 through 6.
PRACTICE EXERCISES
Formulate a model and then develop an algorithm for each of the following problems. In each
case, start with a simple algorithm and then try to think about situations that can realistically go
wrong and make appropriate adjustments to the algorithm. Keep in mind that there is no
“right” answer to these problems. Everyone will have a unique solution.
- 23 -
COMP1405/1005 – An Introduction to Computer Science and Problem Solving Fall 2011
1. go to kitchen
2. open refrigerator
3. choose a drink
4. drink it
However, we could have come up with a more abstract (i.e., less detailed) algorithm as
follows:
1. get a drink
2. drink it.
Each of these algorithmic solutions solves the problem. So then, which one is best ? The
answer is not always easy. As a rule of thumb, we want to produce the simplest possible
algorithm that is easily understandable. While it is important to provide enough detail to be
able to properly describe the problem solution, it is also important not to get hung up on too
much detail so that the algorithm becomes cluttered and overly complicated.
- 24 -
COMP1405/1005 – An Introduction to Computer Science and Problem Solving Fall 2011
So, likely, the first of the three algorithmic solutions here would suffice as an adequate solution
to the problem. This idea of coming up with a clear algorithm without too much details is
known as abstraction.
We also see the idea of abstraction in 3D video game engines which hide details of objects
further away as they are not necessary until the game character moves closer to those objects.
It helps to speed up the game and reduce clutter while allowing the game player to focus on
the more important objects nearby:
Æ ÆÆ Æ ÆÆ Æ ÆÆ Æ Æ Abstraction Æ Æ ÆÆ Æ ÆÆ Æ ÆÆ Æ
We have just been discussing a kind of abstraction that allows our algorithm steps to be either
quite abstract (i.e., high level) or more detailed (i.e., low level). This kind of abstraction is
known as control abstraction.
- 25 -
COMP1405/1005 – An Introduction to Computer Science and Problem Solving Fall 2011
Often, in the cases where details are necessary, it is still possible to provide a higher-level
algorithm, while allowing the more specific details to be described in a sub-algorithm or sub-
program. As a result, we are able to describe an algorithm at multiple layers of abstraction.
For example, in our thirst-quenching scenario, we could use the details of our third very-
specific algorithmic solution but hide the more detailed portions in a sub-algorithm as follows:
AlgorithmX: QuenchThirst
1. get off couch
2. walk to kitchen
3. open refrigerator
4. perform SubAlgorithm1
5. close refrigerator
6. drink it
SubAlgorithm1: GetDrink
1. if there is a carton of lemonade or orange juice then {
2. take the carton
3. close refrigerator
4. go to the cupboard
5. open cupboard
6. take a glass
7. close cupboard
8. pour lemonade or juice into glass
9. go to refrigerator
10. open refrigerator
11. put carton in refrigerator
}
12. otherwise if there is a soda then {
13. take soda
14. open soda
}
Notice how the main algorithm is quite simple now with much less detail. SubAlgorithm1 is
not a stand-alone algorithm in that it cannot solve the problem on its own.
For example, it assumes that the person is standing in front of the refrigerator and that the
refrigerator door is open. We could also lessen these restrictions, for example, by
incorporating steps 3 and 5 of the main AlgorithmX into SubAlgorithm1 and moving them out
of the main algorithm. Notice the changes:
- 26 -
COMP1405/1005 – An Introduction to Computer Science and Problem Solving Fall 2011
AlgorithmX: QuenchThirst
1. get off couch
2. walk to kitchen
3. perform SubAlgorithm1
4. drink it
SubAlgorithm1: GetDrink
1. open refrigerator
2. if there is a carton of lemonade or orange juice then {
3. take the carton
4. close refrigerator
5. go to the cupboard
6. open cupboard
7. take a glass
8. close cupboard
9. pour lemonade or juice into glass
10. go to refrigerator
11. open refrigerator
12. put carton in refrigerator
}
13. otherwise if there is a soda then {
14. take soda
15. open soda
}
16. close refrigerator
Now we have abstracted out a little further by hiding some details within SubAlgorithm1. We
could also perform additional layers of abstraction by abstracting within SubAlgorithm1:
AlgorithmX: QuenchThirst
1. get off couch
2. walk to kitchen
3. perform SubAlgorithm1
4. drink it
SubAlgorithm1: GetDrink
1. open refrigerator
2. if there is a carton of lemonade or orange juice then {
3. perform SubAlgorithm2
}
4. otherwise if there is a soda then {
5. take soda
6. open soda
}
7. close refrigerator
- 27 -
COMP1405/1005 – An Introduction to Computer Science and Problem Solving Fall 2011
SubAlgorithm2: PourDrink
1. take the carton
2. close refrigerator
3. go to the cupboard
4. open cupboard
5. take a glass
6. close cupboard
7. pour lemonade or juice into glass
8. go to refrigerator
9. open refrigerator
10. put carton in refrigerator
Notice how SubAlgorithm1 became more abstract and easier to read. We can abstract out
in this manner as often as we feel it to be necessary. When do we decide to stop this kind of
abstraction ? Well, there is no fixed rule. However, when a sub-algorithm seems to be small
enough to understand or when it relates to a well-defined real-life group of actions, then it is
probably a good idea not to break it down any further.
However, it is possible that there is a portion of the algorithm that may be used by other
algorithms. For example, what if we wanted an algorithm to place a glass on the kitchen table
as part of getting prepared for dinner guests ? In that situation, you may realize that you will
need to perform the same steps again as listed in lines 3 through 6 of SubAlgorithm2.
Hence, it may be a good idea to create a SubAlgorithm3 as follows:
SubAlgorithm3: GetGlass
1. go to the cupboard
2. open cupboard
3. take a glass
4. close cupboard
Then of course we could make use of this SubAlgorithm3 within SubAlgorithm2 as well as
within our new algorithm for setting the table as follows:
SubAlgorithm2: PourDrink
1. take the carton
2. close refrigerator
3. perform SubAlgorithm3
4. pour lemonade or juice into glass
5. go to refrigerator
6. open refrigerator
7. put carton in refrigerator
- 28 -
COMP1405/1005 – An Introduction to Computer Science and Problem Solving Fall 2011
AlgorithmY: SetTableFor4
1. walk to kitchen
2. repeat 4 times {
3. perform SubAlgorithm3
4. place glass on table
5. perform SubAlgorithm4 // similar algorithm to get plate from cupboard
6. place plate on table
7. perform SubAlgorithm5 // similar algorithm to get fork & knife from drawer
8. place knife and fork on table
}
9. go back onto couch
Hopefully you see now how practical and powerful abstraction can be in making algorithms
simpler, more readable and ultimately more understandable.
In computer science we give a special name to the sub-algorithms. They are sometimes
called modules, functions or procedures. In fact, it is not a good idea to simply number all
the sub-algorithms but instead to give them meaningful names. As standard convention,
when naming a function or procedure, you should use letters, numbers and underscore (i.e., _)
characters but not any spaces or punctuation. Also, the first character in the name should be
a lower case letter. If multiple words are used as the name, each word except the first should
be capitalized. Lastly, we often use parentheses (i.e., ()) after the function or procedure name
to identify it as a sub-algorithm. We’ll see why later.
You should choose meaningful names that are not too long. Here are some meaningful
names that we could use for our sub-algorithms:
AlgorithmX: QuenchThirst
1. get off couch
2. walk to kitchen
3. chooseDrink()
4. drink it
and ..
- 29 -
COM
MP1405/1005 – An Introduc
ction to Comp
puter Science
e and Problem
m Solving Fall 2011
AlgorithmY Y: SetTablleFor4
1. waalk to kitchen
2. peat 4 time
rep es {
3. getGlas ss()
4. place glaass on tablee
5. getPlatee()
6. place plaate on table
e
7. getUten nsils()
8. place knnife and forkk on table
}
9. go back onto couch
Hennce each of these sub--algorithms go off and come backk (i.e., returrn) with som me kind of
obje
ect (i.e., drin
nk, glass, plate
p or uten
nsils). Whe en a sub-allgorithm comes back with
w some kind k
of ob
bject (such as the glas ss or plate) or value (ssuch as a numerical re
esult), we ca
all the sub--
algo
orithm a fun nction.
- 30
3 -
COMP1405/1005 – An Introduction to Computer Science and Problem Solving Fall 2011
Algorithm: SolvePuzzle
1. pour out all pieces on the table
2. flip all pieces over to show the picture side
3. separate the edge pieces from the inside pieces
4. solve edge pieces
5. repeat until there are no more easily identifiable pieces {
6. select pieces that form an easily identifiable part of the picture
7. put these selected pieces together
8. try to fit picture portion to border or to other picture portions
}
9. repeat until no more pieces remain {
10. pick piece up and try to fit it somewhere
11. if its location is unclear, put piece back in pile
}
Notice how the seemingly difficult problem is broken down into well-defined stages (or steps).
By solving the edge pieces first, it gives the person a better idea as to the size of the whole
image and it gives a feel for how the other parts of the puzzle will fit together.
In fact, steps 5 through 8 can be made into more specific steps if we had more information
about the puzzle. For example, consider how you would solve this image:
- 31 -
COMP1405/1005 – An Introduction to Computer Science and Problem Solving Fall 2011
Algorithm: SolvePuppies
1. pour out all pieces on the table
2. flip all pieces over to show the picture side
3. separate the edge pieces from the inside pieces
4. solve edge pieces
5. solve the puppies
6. solve the sign
7. solve the grass part
8. solve the gate
9. solve the hay
10. solve the barn
Notice how specific we can be now with our algorithm because we know the exact picture that
we are trying to build. Of course, the order in which we solve the particular parts of the image
is unimportant, but do you see how breaking a problem down into simpler, smaller procedures
can make it easy ?
Programming computers should be done in the same way. Always break your problem down
into simple pieces that you understand. Keep breaking it down until you have a simple
problem that you can understand.
This strategy of breaking down the problem into smaller pieces is often called divide and
conquer and it represents the fundamental principle for problem solving.
It is not a difficult strategy. As mentioned, we do this every day naturally in real life. Even
children can do this.
Algorithm1: DrawSimpleHouse
1. draw a square frame
2. draw a triangular roof
3. draw a door
- 32 -
COMP1405/1005 – An Introduction to Computer Science and Problem Solving Fall 2011
Obviously, we could have made a more elaborate house, but the solution above solves the
original problem. What if this was our solution:
Algorithm2: DrawMoreComplexHouse
1. draw a square frame
2. draw a triangular roof
3. draw a door
4. draw windows
5. draw chimney
6. draw smoke
7. draw land
8. draw path to door
Which is a “better” solution ? That’s not an easy question to answer. It depends on what
“better” means. If time is of the essence (e.g., as in playing a game of Pictionary) then
Algorithm1 would be better because it can be drawn faster. The more elaborate house of
Algorithm2 would perhaps be “better” if visual appearance was the aim, as opposed to speed
of drawing. This example brings up an important topic in computer science called algorithm
efficiency.
Normally in computer science we are interested in algorithms that are time and space
efficient, although there are also other ways (i.e., metrics) for measuring efficiency. For
example, Algorithm1 is more efficient in terms of time but it is also more efficient in terms of
ink or pencil usage as well as the amount of space that it takes on the paper. Algorithm2
may be more efficient in terms of detailing in that, depending on the context, it may take
longer for a person to guess what the drawing is (i.e., it could be confused with a barn, shed or
dog house if this was drawn in a farm setting). In this case, the extra time taken to distinguish
the house through the drawing of the windows and chimney may result in a quicker guess.
- 33 -
COM
MP1405/1005 – An Introduc
ction to Comp
puter Science
e and Problem
m Solving Fall 2011
A para
ameter iss a piece off data providded as inpuut to a function or proccedure.
We can supplyy an arbitrarry number in our algorrithm to spe
ecify how many
m place settings
s to set
as fo
ollows:
- 34
3 -
COMP1405/1005 – An Introduction to Computer Science and Problem Solving Fall 2011
AlgorithmY3: EfficientSetTableFor8
1. walk to kitchen
2. getGlasses(8)
3. place glasses on table
4. getPlates(8)
5. place plates on table
6. getUtensils(8)
7. place knives and forks on table
8. go back onto couch
Notice that we supplied a number 8 between the parentheses of our function. This is where
we normally supply additional information (i.e., parameters) to our functions. Now our function
is clear as to how many place settings will be made, whereas AlgorithmY2 was not clear.
But what would the getGlasses() function now look like ? Here was the 1-glass version:
GetGlass():
1. go to the cupboard
2. open cupboard
3. take a glass
4. close cupboard
Now we need to specify the parameter for the function and use it within the function itself:
GetGlasses(n):
1. go to the cupboard
2. open cupboard
3. repeat n times {
4. take a glass
}
5. close cupboard
Notice how the parameter is now being used within the function to get the necessary glasses.
The value of n will vary according to how we call the function. For example, if we use
getGlasses(8), then within the function, n will have the value of 8.
If we use getGlasses(4), then within the function, n will have the value of 4. So, the value for
parameter n will always be the number that was passed in when the function was called.
For algorithms that are the most general, we often use the letter n as a kind of “placeholder”
or “label” to indicate that we want the algorithm to work for any number from 0 to n. The “n”
itself is not a special letter, it is just commonly used. So, the statement getGlasses(n) is
indicating “get n glasses”, where n may be any integer number that we want.
- 35 -
COMP1405/1005 – An Introduction to Computer Science and Problem Solving Fall 2011
Obviously, there is a limit as to how many glasses a person could carry. However, to describe
an algorithm in a very general way, we use n to indicate that our algorithm will work for any
number from 0 to n. Using n instead of a fixed number, also allows us to compare two
algorithms in regards to their efficiency. That is, we can often compare the number of steps
that one algorithm requires with another algorithm.
For example, consider these two algorithms for setting the table for n people:
Algorithm A: Algorithm B:
1. walk to kitchen 1. walk to kitchen
2. repeat n times { 2. getGlasses(n)
3. getGlass() 3. place glasses on table
4. place glass on table 4. getPlates(n)
5. getPlate() 5. place plates on table
6. place plate on table 6. getUtensils(n)
7. getUtensils() 7. place knives and forks on table
8. place knife and fork on table 8. go back onto couch
}
9. go back onto couch
What if we defined “efficiency” in this example to refer to the “number of times we walked
back and forth between the cupboard and the table” ? Which algorithm is more efficient ?
Well each time through the loop, AlgorithmA makes 3 trips between the cupboard and table.
Since there are n place settings (i.e., n times through the loop), then the whole algorithm takes
n x 3, or 3n, steps. What about AlgorithmB ? It takes only 3 trips between the cupboard
and table altogether, regardless of how many place settings will be required.
So what can we conclude ? If we are setting a place for 1 person, either algorithm is good. If
setting for 2 people, then Algorithm B is twice more efficient than Algorithm A since it
requires half the travel between the cupboard and table. As n gets larger, the difference
becomes more significant. For example, if we are setting the table for 8 people, then
Algorithm A uses 8 times (total of 24) more trips than Algorithm B (which takes 3 trips).
Regardless of the number of place settings, Algorithm B has a fixed cost of 3 (in regards to
back and forth travels). Since this cost is fixed, we say that the algorithm has constant
efficiency in terms of our particular cost metric.
In contrast, Algorithm A is said to be linear in that the efficiency grows equally with respect to
the value of n. Sometimes an algorithm has a constant value times n (e.g., 3n). Since the 3 is
constant (i.e., fixed in our case, because we have exactly 3 kinds of items that we are placing),
the algorithm is still considered to be linear.
If we were to vary the 3 items to be n items (e.g., place 8 items at each of the 8 people’s place
settings, or 12 items at each of the 12 person’s place settings), then we would end up with an
n x n (or n2) algorithm which is called quadratic.
- 36 -
COMP1405/1005 – An Introduction to Computer Science and Problem Solving Fall 2011
Other common algorithm efficiency measures are logarithmic (i.e., log2n), cubic (i.e., n3) and
exponential (i.e., nn) … just to name a few.
Here are two graphs comparing various algorithm efficiencies as the value of n grows (graphs
shown at two different scales):
Algorithm Efficiency Algorithm Efficiency
Comparison Comparison
Constant Logarithmic (log n) Constant Logarithmic (log n)
Linear (n) Quadratic (n^2) Linear (n) Quadratic (n^2)
Cubic (n^3) Exponential (n^n) Cubic (n^3) Exponential (n^n)
50 1000
40 800
# of Steps
# of Steps
30 600
20 400
10 200
0 0
0 2 4 6 8 0 2 4 6 8
n n
Notice that the logarithmic, constant and linear algorithms are insignificant when compared
to the quadratic, cubic and (especially) exponential algorithms. You may notice as well
that the linear algorithm eventually passes the constant algorithm for larger values of n.
You may also notice that for very small values of n, the efficiency is generally not a big factor
but that the efficiency can quickly become an issue for larger values. Exponential algorithms,
for example, are usually unreasonable (i.e., useless) in practice except for very small values of
n.
Logarithmic solutions are often preferred since they are significantly more efficient than even
linear algorithms. For example, if n is 1,000,000 then a linear algorithm can take 1,000,000
steps whereas a logarithmic algorithm may take only 20 steps.
- 37 -
COMP1405/1005 – An Introduction to Computer Science and Problem Solving Fall 2011
For example, assume that 16 players entered a one-on-one tennis tournament. If there can be
no tie games how many games must be played if each player can be eliminated by one loss ?
We can figure this out with a simple table, doing a round-by-round count:
That was easy enough to figure out. But what if we had n players
(assuming that n is an even number) ? What would the table look like ?
The answer seems to be (n/2 + n/4 + n/8 + n/16 + n/32 + …). But how far does that series of
numbers go on ? We can try various values of n to find out. For example, if n=100:
(100/2 + 100/4 + 100/8 + 100/16 + 100/32 + … ) = (50 + 25 + 12.5 + 6.25 + 3.125 + 1.5625 ...)
Hmmm…seems like the values approach 0 but never quite there. Try n=128:
That one was easier. In fact, if you were to try various value of n, you would notice that the
games placed would total (n-1). It works out evenly for games that are powers of 2 (e.g., n=2,
4, 8, 16, 32, 64, 128, 256, 510, 1024, etc). In fact, the number of rounds played is log2(n)
(often written as just log(n) in computer science).
So, as you can see, trying to figure out efficiency with an unknown number is not always easy.
Nevertheless, it is important for you as a computer scientist to understand how to write efficient
algorithms that run faster, take up less computer memory or use less resources. We will
briefly discuss efficiency at various times throughout this course. However, you will take
further courses to investigate algorithmic efficiency much more thoroughly.
- 38 -