0% found this document useful (0 votes)
11 views53 pages

Understanding Algorithms in Computer Science

This document provides an in-depth exploration of algorithms, defining them as finite, ordered sequences of instructions designed to solve problems or perform tasks. It discusses the essential properties of algorithms, their importance in computer science, and various design techniques, emphasizing the need for efficiency, scalability, and adaptability. The document also outlines different algorithm paradigms, such as greedy methods, divide and conquer, and dynamic programming, highlighting their unique approaches to problem-solving.

Uploaded by

iyadesicp
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
11 views53 pages

Understanding Algorithms in Computer Science

This document provides an in-depth exploration of algorithms, defining them as finite, ordered sequences of instructions designed to solve problems or perform tasks. It discusses the essential properties of algorithms, their importance in computer science, and various design techniques, emphasizing the need for efficiency, scalability, and adaptability. The document also outlines different algorithm paradigms, such as greedy methods, divide and conquer, and dynamic programming, highlighting their unique approaches to problem-solving.

Uploaded by

iyadesicp
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

UDL — FSE/Informatique Algorithm analysis

This chapter introduces one of the most fundamental concepts in computer science:
the algorithm. Understanding algorithms is essential not only for programming, but also
for problem-solving in everyday life. We begin by asking a simple but important question:
What exactly is an algorithm? From there, we will define the concept precisely, examine
its role in solving computational problems, and explore how algorithms can be represented
in clear and structured ways. Different representation methods will be presented—such as
natural language, pseudo-code, flowcharts, and actual code implementations—so that
students can learn to express algorithmic solutions effectively and unambiguously. Finally,
we will turn to the process of designing algorithms, focusing on key principles like problem
decomposition, abstraction, and iterative refinement. These skills are vital in translating
problem requirements into executable instructions, ensuring that algorithms are not only
correct but also efficient and adaptable. By the end of this chapter, you should be able to:

• Clearly explain what an algorithm is and describe its essential characteristics.

• Understand why algorithms are central to computer science and everyday problem-
solving.

• Represent algorithms in natural language, pseudo-code, flowcharts, and program


code.

• Apply principles like problem decomposition and abstraction to design effective


algorithms.

• Use an iterative refinement process to improve clarity, correctness, and efficiency.

Khobzaoui Abdelkader 1 2025–2026


UDL — FSE/Informatique Algorithm analysis

1 Introduction: What is an Algorithm?


The word "algorithm" comes from the Latinized name of the legendary Muslim mathe-
matician Abu Ja’far Muhammad ibn Musa al-Khwarizmi, from the Khwarazm region in
the present-day Uzbekistan. When al-Khwarizmi’s mathematical texts were translated
into Latin around the 12th century, his name was Latinized as "Algorithmus". Over time,
this Latin version of his name was used to designate the procedures and calculations
described step by step in his algebra book. The English word "algorithm" is derived from
this Latinized version of al-Khwarizmi’s name and refers to the systematic calculation
procedures that he established in his early algebra books. In Computer science, the
term algorithm refers to a well-defined, step-by-step sequence of instructions that can be
executed by a computer to systematically solve a problem or accomplish a specific task.
Algorithms in essence behave like meticulous recipes for computer operations, providing
the precise instructions that computers need to generate the intended output or solution.
Everyday example: When you follow a cooking recipe, you are executing an algorithm:

• Input = ingredients.

• Steps = instructions (cut, mix, bake).

• Output = the finished dish.

In computer science, an algorithm is like a recipe for a computer: a finite list of clear
instructions that, if followed, produce the correct result.

Definition
An algorithm is a finite, ordered sequence of precise instructions designed to solve a
problem or perform a task.

Actually, the fundamental concept of an algorithm - a systematic method for solving


a class of problems - is not limited to computerized procedures. For example, the
manual techniques for multiplying and dividing whole numbers taught in schools can be
considered as human-executable algorithms. Euclid’s ancient algorithm for finding the
greatest common divisor of two numbers is another seminal mathematical example. Even
recipes can be considered rudimentary algorithms, as long as they provide unambiguous
instructions such as measurements rather than vague directives like "add salt to taste".

Consider this: is brushing your teeth an algorithm? If we look closely, it certainly


fits the definition. The process has a clear input—toothbrush, toothpaste, and water. It
follows a sequence of steps—applying the paste, brushing, and rinsing. Finally, it produces
an output—clean teeth. As long as these steps are clear, ordered, and finite, the activity
can indeed be seen as an algorithm.

Khobzaoui Abdelkader 2 2025–2026


UDL — FSE/Informatique Algorithm analysis

2 Essential Properties of Algorithms


Although our focus is on computer algorithms, it’s worth remembering that algorithmic
thinking transcends modern computing and has its roots in millennia of mathematical
and procedural problem-solving. All algorithms must satisfy the following criteria:

1. Input: Zero or more quantities are externally supplied.

2. Output: At least one quantity is produced.

3. Definiteness. Each instruction of the algorithm should be clearly defined, with no


ambiguity.

4. Finiteness: If we trace out the instructions of an algorithm, then for all cases, the
algorithm terminates after a finite number of steps.

5. Effectiveness. Every instruction must be very basic. It also must be feasible. An


algorithm is composed of a finite set of steps, each of which may require one or more
operations.

Example: Add two numbers.

• Input: A and B.

• Output: S = A + B.

• Steps:

1. Read A and B.
2. Compute S = A + B.
3. Display S.
4. Stop.

With the exponential growth of data and computing, robust algorithms are essential
for building efficient IT systems. As industry demand for optimized software continues to
grow, it’s imperative that we improve our algorithm design skills. Programming languages
provide the tools for implementation, but an understanding of algorithmic concepts and
data structures is essential for writing high-performance programs.

A deeper understanding of algorithms enables developers to create solutions that


can effectively adapt to increasing data sizes. Regardless of the programming language
chosen, algorithmic thinking and knowledge of data structures are indispensable in a
world increasingly dominated by code.

Khobzaoui Abdelkader 3 2025–2026


UDL — FSE/Informatique Algorithm analysis

The Algorithm development combines both art and skill, playing a crucial role in the
software creation process. Before a program is actually implemented, the fundamental step
of designing an algorithm takes center stage. Algorithms can be compared to step-by-step
problem-solving plans. These systematic procedures do not present the answers themselves,
but provide explicit instructions on how to get there. This strong stress on precisely defined
constructive procedures is a defining characteristic of computer science, distinguishing it
from other fields of study. This distinction is particularly evident when compared with
theoretical mathematics. In theoretical mathematics, practitioners are often content to
demonstrate the existence of a solution to a problem and, possibly, to study the properties
of this solution. Thus, the practical and methodical essence of algorithm development
makes computer science a discipline deeply rooted in pragmatic problem-solving, ensuring
that it provides tangible solutions to real-world challenges.

3 Importance of Algorithms
Algorithms are the fundamental building blocks of computer programs and systems. They
define the sequence of logical steps and operations required to complete tasks effectively
and efficiently. In computer science, algorithms are not just tools—they are the very
essence of problem-solving and innovation. Their importance can be highlighted in several
ways:

1. Problem-solving: Algorithms provide structured solutions to complex problems. In-


stead of approaching challenges randomly, they break problems down into systematic
steps that can be followed and replicated.

2. Efficiency: A well-designed algorithm ensures that a task is completed using minimal


time, memory, or other resources. Efficiency is critical in real-world systems, where
even small optimizations can translate into significant savings at scale.

3. Repeatability: Algorithms guarantee consistent and reliable solutions. Given the


same input, they always produce the same output, ensuring predictability and
trustworthiness in computing systems.

4. Scalability: As data sizes and user demands grow, efficient algorithms enable systems
to scale gracefully. An algorithm designed with scalability in mind ensures that
performance remains acceptable even as the workload increases dramatically.

To appreciate their significance, consider real-world applications. A search engine, for


instance, processes billions of queries every day. Without efficient algorithms, this task
would be practically impossible. It is the power of algorithms that allows such systems to
sift through massive datasets in fractions of a second. Example: Google, one of the world’s

Khobzaoui Abdelkader 4 2025–2026


UDL — FSE/Informatique Algorithm analysis

largest search engines, handles billions of searches daily. Thanks to advanced search and
ranking algorithms, results are delivered in milliseconds. Without these algorithms, users
would be left waiting for hours, making the service unusable. Algorithms, therefore, form

the invisible backbone of modern computing—powering everything from smartphones and


medical devices to financial systems and global communication networks.

4 Algorithm Design Techniques


Designing and analyzing algorithms is a disciplined process that ensures solutions are
not only correct but also efficient and adaptable. This process unfolds in four key stages.
First comes problem analysis, where requirements, inputs, outputs, and constraints are
clarified while also choosing appropriate data representations. Next is decomposition,
which breaks down a complex task into smaller, manageable subproblems. Then follows
abstraction, where the high-level steps of the solution are outlined independently of
implementation details. Finally, through iterative refinement, we start with a simple
approach and progressively improve efficiency, accuracy, and elegance.

Consider the problem of generating all prime numbers up to a given integer N . A


naive decomposition checks each number using a helper such as isPrime(x). While
correct, this approach becomes inefficient for large N . Abstraction introduces the idea
of systematically eliminating multiples, and refinement leads to the celebrated Sieve of

Eratosthenes, which marks multiples of discovered primes up to N , achieving far greater
efficiency. This evolution illustrates how an algorithm grows from a basic idea into a
refined solution balancing correctness, scalability, and efficiency.

Algorithm 1: Sieve of Eratosthenes


Input : N ∈ N
Output : All primes ≤ N
1 Create a boolean array isP rime[2..N ] ← true

2 for p ← 2 to ⌊ N ⌋ do

3 if isP rime[p] then


4 for k ← p2 to N byp do
5 isP rime[k] ← false

6 return all i such that isP rime[i] = true

Once these methodological steps are in place, the next challenge is choosing an appro-
priate paradigm—a general strategy of reasoning tailored to the problem’s structure. Algo-
rithm designers rarely start from scratch; instead, they adapt well-established paradigms.

Khobzaoui Abdelkader 5 2025–2026


UDL — FSE/Informatique Algorithm analysis

At the implementation level, choices such as recursion versus iteration, deterministic


versus randomized strategies, or serial versus parallel execution affect performance and
applicability. At a higher conceptual level, paradigms provide reusable blueprints and
embody philosophies of problem solving.

Among the most influential paradigms are the following:


Greedy Method. Make the best local choice at each step in hopes of reaching a
global optimum. Effective when the greedy-choice property and optimal substruc-
ture hold. Examples: Activity Selection, Huffman coding, Kruskal’s and Prim’s algorithms.

Algorithm 2: Greedy Skeleton


1 Initialize feasible solution S
2 while decision remains do
3 pick locally best option according to criterion C
4 if feasible then
5 add to S
6 else
7 discard
8

9 return S

Divide & Conquer. Split the problem into independent subproblems, solve each
recursively, and combine results. Examples: Merge Sort, Quick Sort, Strassen’s matrix
multiplication, Closest Pair of Points.

Algorithm 3: Divide & Conquer Skeleton


1 if small instance then
2 solve directly; return
3

4 Split input into subinstances


5 Solve subinstances recursively
6 Combine partial results

Dynamic Programming (DP). Suitable for problems with overlapping subproblems


and optimal substructure. Avoids recomputation by memoization or tabulation. Examples:
edit distance, LCS, Bellman–Ford, Floyd–Warshall.

Khobzaoui Abdelkader 6 2025–2026


UDL — FSE/Informatique Algorithm analysis

Algorithm 4: DP Skeleton (Bottom-Up)


1 Define states and base cases
2 Order states topologically
3 for s ∈ S in order do
4 DP [s] ← combine predecessors
5 return DP [goal]

Backtracking. Explore solution spaces incrementally, abandoning infeasible paths as


soon as possible. Common in constraint satisfaction problems such as N-Queens, Sudoku,
and graph coloring.

Algorithm 5: Backtracking Skeleton


1 if partial is complete then
2 report solution; return
3

4 foreach choice in candidates do


5 if feasible then
6 apply choice; recurse; undo
7

Branch & Bound. Extends backtracking with bounding functions to prune large parts
of the search space. Widely used for solving NP-hard problems such as TSP or integer
programming.

Algorithm 6: Branch & Bound Skeleton (Best-First)


1 best ← −∞; PQ ← {root}
2 while PQ not empty do
3 node ← pop with highest bound
4 if bound(node) ≤ best then
5 continue
6

7 if node is leaf then


8 best ← max(best, value)
9 else
10 expand children, compute bounds, push if better
11

12 return best

Khobzaoui Abdelkader 7 2025–2026


UDL — FSE/Informatique Algorithm analysis

Randomized Algorithms. Introduce randomness to simplify design or improve expected


efficiency. Examples include Randomized QuickSort, primality testing, and randomized
graph algorithms.

Algorithm 7: Randomized QuickSort Skeleton


1 if n ≤ 1 then
2 return
3

4 p ← random pivot; swap with A[n]


5 q ← partition around pivot
6 QuickSort(A[1..q − 1])
7 QuickSort(A[q + 1..n])

These paradigms are complementary rather than competing. Each arises from a distinct
intuition: greedy methods aim for immediacy by always taking the locally best option,
divide & conquer relies on breaking a problem into smaller pieces, dynamic programming
takes advantage of repeated subproblems, backtracking explores all possibilities system-
atically, branch & bound reduces this exploration by using bounds to cut off hopeless
paths, and randomized algorithms bring in chance to simplify design or improve average
performance. None of these strategies is universally superior—their effectiveness depends
very much on the structure of the problem at hand.
To understand their differences more clearly, let us discuss them in everyday terms:

• Greedy algorithms are fast and simple because they always choose what seems best
in the moment. For example, if you want to give change with the fewest coins, you
might always pick the largest coin available first. This works well in many currency
systems, but it can fail if local choices do not add up to the truly best overall solution.
The strength of greedy methods is speed and simplicity, but the weakness is that
they can miss the global optimum.

• Divide & Conquer splits a problem into smaller subproblems, solves each one, and
then combines the results. A classic example is sorting a list by dividing it in half,
sorting each half, and then merging. This approach is powerful because smaller tasks
are easier to handle, and together they solve the bigger one. However, repeatedly
splitting and combining adds extra work, and recursion may lead to overhead in
implementation.

• Dynamic Programming (DP) is useful when the same subproblems appear again
and again. Instead of solving them repeatedly, DP remembers solutions in a table
and reuses them. Imagine climbing stairs where at each step you can take one or

Khobzaoui Abdelkader 8 2025–2026


UDL — FSE/Informatique Algorithm analysis

two stairs: many paths overlap, and DP avoids recalculating them. This makes
problems that would otherwise take a very long time (growing explosively with input
size) solvable in reasonable time. The tradeoff is that DP usually requires significant
memory and careful design of the table.

• Backtracking explores the entire space of possible solutions step by step. When a
choice leads to a dead end, it goes back (“backtracks”) and tries another. Think
of solving a Sudoku puzzle: you fill in numbers until you get stuck, then erase and
try a different option. Backtracking is complete—it can always find a solution if
one exists—but it may take an extremely long time for large problems because the
number of possibilities grows very quickly.

• Branch & Bound builds upon backtracking by adding a pruning idea. It estimates
whether a partial solution could ever lead to a better result than the best one found
so far. If not, it abandons that path immediately. This can save enormous amounts of
work in practice, but its success depends on how good the estimation (the “bound”)
is. Weak bounds lead to almost the same work as plain backtracking.

• Randomized algorithms introduce randomness into the process, often to simplify


decisions or avoid worst-case scenarios. For instance, choosing a pivot at random in
QuickSort prevents consistently bad splits on already sorted data. Randomization
can give very elegant and fast algorithms on average, but it also means that results
or running time may vary from one execution to another, and an error analysis is
usually required.

In summary, each paradigm embodies a distinct way of thinking about problem solving.
Greedy algorithms are like sprinters: they move quickly by always grabbing the best
immediate option, but sometimes this shortsightedness prevents them from reaching the
true goal. Divide & Conquer is more like a strategist: it breaks the battle into smaller,
manageable fights, solves each one, and then combines the results. Dynamic Programming
is the careful planner: instead of redoing the same work, it remembers past results and
builds on them, turning otherwise impossible tasks into manageable ones. Backtracking is
the explorer who tries every path through a maze; it guarantees that a way out will be
found if one exists, but may take a very long time. Branch & Bound is a smarter explorer:
it uses estimates to avoid going down paths that are clearly hopeless, saving time while
still ensuring correctness. Finally, Randomization is the gambler: it introduces chance
into the process, which may sound risky, but often leads to surprisingly fast and elegant
solutions in practice. As summarized in Table 1, each paradigm has its own assumptions,
strengths, and limitations.

Khobzaoui Abdelkader 9 2025–2026


UDL — FSE/Informatique Algorithm analysis

Table 1: Comparison of algorithmic paradigms

Paradigm Description (assumptions, strengths, issues)

Greedy Assumes greedy-choice property and optimal substructure. Strength:


very fast and simple. Issue: may fail if local choices do not lead to a
global optimum.

Divide & Conquer Assumes independence of subproblems and cheap combination.


Strength: classic O(n log n) algorithms. Issue: recursion overhead
and inefficient base cases.

Dynamic Programming Assumes overlapping subproblems and optimal substructure.


Strength: converts exponential problems into polynomial ones. Issue:
large memory use and tricky state design/order.

Backtracking Assumes cheap feasibility checks. Strength: can find all exact
solutions. Issue: exponential blow-up in the worst case.

Branch & Bound Relies on admissible bounds for pruning. Strength: significant reduc-
tion of search space. Issue: weak bounds lead to little improvement.

Randomized Uses probabilistic decisions. Strength: simplicity and expected


speed. Issue: performance variance, requires careful analysis or
derandomization.

In real-world applications, algorithm designers rarely stick to a single paradigm. Instead,


they often hybridize approaches: a randomized pivot may guide a divide & conquer strategy,
greedy heuristics may be woven into dynamic programming to cut down computation, or
bounding estimates may be layered on top of backtracking to prune large parts of the search
space. As Table 1 also highlights, no single approach is universally best—the right choice
depends on the problem’s structure. True mastery lies not only in understanding each
paradigm separately but also in knowing when—and how—to combine them effectively.
With practice, this skill becomes a toolbox that allows designers to adapt their strategy to
the unique structure of each problem.

In summary, algorithm design operates at two complementary layers: the methodologi-


cal stages of analysis, decomposition, abstraction, and refinement, and the higher-level
choice and combination of paradigms. The prime-number example illustrates the former,
while the paradigms offer versatile strategies for the latter. Together with pseudocode
skeletons, flowcharts, and comparative analysis, these tools equip the reader with a con-
ceptual foundation that will be expanded in subsequent chapters through rigorous proofs
and practical applications.

Khobzaoui Abdelkader 10 2025–2026


UDL — FSE/Informatique Algorithm analysis

5 Algorithm presentation
Once we have conceived an algorithm, the next step is to express it in a way that is
understandable to both humans and, eventually, machines. The manner in which an
algorithm is presented depends heavily on the target audience and the intended use. For
instance, a computer scientist designing a new sorting algorithm for a research paper might
favor pseudocode, while a beginner student might understand the same algorithm more
clearly if represented through a flowchart. Professional programmers, on the other hand,
must eventually translate these representations into executable program code. There are

four major modes of representation we will discuss here: natural language, pseudocode,
flowcharts, and program code. Each offers a unique perspective and serves a specific
purpose.

1. The most straightforward method for expressing an algorithm is through natural


language, that is, ordinary human language used in everyday communication. This
mode of description is analogous to the instructions found in recipes or manuals, as
it requires no specialized background knowledge and is immediately accessible to
a general audience. For example, the procedure for preparing a cup of tea may be
articulated as follows:

Boil water. Place a tea bag in a cup. Pour the boiling water into the cup.
Allow the tea to steep for 3 minutes. Remove the tea bag. Add sugar or
milk if desired. Serve.

Although natural language descriptions are highly intuitive and easily comprehensible
for human readers, they suffer from inherent imprecision. Phrases such as “steep”
or “if desired” are open to multiple interpretations, and crucial details like exact
quantities or precise durations are often omitted. As a result, natural language
cannot be directly interpreted or executed by a computer, which requires instructions
that are unambiguous and formally defined. The principal advantage of natural
language lies in its accessibility, simplicity, and the absence of any need for prior
technical knowledge; however, these strengths are offset by its weaknesses, namely
its susceptibility to ambiguity, its lack of precision, and its unsuitability for direct
translation into executable code.

2. Pseudocode
Pseudocode constitutes an intermediate representation between natural language and
actual programming code, providing a structured yet flexible means of describing al-
gorithms. It employs English-like words combined with programming-style constructs
such as assignments, conditionals, and loops, thereby allowing designers to articulate

Khobzaoui Abdelkader 11 2025–2026


UDL — FSE/Informatique Algorithm analysis

the logical steps of an algorithm without being constrained by the strict syntactic
rules of any particular programming language. This approach eliminates much of
the ambiguity present in natural language while remaining language-independent
and easy to refine during the design process. For instance, the task of finding the
maximum of three numbers can be expressed in pseudocode as follows:

Algorithm 8: Maximum of three numbers


Input : a, b, c
Output : Maximum value among a, b, c
1 max ← a; if b > max then

2 max ← b
3 if c > max then
4 max ← c
5 return max;

These example illustrates how pseudocode combines readability with a clear logical
structure that mirrors actual programming practices, thereby making translation into
any formal programming language straightforward. Despite its advantages of acces-
sibility, clarity, and adaptability, pseudocode remains non-executable by computers
and presupposes a basic understanding of programming constructs. Nevertheless, its
balance between simplicity and rigor has established pseudocode as the most widely
used method for presenting algorithms in textbooks, academic publications, and
early stages of system design.

3. A flowchart is a graphical representation of an algorithm that uses standardized


symbols, summarized in Table 2, to depict the sequence of operations, decisions, and
input/output activities.

Table 2: Standardized flowchart symbols

Symbol Meaning
Oval Start or End point
Rectangle Process or instruction
Diamond Decision point
Parallelogram Input or Output action

This visual approach makes the control flow of an algorithm easy to grasp at a glance,
offering an intuitive means of understanding both simple and complex processes.

Khobzaoui Abdelkader 12 2025–2026


UDL — FSE/Informatique Algorithm analysis

As an illustrative example, consider the task of determining the maximum of three


numbers. The corresponding flowchart is shown in Figure 1, where parallelograms
represent input and output actions, rectangles represent assignment or comparison
operations, and diamonds capture the decision logic.

Start

Read a, b, c

m←a

Yes
b > m? m←b

No

Yes
c > m? m←c

No

Print m

End

Figure 1: Flowchart for finding the maximum of three numbers

Khobzaoui Abdelkader 13 2025–2026


UDL — FSE/Informatique Algorithm analysis

The same algorithm can be expressed textually using pseudocode, as shown in


algorthm 8

Flowcharts are particularly effective in highlighting logical structures such as decisions


and branches, which makes them excellent tools for beginners, for teaching, and for
documentation or communication across teams. However, their usefulness decreases
with large or complex algorithms, since the diagrams can quickly become cluttered and
difficult to interpret, and creating or modifying them is often more time-consuming
than working with pseudocode. Despite these drawbacks, their accessibility, clarity,
and standardized symbolic conventions ensure that flowcharts remain an important
and widely used method of representing algorithms, particularly in the early stages
of design.

4. Code: Once an algorithm has been clearly expressed in pseudocode or as a flowchart,


the final step is to translate it into a programming language. At this stage, the
algorithm is written as syntactically correct statements that a computer can interpret
and execute directly. Unlike pseudocode, program code is strictly formal, requires
adherence to rigid syntax rules, and is unforgiving of even minor errors. For instance,
the task of finding the maximum of three numbers can be expressed in both Python
and C, as shown below:

Python C

def max_of_three(a, b, c): int max_of_three(int a, int b, int c)


{
max_val = a int max_val = a;
if (b > max_val)
if b > max_val: {
max_val = b max_val = b;
}
if c > max_val: if (c > max_val)
max_val = c {
max_val = c;
return max_val }
return max_val;
}

Khobzaoui Abdelkader 14 2025–2026


UDL — FSE/Informatique Algorithm analysis

These representations are executable and produce concrete outputs, ensuring un-
ambiguous interpretation by the computer. Moreover, program code can be tested,
debugged, and integrated into larger systems, which makes it indispensable for
practical applications. However, this precision comes at the cost of accessibility.
Writing code requires familiarity with the syntax and semantics of a specific pro-
gramming language, and it is often less readable for non-technical audiences. In
addition, program code is tied to the rules of the chosen language, which reduces its
portability across different programming environments. Despite these limitations,
coding remains the essential step that transforms abstract algorithmic ideas into
actionable instructions that a machine can execute.

These representations are executable and produce concrete outputs, ensuring un-
ambiguous interpretation by the computer. Moreover, program code can be tested,
debugged, and integrated into larger systems, which makes it indispensable for
practical applications.

However, this precision comes at the cost of accessibility. Writing code requires
familiarity with the syntax and semantics of a specific programming language, and
it is often less readable for non-technical audiences. In addition, program code is
tied to the rules of the chosen language, which reduces its portability across different
programming environments. Despite these limitations, coding remains the essential
step that transforms abstract algorithmic ideas into actionable instructions that a
machine can execute.

Algorithms can be expressed in multiple forms, each suited to a different purpose and
audience. No single representation is universally superior; rather, each has distinct
strengths and limitations that make it more appropriate in specific contexts. Natural
language provides an intuitive and accessible description that anyone can understand,
making it ideal for initial brainstorming or informal communication. Pseudocode, on
the other hand, offers a structured and language-independent way to design and refine
algorithms, striking a balance between readability and formality. Flowcharts emphasize
visualization and are particularly effective when teaching, documenting, or communicating
the logic of an algorithm to audiences with diverse technical backgrounds. Finally, program
code is the only representation that can be directly executed by a computer, but it requires
technical expertise and adherence to strict syntactic rules.

Table 3 summarizes the main advantages and disadvantages of these four methods of
algorithm representation.

Khobzaoui Abdelkader 15 2025–2026


UDL — FSE/Informatique Algorithm analysis

Table 3: Comparison of algorithm representations

Method Advantages Disadvantages


Natural Lan- Intuitive, accessible to all Ambiguous, imprecise, can-
guage readers, requires no techni- not be executed by a com-
cal background puter
Pseudocode Clear and structured, Cannot be directly executed,
focuses on logic rather requires some knowledge of
than syntax, language- programming constructs
independent
Flowcharts Visual clarity, excellent for Diagrams become cluttered
communication and teach- for complex algorithms,
ing, highlights control flow more time-consuming to
create and modify
Program Code Precise, unambiguous, exe- Language-specific, requires
cutable and testable, inte- technical skills, less accessi-
grates into larger systems ble to non-programmers

In practice, these representations are often used in sequence. An algorithm may first
be described informally in natural language, then refined into pseudocode, illustrated with
a flowchart to aid understanding, and finally translated into program code for execution.
This layered approach ensures that the algorithm is both conceptually clear and practically
implementable. For example, when teaching a new algorithm, a teacher might begin with
a natural language explanation to introduce the idea, use pseudocode to demonstrate the
logical steps, reinforce comprehension with a flowchart, and finally present program code
to show how the algorithm is realized in practice.

6 General Structure of an Algorithm


Every algorithm, regardless of whether it is presented in pseudocode, drawn as a flowchart,
or implemented in a programming language, follows a logical and systematic structure. This
structure is essential for three reasons: it ensures clarity (the algorithm is understandable),
consistency (it respects a predictable format), and translatability (it can be easily converted
into real code). At its core, an algorithm is built from three fundamental components: the

header, the environment, and the body. These three pillars form the universal skeleton of
structured programming.

Khobzaoui Abdelkader 16 2025–2026


UDL — FSE/Informatique Algorithm analysis

1. The Algorithm Header


The header identifies the algorithm, much like the title of a book or the name of a recipe. It
always begins with the keyword Algorithm, followed by a meaningful name that captures
the procedure’s purpose.
Guidelines for writing headers:
• Use names that are explicit and self-descriptive, e.g., ComputeAverage, SortList,
or FindMaximum.

• Avoid vague or cryptic names such as A1, Test, or XYZ, which hinder readability.

• In programming practice, this corresponds to naming functions, procedures, or classes


in a way that immediately reveals their purpose.
A well-chosen header serves as the first form of documentation for your algorithm. It
communicates intent clearly to collaborators, readers, and even your future self.

2. The Environment
The environment sets up the context in which the algorithm operates. It is like preparing
a kitchen before cooking: you must gather the right ingredients (variables) and tools
(constants or data types) before starting.
The environment typically contains:
• Custom types (optional): defined with the keyword type, e.g., a Student type with
fields Name, Age, and Grade.

• Constants: fixed values that never change during execution, such as π = 3.14159
or a maximum buffer size. Constants provide clarity and avoid repeating “magic
numbers” in the body.

• Variables: named memory locations where data is stored, modified, and reused.
Each variable should be declared with its type (integer, real, string, Boolean, etc.)
to ensure correctness and avoid ambiguity.
Variables
x, y, m : Real
Constants
PI = 3.14159

Pedagogical note: In compiled languages like C, Pascal, or Java, explicit declarations are
mandatory because the compiler needs to allocate memory and verify valid operations.
Even in flexible languages like Python, planning your variables and constants in advance
is a sign of disciplined and maintainable algorithm design.

Khobzaoui Abdelkader 17 2025–2026


UDL — FSE/Informatique Algorithm analysis

3. The Algorithm Body


The body is the heart of the algorithm: the sequence of instructions that transform the
input into the desired output. It always begins with the keyword Begin and ends with
the keyword End. Instructions in between are executed one after another unless control
structures (conditionals, loops, or calls to sub-algorithms) modify the natural flow.
Analogy. If the header is the name of a recipe and the environment is the list of ingredients,
then the body is the step-by-step cooking process. The order is crucial: doing steps out of
sequence (e.g., baking before mixing ingredients) will result in failure, just as misordering
instructions may break an algorithm.
Begin
instruction 1;
instruction 2;
...
instruction n;
End

In practice, algorithm bodies often include:


• Simple instructions (assignments, input/output).

• Conditional instructions (if, else, switch) to make decisions.

• Iterative instructions (while, for, repeat) to perform repeated actions.

• Procedure or function calls to reuse existing algorithms and avoid redundancy.


To see how these three components fit together, consider the following algorithm that
computes the average of three numbers:

Algorithm 9: ComputeAverage
Input : Three real numbers a, b, c
Output : The average of a, b, c
1 begin

// Environment
2 Variables
3 a, b, c, avg : Real
// Body
4 Begin
5 avg ← (a + b + c)/3
6 write avg
7 End

Khobzaoui Abdelkader 18 2025–2026


UDL — FSE/Informatique Algorithm analysis

This example demonstrates the three-part skeleton in action:

• The header announces the algorithm’s goal (ComputeAverage).

• The environment declares the necessary variables.

• The body carries out the computation and produces the result.

The general structure of an algorithm—header, environment, body—is universal and


independent of the specific programming language. Mastering this skeleton equips students
with a clear mental model for designing algorithms, ensuring that their solutions are
readable, reusable, and easily implemented. From simple classroom exercises to complex
software systems, every algorithm ultimately rests upon this same logical foundation.

7 Fundamental Instructions of Algorithms


Algorithms, in their simplest essence, are built from a small number of fundamental
instructions. These instructions represent the building blocks from which any complex
procedure can be constructed. Just as a language is based on an alphabet, an algorithm
relies on a reduced set of elementary operations that allow data to be manipulated, the
progress of a computation to be controlled, and a result to be produced. The study of these

instructions is therefore essential for every student of algorithmics. It not only enables the
understanding of how to translate an abstract idea into a clear sequence of actions, but
also helps in grasping the universal logic that underpins computer programs. Fundamental
instructions are generally grouped into three categories: sequence instructions, which
are executed in a specific order; selection instructions, which direct the flow based on a
condition; and repetition instructions, which allow tasks to be handled iteratively. Before

moving on to more elaborate data structures and complex design paradigms, it is necessary
to master these basic components. They form the foundation on which all algorithms rest,
from the simplest to the most sophisticated.

7.1 Simple Instructions


Simple instructions are the atoms of programming. Just as atoms combine to form
molecules, simple instructions combine to form algorithms and complete programs. Al-
though each instruction looks small, together they build powerful computations. At the

most fundamental level, simple instructions let us:

• Store values in memory (assignment): give names to data so that they can be
reused later,

Khobzaoui Abdelkader 19 2025–2026


UDL — FSE/Informatique Algorithm analysis

• Read data from the outside world (input): capture information from the user or
another system,

• Display results (output): communicate the outcome of the computation back to the
user.

Assignment

An assignment instruction means: “take the value of an expression and store it in a


variable.” This is how a program remembers information. Once assigned, the variable’s
value can be reused or updated.

x ← 5 // store the value 5 in x


y ← x + 2 // compute x + 2 = 7, then store in y
x ← y // copy the value of y (7) into x
// the old value of x (5) is overwritten

Rule: Always evaluate the right-hand side expression first, then assign the result to the
left-hand side variable.
This is different from mathematics: in math, x = x + 2 makes no sense, but in
programming, it means “take the current value of x, add 2, and store the result back into
x.”

Input and Output

Programs are not useful unless they interact with their environment.

• Input provides the program with external values.

• Output presents the results to the user.

Read(x, y) // user types two numbers


Write("The sum is: ", x+y) // program prints their sum

This simple pair of instructions transforms a passive program into an interactive tool.

Example: Average of Two Numbers

Let us illustrate assignment, input, and output with a concrete problem: computing the
average of two numbers.

Khobzaoui Abdelkader 20 2025–2026


UDL — FSE/Informatique Algorithm analysis

Algorithm 10: Average of Two Numbers


Input : x, y ∈ R
Output : m = (x + y)/2
1 begin

2 read x, y // get two numbers from the user


3 m ← (x + y)/2 // compute their average
4 write “The average is: ”, m // display result

Notice how the three kinds of simple instructions appear:

1. Input: we read x and y,

2. Assignment: we calculate and store the average in m,

3. Output: we display the result.

Implementation in Real Languages

To better understand how pseudocode maps into real programming languages, let us
translate the previous algorithm into Python and C.

Listing (2) C Implementation


#include <stdio.h>

int main() {
double x, y
printf("Ent
scanf("%lf"
Listing (1) Python Implementation printf("Ent
# Average of two numbers scanf("%lf"
x=float(input("Enter first number: "))
y=float(input("Enter second number: ")) m = (x + y)
printf("The
m = (x + y) / 2 return 0;
print("The average is:", m) }

Figure 2: Side-by-side implementations of the average calculation: (a) Python, (b) C.

Both programs implement the exact same logic:

• Input: reading x and y,

Khobzaoui Abdelkader 21 2025–2026


UDL — FSE/Informatique Algorithm analysis

• Assignment: computing m,

• Output: printing the result.

This side-by-side comparison shows that while the syntax may differ between languages,
the underlying concepts remain universal.

7.2 Conditional Instructions and Structures


In algorithm design, conditional instructions are the cornerstone of decision-making. They
give an algorithm the ability to follow different paths of execution depending on whether
a condition evaluates to true or false. Without them, every program would be limited to a
rigid sequence of steps, unable to react to different inputs or situations. One can think of
conditionals as crossroads in a journey: when arriving at an intersection, the traveler must
choose the appropriate road according to the signs; in the same way, an algorithm decides
which branch to take depending on the outcome of a logical test.

This idea is very intuitive because we apply it constantly in everyday life. For example,
if it rains, we take an umbrella; otherwise, we go without. At a traffic light, if the light is
green, we move forward; if it is red, we stop. Algorithms mimic this reasoning through
conditional structures, allowing them to adapt dynamically to different scenarios. Broadly
speaking, conditionals come in several forms—complete, reduced, nested, multiple-choice,
and compound—but the essence is always the same: make a decision, then act accordingly.

The most common form of branching is the complete conditional, expressed as if/else.
In this structure, a test determines which of two mutually exclusive blocks is executed: if
the condition is true, one block runs; if false, the other block runs.

Start

Yes No
Condition?

Execute Block 1 Execute Block 2

End

Figure 3: Flowchart of a complete conditional structure (if/else).

The pseudocode in Algorithm 11 illustrates this by determining whether a number is


positive, zero, or negative.

Khobzaoui Abdelkader 22 2025–2026


UDL — FSE/Informatique Algorithm analysis

Algorithm 11: Sign of a Number


Input : x ∈ R
Output : Message about x
1 begin

2 if x > 0 then
3 write “Positive”
4 else if x = 0 then
5 write “Zero”
6 else
7 write “Negative”

Start

Read x

Yes
x > 0? Write “Positive”

No
Yes
Write “Zero” x = 0?

No

Write “Negative”

End

Figure 4: Flowchart of Algorithm 11 (Sign of a Number).

This step-by-step process exemplifies the general principle: exactly one block is executed
depending on the outcome of the condition. The flowchart in Figure 3 visually confirms
this behavior: the diamond-shaped decision node splits the execution into two distinct
paths—“Yes” and “No”—that rejoin after the block is executed. Sometimes there is no
need for an alternative block; we only want to act if a condition is true. This leads to
the reduced conditional, or simple if. If the condition holds, a block of instructions is

Khobzaoui Abdelkader 23 2025–2026


UDL — FSE/Informatique Algorithm analysis

executed; otherwise, execution continues immediately after the test.

Algorithm 12 shows this principle applied to the parity of a number.

Algorithm 12: Parity Test with Reduced Conditional

Input :n ∈ Z

1 begin
2 if n mod 2 = 0 then
3 write “Even”

The flowchart in Figure 5 reflects the same logic: if the condition evaluates to true,
the block runs; if not, the program bypasses the block entirely and continues.

Start

No
Condition?

Yes

Execute Block

End

Figure 5: Flowchart of a reduced conditional structure (single if).

In real-world problems, two branches are often insufficient. We may need to test several
alternatives in sequence. This situation calls for nested conditionals, or the if / else if
/ else chain. Algorithm 13 shows a typical case where a grade is classified as Excellent,
Pass, or Fail depending on thresholds.

Khobzaoui Abdelkader 24 2025–2026


UDL — FSE/Informatique Algorithm analysis

Algorithm 13: Classification with Nested Conditionals

Input : grade ∈ [0, 20]

1 begin
2 if grade ≥ 16 then
3 write “Excellent”
4 else if grade ≥ 10 then
5 write “Pass”
6 else
7 write “Fail”

The flowchart in Figure 6 makes the branching explicit: the program checks each condi-
tion in order until one is satisfied, at which point execution continues in the corresponding
block.

Start

Yes No
Grade ≥ 16?

Yes No
Excellent Grade ≥ 10?

Pass Fail

End

Figure 6: Flowchart of nested conditionals (if / else if / else).

When the program must select one action among many based on the value of a single
variable, a multiple-choice structure is more efficient. In C, this appears as a switch/case
statement. Algorithm 14 illustrates how a month number is mapped to its name.

Khobzaoui Abdelkader 25 2025–2026


UDL — FSE/Informatique Algorithm analysis

Algorithm 14: Month Name with Multiple Choice


Input : m ∈ {1, . . . , 12}
1 begin

2 switch m
3 otherwise 1
4 write “January”
5 otherwise 2
6 write “February”
7 . . . otherwise 12
8 write “December”
9

10 write “Invalid”

The flowchart in Figure 7 shows this fan-out clearly: the decision branches into many
cases depending on the variable’s value, with one optional default branch.

Start

Read variable v

Which value?

v=1 v=2 v=n other

case 1: Action 1 case 2: Action 2 case N : Action N default: Action

End

Figure 7: Flowchart of a multiple-choice structure (switch/case). All branches rejoin


after their actions (as with break). Dashed edge (optional) illustrates fall-through when
break is omitted.

Khobzaoui Abdelkader 26 2025–2026


UDL — FSE/Informatique Algorithm analysis

Finally, conditions themselves may be composed using logical operators. With AND
(&&), all sub-conditions must be true; with OR (||), at least one must be true. This
allows complex decision criteria.
Algorithm 15 shows a student admission rule: admitted if the average is at least 10
and both fundamental subjects are above 5.

Algorithm 15: Admission Rule with Compound Condition


Input : avg, note1, note2
1 begin

2 if avg ≥ 10 and note1 > 5 and note2 > 5 then


3 write “Admitted”
4 else
5 write “Failed”

The flowchart in Figure 8 captures this logic: execution proceeds only if both conditions
are satisfied; otherwise, the program branches to the failure block.

Start

No
avg ≥ 10?

Yes

No Yes
note1 > 5 and note2 > 5?

Failed Admitted

End

Figure 8: Flowchart of a compound condition using logical AND.

Across these five variants, the unifying theme is that conditionals allow programs to
adapt to data and circumstances. Pseudocode provides a compact formal description,
while flowcharts give an intuitive visual picture of the branching logic. Together, they
highlight how conditional instructions form the essential building blocks of decision-making
in algorithms.

Khobzaoui Abdelkader 27 2025–2026


UDL — FSE/Informatique Algorithm analysis

7.3 Iterative Instructions (Loops)


When solving problems, we often encounter tasks that require repeating the same action
many times. Writing the same instruction again and again would be tedious, error-prone,
and inefficient. Instead, algorithms and programming languages provide a mechanism
called iteration or looping. A loop is an instruction that tells the computer to repeat

a block of steps until a certain condition is met (for example, "repeat while there are
still items in the list" or "repeat until the number becomes zero"). This makes loops
powerful tools for handling repetitive tasks such as summing numbers, processing arrays,
or searching through data. In this subsection, we will explore the main types of loops,

how they are expressed in pseudocode, and how they are implemented in programming
languages. We will also see how loops connect directly to the idea of algorithm efficiency,
since repetition often dominates the cost of computation.

The For Loop

A For loop is the clearest way to say “repeat this action a fixed number of times.” You
choose a start value for a counter (often i = 1), specify an end test (for example i ≤ n),
perform the body once for that counter value, and then update the counter by a fixed step
(usually +1 when counting up or −1 when counting down) before checking the test again;
the loop stops as soon as the test becomes false. This pattern fits tasks where the number
of repetitions is known in advance—printing the numbers from 1 to n, accumulating a
total, or producing a neat countdown. To anchor the idea, Algorithm 16 computes the
sum 1 + 2 + · · · + n. This simple algorithm demonstrates how a for-loop can accumulate
values step by step until the final result is obtained.

Algorithm 16: Sum from 1 to n


Input : n ∈ N
Output : S = ni=1 i
P

1 begin

2 S←0
3 for i ← 1 to n do
4 S ←S+i
5 write “Sum = ”, S

Notice how the loop structure provides a systematic way to repeat an operation (adding
i) until the condition is no longer satisfied. Algorithm 17 shows a countdown variant,
where the same looping mechanism works in reverse. Instead of incrementing, the index
decrements until it reaches the stopping condition.

Khobzaoui Abdelkader 28 2025–2026


UDL — FSE/Informatique Algorithm analysis

Algorithm 17: Countdown n → 1


Input : n ∈ N
1 begin

2 for i ← n ; i ≥ 1 ; i ← i − 1 do
3 write i

The control flow of a for-loop can be summarized as follows:


initialize → test → body → step → test again.
This cycle continues until the condition becomes false, at which point the loop terminates.

Start

Initialize i ← 1

Yes
i≤n? write i

No
i←i+1

End

Figure 9: Control flow of a typical for-style loop.

Finally, Listings 3 and 4 translate the same logic into executable Python and standard
C. Placing them side by side helps students directly compare syntax and style across
languages.

Khobzaoui Abdelkader 29 2025–2026


UDL — FSE/Informatique Algorithm analysis

Listing 4: C: print 1..n and countdown


#include <stdio.h>
Listing 3: Python: print 1..n and
countdown void print_one_to_n(int n)
def print_one_to_n(n): for (int i = 1; i <
for i in range(1, n+1): printf("%d
print(i, end=" ") }
}
def countdown(n):
for i in range(n, 0, -1): void countdown(int n) {
#from n down to 1 for (int i = n; i >
print(i, end=" ") printf("%d
}
}

The While Loop

The while loop is a flexible control structure that allows repeated execution of a block of
instructions as long as a given condition holds. Its main strength lies in situations where
the number of repetitions is not known in advance. Figure 10 illustrates its use in both C
and Python, where a simple counter is incremented until the condition fails.

int i = 1;
while (i <= n) { i = 1
printf("%d ", i); while i <= n:
i = i + 1; // progress print(i, end=" ")
} i = i + 1 # pro

(a) C version. (b) Python version.

Figure 10: Comparison of while loop in C and Python.

The power of the construct becomes clear when it is combined with pseudocode and
flowcharts, which help to solidify the intuition behind this essential programming tool.

Several common applications highlight the importance of the while loop: input
validation (“while the entered value is not in the allowed range, ask again”), state-driven
processing (“while the file still has data,” “while the connection is open”), and cases where
the loop may legitimately do nothing (if the initial state already violates the condition,

Khobzaoui Abdelkader 30 2025–2026


UDL — FSE/Informatique Algorithm analysis

the loop never executes). Example 18 demonstrates input validation, where the program
repeatedly asks for a grade until a value in [0, 20] is given.

Algorithm 18: Read a valid grade in [0, 20]


1 begin
2 read g
3 while (g < 0) or (g > 20) do
4 write “Invalid, try again.”
5 read g
6 write “Accepted grade = ”, g

The C implementation, shown below the pseudocode, makes the same structure explicit
with a test and repeated reads inside the loop. In contrast, Example 19 illustrates the
sentinel pattern: numbers are read and their squares are printed until the user enters 0.

Algorithm 19: Squares until the user enters 0 (sentinel)


1 begin
2 read n // priming read
3 while n ̸= 0 do
4 write “Square = ”, n × n
5 read n
6 write “Done.”

Because the loop checks the condition first, a priming read is performed before the
loop begins, and each iteration ensures progress by reading again inside the loop.

Reasoning about a while loop requires attention to two aspects: termination and
correctness. Termination is guaranteed if a quantity in the loop moves steadily toward
ending the process (for instance, a counter approaching a bound, or successive inputs
eventually matching a sentinel). Correctness can be understood through informal invariants,
such as “i is the next number to print” or “all previously printed squares correspond to
earlier inputs.” Tracing three or four iterations on paper is a powerful way to make these
invariants visible. Nevertheless, the construct is prone to common pitfalls. A missing

update step inside the body (e.g., forgetting i = i + 1 or omitting the update read)
results in an infinite loop. An incorrect initial state may cause the loop to skip or overshoot,
while a wrongly formulated condition can make the loop never run. In C, one must also
be careful not to confuse = (assignment) with == (comparison), since writing while (i
= 0) accidentally assigns zero to i and prevents the loop from executing. In summary,

Khobzaoui Abdelkader 31 2025–2026


UDL — FSE/Informatique Algorithm analysis

the while loop embodies the principle of “look first, act later.” Its correct use depends on
starting with a valid initial state, writing a condition that will eventually become false, and
ensuring clear progress inside the loop body. By combining careful reasoning, pseudocode,
actual implementations, and supporting figures, one can develop a robust understanding
of this versatile programming construct.

The Repeat–Until Loop

The repeat–until loop is a post-test control structure, which means the body of the loop
is always executed at least once before any condition is checked. This property makes
it fundamentally different from the while loop, which evaluates its condition first and
may skip the body entirely if the test fails immediately. To build intuition, imagine being
told to “do the task once, then check if it is time to stop; if not, repeat.” This model is
especially appropriate for situations where an action must happen at least once, such as
prompting a user for input, displaying a menu, or generating initial output before deciding
whether to continue.

Algorithm 20 illustrates this with a simple example: reading integers and printing their
squares until the user enters 0. Notice how the loop’s stop condition (n = 0) is tested after
the body executes, guaranteeing at least one read and one print.

Algorithm 20: Read integers and print their squares until the user enters 0
1 begin
2 repeat
3 read n
4 write “Square = ”, n × n
5 until n = 0

In many programming languages, such as C, the equivalent structure is written as


‘do ... while (cond);‘. Here, it is important to pay attention to condition polarity: in
pseudocode, Until(stop) indicates a stop condition, whereas in C the ‘do ... while‘ loop
continues as long as the condition is true. Thus, Repeat ... Until(stop) corresponds
to ‘do ... while (!stop)‘. The C code for the square-printing example is shown below, where
the loop continues as long as the entered number is not zero:

int n;
do {
scanf("%d", &n);
printf("Square = %d\n", n*n);
} while (n != 0); // continue while n != 0 (stop when n == 0)

Khobzaoui Abdelkader 32 2025–2026


UDL — FSE/Informatique Algorithm analysis

The same behavior could be simulated with a while loop, but it would require a
priming read before the loop starts, which makes the structure slightly less natural:

// equivalent using a priming read + while


int n;
scanf("%d", &n); // priming read: ensures body runs at least once
while (n != 0) {
printf("Square = %d\n", n*n);
scanf("%d", &n);
}

The flow of execution for a repeat–until loop is captured visually in Figure 11. The
diagram emphasizes that the sequence begins with the body (read and compute the square),
followed by testing the condition. If the condition is false, control flows back to repeat
the body; if true, the loop terminates. This visual reinforcement is useful for students to
grasp why at least one iteration is always guaranteed.

Start

Read n

Write “Square = ” n × n

n=0?
No

Yes

End

Figure 11: Repeat–Until: at least one iteration; stop when condition becomes true.

From a reasoning perspective, the correctness of a repeat–until loop depends on two


key ideas: termination and invariants. Termination requires identifying a measure that

Khobzaoui Abdelkader 33 2025–2026


UDL — FSE/Informatique Algorithm analysis

moves toward the stop condition (for example, the user eventually enters 0, or a counter
decreases). Invariants are properties that remain true after each iteration, such as “every
square printed corresponds to a previously read value.” By tracing a few iterations by
hand, students can verify both termination and invariants.

However, there are common pitfalls to avoid. A frequent mistake is getting the
condition polarity wrong: writing ‘do ... while (stop);‘ produces the opposite of repeat
until stop. Another issue is forgetting that the body always runs at least once, meaning
any variables needed in the first iteration must be initialized or read inside the loop body.
Finally, complex or side-effecting expressions in the stop test should be avoided to keep
the logic clear.

The pedagogical takeaway is straightforward: use repeat–until when the problem


naturally requires “do first, check later.” This pattern ensures that necessary actions
such as user prompts or initial computations occur before testing the stop condition. By
carefully reasoning about polarity, termination, and invariants, and by referring to the
pseudocode (Algorithm 20) and the flowchart (Figure 11), students can confidently design
correct and reliable repeat–until loops.

Counters and Accumulators

In many algorithms, two simple but extremely powerful programming patterns appear
again and again: the counter and the accumulator. A counter is a variable whose purpose is
to keep track of how many iterations have been executed (commonly named i, k, or count).

An accumulator, on the other hand, is a variable used to aggregate values step by step,
such as computing a running sum or a product. These two patterns, often used together,
form the backbone of loops that process sequences of values. A very classical example is
the computation of the sum of n numbers.

The general idea is straightforward: initialize the sum to zero, then for each of the
n values read from the input, add it to the accumulator. At the end, the accumulator
contains the desired total. Algorithm 21 illustrates this process in pseudocode and listing
5 its C equivalent implementation.

Khobzaoui Abdelkader 34 2025–2026


UDL — FSE/Informatique Algorithm analysis

Algorithm 21: Summation using a counter (i) and an accumulator (S)


Input : n ∈ N
Output : S = ni=1 xi
P

1 S ← 0

2 for i ← 1 to n do

3 read x
4 S ←S+x
5 write “Sum = ”, S

Listing 5: C implementation with counter (loop index) and accumulator (running sum)
long long S = 0, x;
for (int i = 1; i <= n; ++i) {
scanf("%lld", &x);
S += x;
}
printf("Sum = %lld\n", S);

One of the most recurring patterns in algorithm design is the combined use of a counter
and an accumulator. A counter is a variable whose role is to keep track of the number of
iterations performed, often written as i, k, or count. An accumulator, on the other hand,
is a variable used to store and update a running value across iterations, such as a sum or
a product. These two mechanisms appear so often that mastering them is essential for
building correct and efficient loops.

A classical example is the computation of the sum of n numbers. The algorithm


proceeds in three phases: initialize the accumulator to zero, use the counter to iterate
exactly n times, and at each iteration read a new number and add it to the accumulator.
Figure 12 illustrates this process both in pseudocode (left) and in its C implementation
(right). In the pseudocode, the role of the counter variable i and the accumulator S is
explicit, while in C the for-loop naturally controls the counter and the accumulator is
updated inside the loop body.

Khobzaoui Abdelkader 35 2025–2026


UDL — FSE/Informatique Algorithm analysis

Input : n ∈ N
Output : S = ni=1 xi
P

1 begin
long long S
2 S←0
for (int i
3 for i ← 1 to n do sca
4 read x S +
5 S ←S+x }
6 write “Sum = ”, S printf("Sum

(a) Pseudocode with counter (i) and


accumulator (S) (b) C implementation of the same algorithm

Figure 12: Two complementary views of the same idea: counters (loop index) and
accumulators (running sum).

Khobzaoui Abdelkader 36 2025–2026


UDL — FSE/Informatique Algorithm analysis

This example highlights two important programming habits:

• Always initialize your accumulator before entering the loop (here, S ← 0). Without
this, the final result would be meaningless.

• Use the counter variable to ensure that the loop executes exactly n times, no more
and no less, which guarantees correctness.

To strengthen intuition, it is very useful to trace a small example by hand. Suppose


n = 3 and the numbers entered are 5, 7, and 2. Initially, S = 0. After reading the first
number, S = 5. After the second, S = 12. After the third, S = 14. At the end of the
loop, the program outputs 14, which matches the expected total 5 + 7 + 2. Writing out
these intermediate steps helps students clearly see the interplay between the counter,
which controls the number of repetitions, and the accumulator, which stores the evolving
sum. The counter-and-accumulator pattern may appear simple, but it is extraordinarily

powerful. It underpins algorithms for computing sums, averages, factorials, counting


how many inputs satisfy a condition, or even more advanced statistical measures. By

consistently initializing the accumulator, designing the loop with a reliable counter, and
updating both correctly at each step, students gain a robust foundation for solving a wide
range of algorithmic problems with confidence.

Nested Loops (Repeating Inside Repeating)

In programming, it is not only possible but also very common to place one loop inside
another. This situation is called a nested loop. The outer loop controls the broader
repetition, while the inner loop completes all its iterations every time the outer loop
executes once. A useful mental image is that of a school timetable: for each day of the
week (outer loop), you attend all the scheduled lessons (inner loop). Only when the day is
over does the counter move to the next day. A simple example is printing a rectangle of

stars. Instead of storing anything in a matrix or special data structure, we can directly
use two loops: the outer loop for the rows, and the inner loop for the columns. Figure 13
illustrates this with pseudocode on the left and C code on the right. In both cases, the
logic is the same: for each row, print c stars, then move to the next line.

Khobzaoui Abdelkader 37 2025–2026


UDL — FSE/Informatique Algorithm analysis

Input : r, c ∈ N
1 begin for (int i
2 for i ← 1 to r do for
3 for j ← 1 to c do
4 write “*” }
pri
5 write “\n” // move to
}
next line

(a) Pseudocode version (b) C implementation

Figure 13: Printing a r × c rectangle of stars using nested loops.

To understand the flow of control, look at the conceptual flowchart in Figure 14. The
outer loop variable i controls how many rows are printed. For each value of i, the inner
loop variable j runs from 1 to c, printing one star at a time. Once the inner loop finishes,
a newline is written and i increments to move to the next row. This cycle continues until
all r rows have been printed.

Start

i←1

Yes
i≤r? j←1

No

No
j≤c?

write “\n” Yes

write “*”

i←i+1

j ←j+1

End

Figure 14: Flowchart of nested loops: the inner loop repeats fully for each iteration of the
outer loop.

Khobzaoui Abdelkader 38 2025–2026


UDL — FSE/Informatique Algorithm analysis

For students, the key to nested loops is to imagine the inner loop as a complete task
that runs to completion before the outer loop can continue. In this rectangle example,
every row is finished (all c stars printed and a newline written) before the next row begins.
By tracing a small case, say r = 2 and c = 3, you can see exactly how six stars are printed
in the first line, then another three in the second line, giving a 2 × 3 block.

Nested loops let us build structured repetitions within repetitions. They are the
foundation for processing grids, matrices, tables, and more advanced algorithmic patterns
such as searching in two dimensions or generating combinations. By mastering the
interaction of counters in both the outer and inner loops, students gain a powerful toolset
for tackling problems where repetition naturally occurs on multiple levels.

Case Study: Factorial in Three Representations and Three Loops

To connect algorithmic thinking with practice, consider computing the factorial of n


(product of all integers from 1 to n). This example illustrates two fundamental ideas:
repetition, since we must multiply successively by each integer from 2 up to n, and
accumulation, since we maintain a running product inside a variable called an accumulator.

The general strategy is simple: initialize an accumulator fact to 1, then loop across
the required values, updating fact each time. At the end of the process, the accumulator
contains the final factorial value. Algorithm 22 expresses this reasoning in pseudocode,
using a for loop to emphasize the initialization, repetition, and accumulation steps.

Algorithm 22: Factorial in pseudocode

Input :n ∈ N

Output : n!

1 f act ← 1

2 for i ← 2 to n do
3 f act ← f act × i
4 write f act

To complement the pseudocode, Figure 15 shows a flowchart representation of the same


algorithm. The diagram makes the cycle explicit: we begin with initialization, test whether
the loop condition still holds, update the accumulator, and repeat until the condition fails.
Once the loop terminates, the final value is written out.

Khobzaoui Abdelkader 39 2025–2026


UDL — FSE/Informatique Algorithm analysis

Start

Read n

i←2
f act ← 1

Yes f act ← f act × i


i≤n?
i←i+1

No

Write f act

End

Figure 15: Flowchart representation of the factorial algorithm.

In real programming practice, factorial can be implemented with different loop con-
structs. Figure 16 demonstrates three variants in both Python and C: using a for loop,
using a while loop, and using a repeat–until style (in C: do...while, in Python: while
True with a break). The syntax differs, but the underlying algorithmic pattern remains
identical: start with fact = 1, repeat multiplication, and stop once the loop condition is
no longer satisfied.

Khobzaoui Abdelkader 40 2025–2026


UDL — FSE/Informatique Algorithm analysis

C (for loop)
long long factorial
Python (for loop) long long f
for (int i
def factorial_for(n):
fac
fact = 1
}
for i in range(2, n+1):
return fact
fact *= i
}
return fact

C (while loop)
Python (while loop)
long long factorial
def factorial_while(n):
long long f
fact, i = 1, 2
int i = 2;
while i <= n:
while (i <=
fact *= i
fac
i += 1
i++
return fact
}
return fact
Python (repeat–until style) }

def factorial_repeat(n):
fact, i = C1,(do–while
2 loop)
if n == 0: # 0! = 1
long long factorial
return 1
long long f
while True:
int i = 2;
fact *= i
if (n == 0)
i += 1
do {
if i > n: # stop condition
fac
break
i++
return fact
} while (i
return fact
}

Figure 16: Factorial implemented with three different loop types in Python and C.

As a concrete worked example, suppose n = 5. The accumulator evolves step by step:


f act = 1 → 2 → 6 → 24 → 120. At the end of the loop, the correct answer 5! = 120 is
obtained.
For quick revision, Table 4 compares the three loop constructs side by side for Python

Khobzaoui Abdelkader 41 2025–2026


UDL — FSE/Informatique Algorithm analysis

and C. This tabular view reinforces the idea that although the syntax differs across
languages and constructs, the essence of the algorithm is constant.

Table 4: Factorial implementations with different loop constructs in Python and C.

Loop Type Python C


for
fact = 1 fact = 1
for i in range(2, n+1): for (int
fact *= i fact *=

while
fact, i = 1, 2 fact = 1
while i <= n: while (i
fact *= i
i += 1
}

repeat–until
fact, i = 1, 2 fact = 1
while True: do {
fact *= i
i += 1
if i > n: break } while

In summary, computing factorial provides an excellent case study that bridges abstract
algorithmic thinking and concrete programming practice. Across the different loop con-
structs we studied—for, while, and repeat–until—the essential algorithmic pattern
is always the same: initialize, repeat, accumulate, and stop. What changes is not the
underlying idea, but the placement of the test and the style of iteration. Recognizing this
fact helps students transfer their reasoning seamlessly from pseudocode, to flowcharts, to
actual implementations in C or Python. This transfer builds confidence and flexibility:
you learn to choose the right tool for the situation, while keeping the algorithm clear and
correct.

Khobzaoui Abdelkader 42 2025–2026


UDL — FSE/Informatique Algorithm analysis

To summarize the lessons in a compact framework that you can reuse for many problems
involving repetition:

• For loop: use a counter with explicit bounds and step — ideal when the number of
iterations is known in advance.

• While loop: test at the beginning — allows zero, one, or many iterations, depending
on the initial condition.

• Repeat–Until loop: test at the end — guarantees at least one iteration before
stopping.

• Counters & accumulators: use counters to track progress and accumulators to


aggregate results (sum, product, average, etc.).

• Nested loops: repeat one loop completely inside another to construct multi-line
patterns or solve multidimensional problems.

This unified view of loops provides a practical toolkit: you can describe repetition clearly,
select the structure that matches your problem, visualize the process with simple diagrams,
and translate it unambiguously from pseudocode into a working program in standard C
(or any other language).

8 Recursion and Backtracking


When tackling complex problems, computer scientists often rely on techniques that allow
a task to be decomposed into smaller, more manageable subproblems. Two of the most
powerful such techniques are recursion and backtracking. Although closely related, they
serve different purposes:

• Recursion captures the principle of self-similarity: the solution to a problem is


expressed in terms of smaller instances of the same problem.

• Backtracking extends recursion by exploring an entire search space systematically,


but it intelligently prunes paths that cannot lead to valid or optimal solutions.

Together, these methods form the backbone of many algorithmic strategies, from
mathematical computations (factorials, Fibonacci) to constraint satisfaction problems
(Sudoku, N-Queens).

Khobzaoui Abdelkader 43 2025–2026


UDL — FSE/Informatique Algorithm analysis

8.1 Recursion
In the previous chapters, we studied loops and iterative structures that allow the repetition
of instructions. These tools are powerful for many tasks, but some problems are more
naturally described by recurrence, such as computing mathematical sequences, evaluating
factorials, or traversing hierarchical structures like trees. In such cases, the concept of
recursion becomes indispensable. Recursion consists of defining an algorithm that calls
itself until a base case is reached, which ensures termination and prevents infinite regress.
This makes recursion one of the most elegant and fundamental ideas in computer science.
At its heart, a recursive algorithm mirrors the self-similar nature of many problems:
it solves a larger instance by reducing it into smaller, simpler ones of the same type,
eventually combining their solutions into the final answer.

Every recursive algorithm is built on two essential components:

(1) the base case, which directly solves the simplest instance of the problem and
guarantees termination; and

(2) the recursive step, which defines how a larger problem can be decomposed into
one or several smaller subinstances, solved recursively, and then combined.

Without a base case, recursion would never stop; without a recursive step, it would
not progress.

Classic examples illustrate these principles clearly. The factorial function is defined
mathematically as 
1 if n = 0,


n! =

n · (n − 1)!

if n > 0,

which directly translates into recursive pseudocode (Algorithm 23). Each call to fact(n)
invokes a smaller problem fact(n-1) until the base case 0! = 1 is reached. The call stack
records each pending multiplication, which are resolved in reverse order as the recursion
unwinds.

Khobzaoui Abdelkader 44 2025–2026


UDL — FSE/Informatique Algorithm analysis

Algorithm 23: Factorial (recursive)


Input : n ∈ N
Output : n!
1 if n = 0 then

2 return 1
3 else
4 return n × f act(n − 1)

Another classical case is the Fibonacci sequence, defined as

F (0) = 0, F (1) = 1, F (n) = F (n − 1) + F (n − 2) for n ≥ 2,

which can also be expressed recursively (Algorithm 24). Here, each term is defined in terms
of the two preceding ones, and the algorithm makes two recursive calls at every step. This
leads to an elegant but computationally inefficient implementation, as many subproblems
are recomputed. This inefficiency naturally motivates the study of optimization techniques
such as memoization or iterative formulations.

Algorithm 24: Fibonacci (recursive)

Input :n ∈ N

Output : F (n)

1 if n = 0 then
2 return 0
3 else if n = 1 then
4 return 1
5 else
6 return F (n − 1) + F (n − 2)

From an execution perspective, recursion is deeply tied to the mechanism of the


call stack. Each time a function (or procedure) is called, the computer allocates
a new stack frame that stores the function’s parameters, local variables, and the
return address (i.e., where the execution should continue once the function finishes).
When a recursive algorithm calls itself, a new frame is pushed onto the stack, layered
on top of the previous ones. This process continues until a base case is reached.
At that point, the recursion stops generating new calls, and the results begin to
travel back: the most recent call finishes, its frame is popped off the stack, and con-

Khobzaoui Abdelkader 45 2025–2026


UDL — FSE/Informatique Algorithm analysis

trol returns to the previous one. This gradual reversal is known as unwinding the recursion.

This stack-based execution model explains why recursion feels so natural for many
mathematical and hierarchical problems. It allows us to write programs that are compact,
elegant, and very close to the way problems are defined in mathematics (for example,
factorials or the Fibonacci sequence). However, the same mechanism also brings certain
costs. Each new recursive call consumes additional memory on the stack. If the recursion
is very deep (for example, when processing a huge input without careful design), this can
lead to significant overhead or even a stack overflow error, where the program runs out
of memory reserved for the stack. That is why understanding both the beauty and the
limitations of recursion is important for every programmer.

To guide the design of recursive algorithms, computer scientists often rely on a general
recursive pattern. This template captures the essence of most recursive solutions and serves
as a checklist when writing your own algorithms:

1. Base case test. Check whether the current instance is a simplest possible case. If
it is, return the direct answer immediately. This ensures termination and prevents
infinite recursion.

2. Decomposition. If the problem is not a base case, divide it into smaller subproblems
I1 , I2 , . . . , Ik that resemble the original problem in structure.

3. Recursive calls. Solve each subproblem by calling the same algorithm recursively.
At this stage, new stack frames are created and pushed, one for each call.

4. Combination of results. Once the recursive calls finish and their answers are returned,
combine these partial results to produce the solution to the original problem.

This structure can be visualized in a flowchart (see Figure 17), where the algorithm first
checks for a base case, otherwise decomposes the instance, calls itself recursively, and finally
merges the results. Thinking in terms of this general template helps students develop
recursive solutions systematically, and also makes it easier to reason about correctness,
efficiency, and termination.

Recursion is especially powerful because it naturally mirrors the structure of many


hierarchical systems, where a large object is made up of smaller objects of the same kind.
In such cases, a recursive approach often feels more intuitive than an iterative one, since the
algorithm can directly “follow” the structure of the data or problem. Common examples
include:

Khobzaoui Abdelkader 46 2025–2026


UDL — FSE/Informatique Algorithm analysis

START

Base case? YES


(Step 1) Return direct answer

NO
Decompose into subproblems
I1 , I2 , . . . , Ik
(Step 2)

Make recursive calls


to solve subproblems
(Step 3)

Combine results from recursive calls


(Step 4)

Return combined result

END

Figure 17: General Recursive Pattern.

• File systems. A folder may contain both files and other subfolders, which in turn
may contain even more files and folders. A recursive algorithm to list all files simply
processes the current folder, and whenever it encounters a subfolder, it calls itself on
that subfolder. This continues until the recursion reaches an empty folder, which is

Khobzaoui Abdelkader 47 2025–2026


UDL — FSE/Informatique Algorithm analysis

the base case.

• Trees. Trees are inherently recursive structures: each tree is made up of smaller
subtrees. Many fundamental operations—such as computing the height of a tree,
counting the total number of nodes, or searching for a specific value—are easily
described recursively. For example, the height of a tree is one plus the maximum of
the heights of its left and right subtrees, with the base case being an empty tree of
height zero.

• Divide-and-conquer algorithms. Some of the most famous algorithms in computer


science rely on recursion to break a large problem into smaller subproblems of the
same type. Examples include:

– Mergesort: divide the list into two halves, sort each half recursively, then merge
them.
– Quicksort: partition the list around a pivot, recursively sort the two partitions,
and combine the results.
– Binary search: compare the target element to the middle of the sorted array;
depending on the result, recursively search either the left or right half.

In all these cases, the recursion continues until the subproblems become trivial (such
as a list of length 1 in sorting), which serves as the base case.

These examples highlight why recursion is not just a programming trick, but a natural
way of thinking about problems whose structure is inherently recursive. By identifying
the base case and recursive step, we can often translate real-world hierarchies directly into
elegant algorithms.

8.2 Backtracking
Backtracking is essentially recursion with the addition of systematic trial and error.
The algorithm incrementally builds a candidate solution and, whenever it detects that
the current partial solution P cannot possibly be extended to a valid full solution, it
backtracks to explore a different branch. This makes backtracking a refined form of
exhaustive search: still exponential in theory, but drastically reduced in practice by pruning.

The pseudocode in Algorithm 25 highlights the recursive structure of backtracking:


try a choice, recurse, undo it, and continue.
This cycle is reflected in the flowchart of Figure 18, which shows how backtracking
repeatedly enumerates candidates, tests feasibility, recurses when possible, and undoes the
choice otherwise.

Khobzaoui Abdelkader 48 2025–2026


UDL — FSE/Informatique Algorithm analysis

Algorithm 25: Backtracking Template


Input : Partial solution P
Output : Reports all valid solutions
1 if P is complete and valid then
2 report P ; return
3 for choice ∈ Candidates(P ) do
4 if Feasible(P , choice) then
5 Apply(choice, P )
6 Backtrack(P ) // recursive exploration
7 Undo(choice, P )

Start with
partial solution P

Is P complete & valid? Yes Report P and Return

No

For each choice ∈ Candidates(P )

Feasible(P , choice)? Yes Apply(choice, P )

Backtrack(P )
No

Undo(choice, P )

End

Figure 18: Backtracking flow: enumerate candidates, test feasibility, recurse, and undo.

Backtracking is widely used to solve problems where the solution space is extremely
large and cannot be explored exhaustively in a naive way. The key idea is to build a

Khobzaoui Abdelkader 49 2025–2026


UDL — FSE/Informatique Algorithm analysis

solution step by step, and whenever the current partial solution is found to be invalid or
unpromising, the algorithm backtracks—it undoes the last choice and tries a different
path. This makes backtracking a systematic form of trial-and-error search that remains
practical thanks to pruning.

Some classical applications include:

• N-Queens problem. The task is to place n queens on an n × n chessboard such that


no two queens attack each other. A backtracking algorithm places queens row by
row; if a conflict occurs (two queens in the same column or diagonal), it backtracks
and tries a new column. The recursion ends when all rows are filled safely.

• Sudoku. Filling a 9 × 9 grid so that every row, column, and 3 × 3 box contains digits
1 to 9 exactly once can be framed as a constraint satisfaction problem. Backtracking
chooses an empty cell, tries candidate numbers, and recursively fills the grid. If no
number works, it backtracks to a previous cell and changes the assignment.

• Graph coloring. The goal is to assign colors to graph vertices so that no two adjacent
vertices share the same color. Backtracking selects a vertex, assigns a color, and
recurses to the next vertex. If a conflict arises, it backtracks and tries a different
color. This is widely used in scheduling and register allocation problems.

• Subset sum. Given a set of numbers, the task is to determine whether a subset sums
to a target value. Backtracking decides for each element whether to include it or
not, recursively exploring both possibilities. If the partial sum exceeds the target or
cannot possibly reach it, the algorithm backtracks early and prunes the branch.

In each of these examples, the recursive pseudocode and the corresponding flowchart
(see Figure 18) help students visualize the decision-making process: the algorithm tries a
choice, explores deeper when feasible, and retreats when blocked. This combination of
recursion, pruning, and systematic search illustrates why backtracking is such a versatile
tool for solving complex combinatorial problems.

To conclude, recursion and backtracking are two fundamental problem-solving


paradigms that every computer scientist must master.

• Recursion provides a natural and elegant way to express problems that exhibit
self-similarity or hierarchical structure. By breaking a complex task into smaller
instances of the same task, recursive algorithms mirror mathematical definitions and
lead to concise code. However, they come at the cost of extra memory usage due to
repeated function calls and deep call stacks, which may cause inefficiency or stack
overflows if not carefully managed.

Khobzaoui Abdelkader 50 2025–2026


UDL — FSE/Informatique Algorithm analysis

• Backtracking extends recursion with the ability to explore large solution spaces
systematically. By pruning unpromising paths and undoing choices when necessary,
backtracking transforms what would otherwise be an infeasible brute-force search
into a practical algorithmic technique. It is particularly powerful in combinatorial
problems such as puzzles, constraint satisfaction, and optimization.

• Both techniques share a common drawback: in the worst case, their time complexity
can still grow exponentially with the problem size. Yet with thoughtful design, effec-
tive pruning, and heuristics (such as memoization, bounding, or ordering strategies),
they become not only usable but also powerful tools that underpin many real-world
applications, from file system traversal to game AI and scheduling.

In summary, recursion gives us a way to think clearly about problems defined in terms
of themselves, while backtracking equips us with a disciplined method to search and
explore possibilities. Together, they form a cornerstone of algorithmic thinking and lay the
foundation for more advanced strategies like dynamic programming and branch-and-bound.

9 Summary
This chapter introduces and formalizes the notion of an algorithm as a finite, ordered
sequence of precise instructions designed to solve a problem. An algorithm must satisfy
essential properties—input, output, definiteness, finiteness, and effectiveness—which dis-
tinguish it from vague or informal strategies. Algorithms are not limited to programming;
they represent a way of reasoning systematically about problem-solving in both computing
and everyday life. To make algorithms concrete and accessible, the chapter surveys four

complementary forms of representation: natural language (intuitive but potentially am-


biguous), pseudocode (clear and language-independent), flowcharts (graphical visualization
of control flow), and program code (precise and executable, but tied to a specific language).
Each representation plays a unique role in bridging human understanding and machine
execution. A worked example, including the generation of prime numbers refined through
the Sieve of Eratosthenes, illustrates how design evolves from informal description to
structured solution. Most importantly, algorithm design is shown to be a systematic

process rather than a random activity. It begins with problem analysis, proceeds through
decomposition and abstraction, and culminates in iterative refinement for clarity, correct-
ness, and efficiency. This layered workflow—informal description, pseudocode, flowchart,
and program code—ensures solutions are both understandable and implementable. In

essence, algorithms embody the logic and structure that underlie every program. Good
programming is not about memorizing syntax but about thinking algorithmically: starting

Khobzaoui Abdelkader 51 2025–2026


UDL — FSE/Informatique Algorithm analysis

from a clear idea, refining it into precise steps, representing it in multiple forms, and finally
translating it into efficient code. This mindset enables the creation of solutions that are
correct, effective, and scalable.

10 Exercises
1. Write an algorithm in natural language to compute the average of three numbers.

2. Draw a flowchart that takes an integer as input and prints whether it is even or odd.

3. Give one example of an algorithm that always terminates and one that may result
in an infinite loop. Explain why.

4. Write pseudocode for finding the maximum of two numbers. Count the number of
elementary steps and determine the time complexity.

5. Design an algorithm to check if a year is a leap year. Represent it in:

(a) Natural language


(b) Pseudocode
(c) Flowchart

6. Write an algorithm to read n integers from the user and print their sum and average.

7. Identify the header, environment, and body in the following pseudocode:

Algorithm ComputeSquare
Input: n
Output: n^2
Begin
result <- n * n
return result
End

8. Write an algorithm that takes three numbers and prints the largest. Implement it in
pseudocode and explain the control structures used.

9. Design an algorithm to print all integers from 1 to n using:

(a) A while loop


(b) A for loop

Khobzaoui Abdelkader 52 2025–2026


UDL — FSE/Informatique Algorithm analysis

10. Write a recursive algorithm to compute the factorial of a number n. Show the
recursion tree for n = 5.

11. Translate the following natural language algorithm into pseudocode: “Start with 0.
For each integer from 1 to n, add it to the total. Print the total.”

12. Discuss the difference between an algorithm and a program. Give an example where
the same algorithm can be implemented in two different programming languages.

Khobzaoui Abdelkader 53 2025–2026

You might also like