Java Developer Initial Program

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 110

Week 7

Introduction to bluej

A simple integrated development environment


http://www.bluej.org/
Exercise: How to use arrays
Exercise: How to use arrays Draft of the class public interface

We want to write the TextContainer class that stores text broken into lines
On the text container, we want to carry out the operations below:

Insert a new line as last line of the text (appends a line to the exixting text)

Remove the first line

Replace one word with another word in the text

Check if the text container is empty
public class TextContainer
{ public TextContainer() {…}
public void add(String aLine){…}
public String removeFirst() {…}
public boolean isEmpty() {…}
public int replace(String find, String replace) {…}
4 } Foundations of Computer Science – A. Luchetta 2021-2022
Class TextContainer

public TextContainer(): constructor that initializes an empty text container


public void add(String aLine): adds the specified string at the end of the text
(appends a new string)
public String removeFirst(): removes and returns the first line of the text container
public boolean isEmpty(): returns true if the text container is empty, false otherwise
public int replace(String find, String replace): searches the text for the specified
word find and replaces it with the specified word replace. It returns the number of
replacements made. It treats a word as a string delimited by one or more
whitespaces

5 Foundations of Computer Science – A. Luchetta 2021-2022


Design of TextContainer class
1. Define the public interface
We have to define it with great care because it provides the tools with which we can process the
information contained in the objects created with the class
The name of the methods generally derives from verbs that describe the actions performed by
the methods themselves
In the case of our tutorial, the public interface is already defined
2. Define the private section
We have to carefully choose the instance variables where to store the information (data)
associated with the objects of the class
Different realizations of the same class can have different private parts, but they
must have the same public interface!
6 Foundations of Computer Science – A. Luchetta 2021-2022
Design of TextContainer class – private section

The class has to store text broken down into lines


A simple solution is to create the private part by means of an 1st line
array of string references in which each element is a reference 2nd line
to a line of text ...
The element of the array with index zero will point to the first last line
line, the element with index 1 to the second line, and so on
How many elements will our array have? We don't know in
advance how many lines we need to store, so we'll make a
partially filled array with dynamic resizing

7 Foundations of Computer Science – A. Luchetta 2021-2022


TextContainer – private section
import java.util.Scanner;
import java.util.NoSuchElementException;
/**
Text container that stores a text broken into lines
@author Adriano Luchetta Javadoc comment
@version 16-Sept-2021
*/
public class TextContainer
{ partially filled array
//private section
private static final int INITIAL_CAPACITY = 1;
private String[] v; // reference to array of Strings
private int vSize; // counter of lines already inserted in
// the array

8 Foundations of Computer Science – A. Luchetta 2021-2022


TextContainer class – public section
The constructor task is to initialize the instance variables
Generally, if the number of items to be stored in the array is not known, we starts with
one item only
A constructor that creates an empty text container is what we want
/** Javadoc comment
Costructor: initializes an empty text container
*/
public TextContainer()
{
v = new String[INITIAL_CAPACITY]; // array allocation
vSize = 0; // no. of elements already inserted into
// the container
}
9 Foundations of Computer Science – A. Luchetta 2021-2022
/**
inserts the specified line, if not null, into the
container as last line
@param aLine the specified line
*/
public void add(String aLine)
{ // check of precondition
if (aLine == null)
return;

// Possible array dynamic resizing


if (vSize >= v.length) // check if array is full
{ String[] newV = new String[2 * v.length];//double length
for (int i = 0; i < v.length; i++)
newV[i] = v[i];
v = newV; // old array is abandoned
}
// insertion of the new line and counter increment
v[vSize] = aLine;
vSize++;
10
}
Foundations of Computer Science – A. Luchetta 2021-2022
TextContainer class – public section

Boolean method to check if the container is empty


/**
Checks if the container is empty
@return true if the container is empty,
false otherwise
*/
public boolean isEmpty()
{
return vSize <= 0;// boolean expression

}
Note: check vSize <= 0 is more robust than the
check vSize == 0
11 Foundations of Computer Science – A. Luchetta 2021-2022
/**
Removes the first line of the container
@return if the container is not empty, removes the first
line of the container, throws the exception
otherwise
@throws NoSuchElementException //javadoc for exceptions
*/
public String removeFirst()
{
// check of precondition
if (isEmpty())
throw new NoSuchElementException();
// temporary storage of line to remove (index 0)
String tmpString = v[0];
// removal of first line – SEQUENCE IS IMPORTANT!
for (int i = 0; i < vSize - 1; i++)
v[i] = v[i+1];
v[vSize – 1] = null; // cleaning
vSize--; // counter decrement
return tmpString;
12
}
Foundations of Computer Science – A. Luchetta 2021-2022
Replace method

The method must traverse all stored lines to search for the word find (the
TextContainer class stores lines, not single words)
The method must be able to split the individual lines into words and identify the
presence of the specified word find
If we find the specified word find, we must replace it with the specified word
replace, increase the replacement counter and insert the modified line into the array
in the correct sequence

13 Foundations of Computer Science – A. Luchetta 2021-2022


/**
Searches the first specified word and, when found,
replaces it with the second specified word
@param find first specified word
@param replace second specified word
@return number of replacement executed
@throws IllegalArgumentException if the first or
second specified words are null
*/
public int replace(String find, String replace)
{
// check of preconditions
if (find == null || replace == null)
throw new IllegalArgumentException();

int count = 0; // counter of replacements


//continues
14 Foundations of Computer Science – A. Luchetta 2021-2022
for (int i = 0; i < vSize; i++) // note vSize
{ Scanner st = new Scanner(v[i]); // scanner to extract token
Loop to traverse the lines of the

String tmpLine = "";


boolean found = false;
while (st.hasNext()) // token extraction
{
Loop to split the

String token = st.next(); // current token


current line

if (token.equals(find))
{
container

token = replace;
count++; // replacement counter increment
found = true;
}
tmpLine = tmpLine + token + " ";
}
if (found) // substitution of line
v[i] = tmpLine;

st.close();
}
return count;
} // end of method
15} // end of class
Foundations of Computer Science – A. Luchetta 2021-2022
Test class - TextContainerTester

Let's program a TextContainerTester test class that



reads a text from standard input

Prints the original text on standard output

If it receives two words as arguments from the command line, uses them for search /
replacement in the text

prints the text, possibly modified, to the standard output

Prints the number of word replacements executed
We will use the test class to read a text from a file using standard input redirection, modify
the text and write it to a new file using standard output redirection.

16 Foundations of Computer Science – A. Luchetta 2021-2022


import java.util.Scanner;
public class TextContainerTester
{ public static void main(String[] args)
{ //opens the input data flow from standard input
Scanner in = new Scanner(System.in);

//creates an empty text container


TextContainer text = new TextContainer();

//reads and prints the text entered from standard input


System.out.println("\n*** ORIGINAL TEXT ***");
while(in.hasNextLine())
{ String line = in.nextLine();
text.add(line);
System.out.println(line);
}
in.close(); // closes the input data flow
System.out.println("***END OF ORIGINAL TEXT***");
17 Foundations of Computer Science – A. Luchetta 2021-2022
// continues
//possible search and replacement of words
int n = 0;
if (args.length > 1) // we need at least 2 arguments
{ System.out.println("\nreplacement of " + args[0]
+ " with " + args[1]);
System.out.println("\n*** MODIFIED TEXT: ***");

n = text.replace(args[0], args[1]);
}

//print-out on standard output


System.out.println();
while(!text.isEmpty())
System.out.println(text.removeFirst());

System.out.println("**END OF MODIFIED TEXT**\n");


System.out.println("replacements excuted: " + n);
}
}
18
//End Foundations of Computer Science – A. Luchetta
of TextContainerTester 2021-2022
Testing a class

As an alternative to writing the TextContainerTester test class, it is possible to make


the TextContainer class executable by programming the main() method inside

19 Foundations of Computer Science – A. Luchetta 2021-2022


import java.util.Scanner;
public class TextContainer // now the class can be executed
{ // ... enter the previously programmed class code here
public static void main(String[] args)
{ //opening of flow from standard input
Scanner in = new Scanner(System.in);
//creates an empty text container
TextContainer text = new TextContainer();
//reads and prints the text entered from standard input
System.out.println("\n*** ORIGINAL TEXT ***");
while(in.hasNextLine())
{ String line = in.nextLine();
text.add(line);
System.out.println(line);
}
in.close();
System.out.println("\n***END OF ORIGINAL TEXT***");
//possible search and replacement
int n = 0;
if (args.length > 1) // we need at least 2 arguments
{ System.out.println("\n*** MODIFIED TEXT: ***");
System.out.println("sostituzione di "+args[0]+" con "+ args[1]);
n = text.replace(args[0], args[1]);
}
//printout on standard output
System.out.println();
while(!text.isEmpty()) System.out.println(text.removeFirst());
System.out.println("\n**END OF MODIFIED TEXT**\n");
System.out.println("no. " + n + " of replacements executed");
20 } Foundations of Computer Science – A. Luchetta 2021-2022
} //End of TextContainer
Common errors
Common errors – initialization of local variables

Instance variables, if not explicitly initialized, are automatically initialized to a


default value

zero for numeric variables

false for boolean variables

null for reference variables
Method arguments are initialized by copying the value of the actual arguments used
in the invocation of the method
Local variables are not automatically initialized, and the compiler performs a
semantic check preventing them from being used before receiving a value

22 Foundations of Computer Science – A. Luchetta 2021-2022


Common errors – initialization of local variables
Here is an example of a semantic error reported by the compiler for failure to
initialize a local variable
int n; // without initialization
if (in.hasNextInt())
n = in.nextInt();
System.out.println("n = " + n);
variable n might have not been initialized
int n = 0; // OK
if (in.hasNextInt())
n = in.nextInt();
System.out.println("n = " + n);

Sometimes it is necessary use a “dummy” initialization


23 Foundations of Computer Science – A. Luchetta 2021-2022
Recursion
Recursion

25 Foundations of Computer Science – A. Luchetta 2021-2022


Recursion

26 Foundations of Computer Science – A. Luchetta 2021-2022


The factorial function
The factorial function, widely used in combinatorics, is defined recursively as below
f(n)=n!= where n is an integer
The definition is recursive, that is the function itself is used in its definition
At first sight this definition seems impossible, but if we analyze it we find the
sequence below
0! = 1
1! = 1 (1-1)! = 1 0! = 1 1 = 1 3! = 3 (3-1)! = 3 · 2! = 3 · 2 · 1 = 6
2! = 2 (2-1)! = 2 1! = 2 1 = 2 4! = 4 (4-1)! = 4 · 3! = 4 3 2 1 = 24 ......
Hence, for every n positive integer, the factorial of n is the product of the first n
positive integers
27 Foundations of Computer Science – A. Luchetta 2021-2022
Loop-based implementation of the factorial function
Let's write a static public static int factorial(int n)
method to calculate the {
factorial if (n < 0)
throw new IllegalArgumentException();
It takes advantage of else if (n == 0)
the numerical analysis return 1;
we have carried out else
{
It implements the int p = 1;
product by using a for- for (int i = 1; i <= n; i++)
loop p = p * i;
return p;
}
}
28 Foundations of Computer Science – A. Luchetta 2021-2022
Recursive implementation of the factorial function
So far, nothing new , but
we had to do a public static int factorial(int n)
mathematical analysis {
of the recursive if (n < 0)
definition to write the throw new IllegalArgumentException();
else if (n == 0)
algorithm return 1;
Realizing the definition else
return n * factorial(n - 1);
directly, it would have
}
been more natural to
write

29 Foundations of Computer Science – A. Luchetta 2021-2022


Recursive implementation of the factorial function
You might think: "It is not possible to invoke a method while executing the method
itself!"
Instead, as it is easy to verify by writing a program that uses the recursive
implementation of the method factorial(), this is legal in Java, just as it is legal in almost
all programming languages
Invoking a method while executing the same method is a programming paradigm called
recursion
and a method that makes use of it is called recursive method
Recursion is a very powerful tool for building some algorithms, but it is also the source
of many, difficult to diagnose errors

30 Foundations of Computer Science – A. Luchetta 2021-2022


Execution of recursive methods

To understand how to use recursion correctly, let's first see how it works
When a recursive method invokes itself, the Java virtual machine performs the same
actions that are performed when any method is invoked
suspends the execution of the invoking method
executes the invoked method until it is terminated
the execution of the invoking method resumes from the point at which it was
suspended

31 Foundations of Computer Science – A. Luchetta 2021-2022


0!=1
Execution of recursive methods
1!=1*0!
Let's consider the sequence used to calculate 3!
factorial (3) invokes factorial (2) 2!= 2*1!
factorial (2) invokes factorial (1)
factorial (1) invoke factorial (0)
factorial (0) returns 1 3!=3*2!
factorial (1) returns 1
factorial (2) returns 2
factorial (3) returns 6
A list of "pending" methods is then created,
which lengthens and then shortens until it dies out
32 Foundations of Computer Science – A. Luchetta 2021-2022
Rules to design a correct recursive algorithm
To design correct recursive algorithms we have to respect two basic rules
1st rule
the recursive method must provide the solution to the problem in at least one
particular case, without using a recursive call
this case is called the base case of recursion
in our example, the base case is
if (n == 0)
return 1;

sometimes there are multiple base cases, it doesn't need to be unique

33 Foundations of Computer Science – A. Luchetta 2021-2022


Recursive (called also inductive) step
2nd rule
the recursive method must make the recursive call after simplifying the problem
in our example, for the computation of the factorial of n we invoke the function
recursively to get the factorial of n-1, that is to solve a simpler problem
if (n > 0)
return n * factorial(n - 1);
the concept of "simpler problem" varies from time to time: in general, the recursive
calls must strive for a base case
base case
In our math definition: f(n)=n!= Recursive step

34 Foundations of Computer Science – A. Luchetta 2021-2022


Recursion is an algorithm
The above rules are fundamental in order to prove that the recursive solution of a
problem is an algorithm
in particular, that it reaches its conclusion in a finite number of steps
We might think that recursive calls can happen one after the other, indefinitely
But if

at each recursive invocation the problem becomes more and more simple

and approaches the base case in which the result does not require recursion
then certainly the solution is computed in a finite number of steps (possibly very
high), however complex the problem may be

35 Foundations of Computer Science – A. Luchetta 2021-2022


Never-ending recursion
Not all recursive methods implement algorithms

if the base case is missing, the recursive method continues to invoke itself
indefinitely

if the problem is not simplified with each recursive invocation, the recursive
method continues to invoke itself indefinitely
Since the list of "pending" methods stretches indefinitely, the run-time
environment runs out of memory available to keep track of this list and the program
ends with an error

36 Foundations of Computer Science – A. Luchetta 2021-2022


Never-ending recursion
Try running a program that has an endless recursion
public class EndlessRecursion
{ public static void main(String[] args)
{ main(args);
}
}

The program will terminate with the StackOverflowError


the runtime stack (or Java Stack) is the data structure within the Java interpreter that
manages pending invocations
overflow means that it has run out of memory space

37 Foundations of Computer Science – A. Luchetta 2021-2022


Linear recursion

If a recursive method starts only one new recursive factorial(4) 24


invocation at each execution, the recursion is called
linear 6
factorial(3)
public static int fact(int n)
{ if (n < 0)
throw new IllegalArgumentException(); factorial(2) 2
if (n == 0)
return 1; factorial(1) 1
return n * fact(n - 1);
} factorial(0) 0

38 Foundations of Computer Science – A. Luchetta 2021-2022


Double ricursion
When a method makes two recursive calls during its execution, recursion is said to be
binary or double
/**sums the elements of an array from index from to index to */
public static int binSum(int [] a, int from, int to)
{ if (from > to)
return 0;
if (from == to)
return a[from];
int mid = (from + to) / 2;
return binSum(a,from,mid) + binSum(a,mid+1,to);
}

This method is clearly inefficient and serves only to illustrate the binary recursion
39 Foundations of Computer Science – A. Luchetta 2021-2022
Double ricursion

Below you can see the diagram of recursive calls with values of the from and to
arguments equal to 0 and 7, respectively

0, 7

0, 3 4, 7

0, 1 2, 3 4, 5 6, 7

0, 0 1, 1 2, 2 3, 3 4, 4 5, 5 6, 6 7, 7

40 Foundations of Computer Science – A. Luchetta 2021-2022


Tail recursion
There are several types of recursion public void tail(...)
{ ...
Tail recursion tail(...);
the recursive method performs a single recursive }
invocation and this invocation is the last action of
the method
/**reverses the content of a sub-array from index from to index to*/
public static void revArray(int[] a, int from, int to)
{
if (from >= to) // base case
return;

int tmp = a[from];


a[from] = a[to];
a[to] = tmp;
revArray(a, from + 1, to - 1); // recursive step
41
} Foundations of Computer Science – A. Luchetta 2021-2022
Elimination of tail recursion
public static void revArrayRec(int[] a,int from,int to)
{
if (from >= to) // caso base
Tail recursion can return;
always be easily int tmp = a[from];
Recursive method
eliminated by a[from] = a[to];
a[to] = tmp;
transforming the revArray(a, from + 1, to - 1);
recursive method }
into a method public static void revArrayIt(int[] a,int from,int to)
that uses a loop {
while (from < to)
{
int tmp = a[from];
a[from++] = a[to];
a[to--] = tmp; Iteration method
}Foundations of Computer Science – A. Luchetta 2021-2022
42 }
Tail recursion vs non-tail recursion
So what's the purpose of tail recursion?
It is not necessary, but in some cases it makes the code more readable
It is useful when the solution to the problem is explicitly recursive
In any case, tail recursion is less efficient than the equivalent loop, because the system
has to handle suspended invocations
If the recursion is not in the tail, it is not easy to eliminate it (i.e. write equivalent non-
recursive code), but it can be shown that this is always possible
this must be the case, because the processor executes instructions in sequence and
cannot keep instructions waiting ... so the interpreter must take care of eliminating the
recursion (using the runtime stack)

43 Foundations of Computer Science – A. Luchetta 2021-2022


Mutiple recursion – Example: Fibonacci numbers
We speak of multiple recursion when a method invokes itself several times during its
execution
multiple recursion is even more difficult to eliminate, but it is always possible
Example: the calculation of Fibonacci numbers

44 Foundations of Computer Science – A. Luchetta 2021-2022


Fibonacci numbers - fibBad
The fibBad() method performs a multiple (binary) recursion
We will soon see why fibBad
**/ returns the n-th fibonacci number */
public static long fibBad(int n)
{
if (n < 0)
throw new IllegalArgumentException();
if (n < 2) // base cases
return n;
return fibBad(n-2) + fibBad(n-1);
}
45 Foundations of Computer Science – A. Luchetta 2021-2022
Multiple recursion

Multiple recursion should be used very carefully, as it can lead to very inefficient
programs
By calculating the Fibonacci numbers of ascending order ...
… We notice that the processing time glines VERY rapidly… almost 3 million
invocations to calculate fibBad (31) !
we will quantify this problem later

46 Foundations of Computer Science – A. Luchetta 2021-2022


Mutiple recursion
fibBad(5)
Multiple invocations with
the same arguments make
the code inefficient! fibBad(4) fibBad(3)
fibBad(1) is calculated 5
times
fibBad(2) is computed 3 fibBad(3) fibBad(2) fibBad(2) fibBad(1)
times
fibBad (3) is computed
twice
fib(2) fib(1) fib(1) fib(0) fib(1) fib(0)

fib(1) fib(0)
47 Foundations of Computer Science – A. Luchetta 2021-2022
Fibonacci numbers - fibGood
**/ returns an array that contains
fib(n) and fib(n-1) */
public static long[] fibGood(int n)
{
The fibGood if (n < 0) // preconditions
method shown on throw new IllegalArgumentException();
the right performs if (n < 2) // base cases
linear recursion: {
long[] ret = {n, 0};
The algorithm is return ret;
now efficient }
// recursive step
long[] tmp = fibGood(n - 1); //F(n-1), F(n-2)
long[] ret = {tmp[0] + tmp[1], tmp[0]};
return ret;
48 } Foundations of Computer Science – A. Luchetta 2021-2022
fibGood(5) 5, 3

fibGood(4) 3, 2

fibGood(3) 2, 1

Fibonacci numbers fibGood(2) 1, 1

Fibonacci n 0 1 2 3 4 5 6 7 ...
fibGood(1) 1, 0
numbers Fn 0 1 1 2 3 5 8 13 ...
49 Foundations of Computer Science – A. Luchetta 2021-2022
Example: recursively reverse a string
We want to reverse the sequence of a string with a recursive algorithm
Example: "One" is reversed in "enO"
Recursive algorithm:
preconditions: if the string s to be inverted is not a string, but the null reference, null is
returned
base case: if the number of characters in the string s is less than 2, the string is returned
because there is no need to reverse
recursive step: extract the first character of the string s[0]. Recursively invert the substring
with indexes from 1 to s.length() - 1. Return the concatenation of the revesed substring with
the first character.
Example: s = "One"; returns "en" + "O" think recursively!
50 Foundations of Computer Science – A. Luchetta 2021-2022
Example: recursively reverse a string
/**
Reverses the specified string
@param s the specified string
@return the reversed string
*/
private static String reverseString(String s)
{
// preconditions
if (s == null)
return null;
// base case
if (s.length() < 2)
return s;
// recursive step
String r = reverseString(s.substring(1));
return r + s.charAt(0);
51 } Foundations of Computer Science – A. Luchetta 2021-2022
Recursive calculation of the greatest common divisor

Calculate the greatest common divisor (GCD) between two positive integers m and n
with the Euclidean algorithm below:
preconditions: if m or n are not positive throw the IllegalArgumentException
base case: if n divides m, then the GCD is equal to n
recursive step: the GCD between m and n is equal to the GCD between n and m% n

think recursively!

52 Foundations of Computer Science – A. Luchetta 2021-2022


Euclidean algorithm for the greatest common divider
/** computes the greatest common divider
@param m first specified integer
@patam n second specified integer
@return greatest common divider of the specified
integers */
private static int gcd(int m, int n)
{
// preconditions
if (m <= 0 || n <= 0)
throw new IllegalArgumentException();
// base case
if (m % n == 0)
return n;
// recusive step
return gcd(n, m % n);
}
53 Foundations of Computer Science – A. Luchetta 2021-2022
Recognizing recursively a palindrome
A string is called a palindrome if it is equal to the string of characters arranged in reverse sequence
Examples of palindromes:
"radar", "anilina", "123454321"
Recursive algorithm to check if a string is a palindrome:
base case: if the string has less than two characters (so none or one character) then the string is
palindrome
another basic case: if the first and last characters of the string are different, then the string is not a
palindrome
recursive step: if the substring without the first and last character is palindrome, then the string is
palindrome
think recursively!
54 Foundations of Computer Science – A. Luchetta 2021-2022
/**
recognizes a palindrome
@param s the specified string
@return true if the specified string is a palindrome,
false otherwise
*/
private static boolean isPalindrome(String s)
{
// precondition
if (s == null)
throw new IllegalArgumentException();
// base case – length less than 2 chars
if (s.length() < 2)
return true;
// other base case – different first and last chars
if (s.charAt(0) != s.charAt(s.length() - 1))
return false;
// recursive step – check on substring
55 return isPalindrome(s.substring(1,
Foundations of Computer Science – A. Luchetta s.length() - 1));2021-2022
}
Example of recursion – The Hanoi tower
Towers of Hanoi
The Towers of Hanoi is a mathematical game consisting of three rods and a number of
disks of various diameters, which can slide onto any rod. The puzzle begins with the
disks stacked on one rod in order of decreasing size, the smallest at the top, thus
approximating a conical shape. The objective of the game is to move the entire stack
to the last rod, obeying the following rules:
1. Only one disk may be moved at a time
2. Each move consists of taking the upper disk from one of the stacks and placing it on top of
another stack or on an empty rod
3. No disk may be placed on top of a disk that is smaller than it

Three-disk tower Eight-disk tower

57 Foundations of Computer Science – A. Luchetta 2021-2022


Situazione iniziale

final situation move 4

move 1 move 5

move 2 move 6

move 3 move 7 final situation

58 tower 1 tower 2 tower 3


Foundations of Computer Science – A. Luchetta tower 1 tower 2 tower 2021-2022
3
Towers of Hanoi
A recursive solution algorithm is known for the game of the Towers of Hanoi
The general problem consists in moving n discs from one tower to another, using a
third tower as a temporary storage
To move n disks from one tower to another, we supposed to be able to move n-1
disks from one tower to another, as is always done in recursion
To describe the algorithm, we identify the towers with the integers 1, 2 and 3

59 Foundations of Computer Science – A. Luchetta 2021-2022


Recursive algorithm to solve the Towers of Hanoi game

To move n discs from tower 1 to tower 3


1. the n-1 disks on top of tower 1 are moved to tower 2, using tower 3 as a temporary
storage (a recursive call is used, at the end of which tower 3 remains empty)
2. the disk left in tower 1 is taken to tower 3
3. the n-1 disks on top of tower 2 are moved to tower 3, using tower 1 as a temporary
storage (a recursive call is used, at the end of which tower 1 remains empty)
The base case is when n is 1 and the algorithm uses only step 2, which has no
recursive calls

60 Foundations of Computer Science – A. Luchetta 2021-2022


Recursive algorithm to solve the Towers of Hanoi game

It can be shown that the number of moves required to solve the puzzle with the
proposed algorithm is

2 1 n

with n equal to the number of disks

61 Foundations of Computer Science – A. Luchetta 2021-2022


public class HanoiSolver
{
public static void main(String[] v)
{
System.out.println(“Solution of Towers of Hanoi for ” + v[0]
+ “ disks”);
solveHanoi(1,3,2,Integer.parseInt(v[0])); // arg from command line
}

private static void solveHanoi(int from, int to, int temp, int size)
{
if (size > 0)
{
solveHanoi(from, temp, to, size - 1);
System.out.println("move from T" + from + " to T" + to);
solveHanoi(temp, to, from, size - 1);
}
}
}
62 Foundations of Computer Science – A. Luchetta 2021-2022
main() calls solveHanoi(1,3,2,2)

Calls to solve Towers of Hanoi game with 2 disks


start solveHanoi(1, 3, 2, 2)
start solveHanoi(1, 2, 3, 1)
Towers of Hanoi with n = 2
start solveHanoi(1, 3, 2, 0)
stop solveHanoi(1, 3, 2, 0)
move from T1 to T2
start solveHanoi(3, 2, 1, 0)
stop solveHanoi(3, 2, 1, 0)
stop solveHanoi(1, 2, 3, 1)
move from T1 to T3
start solveHanoi(2, 3, 1, 1)
start solveHanoi(2, 1, 3, 0)
stop solveHanoi(2, 1, 3, 0)
move from T2 to T3
start solveHanoi(1, 3, 2, 0)
stop solveHanoi(1, 3, 2, 0)
stop solveHanoi(2, 3, 1, 1)
63 stop
Foundations of ComputersolveHanoi(1,
Science – A. Luchetta 3, 2, 2) 2021-2022
When will the world end?

A legend tells that some Buddhist monks in a temple in the Far East have been
involved since long in solving the game, physically moving their 64 disks from one
tower to another, aware that when they have finished the world will end
It takes 264 - 1 moves, which is about 18 billion trillion moves… that's about 18x1018
Assuming that the monks make a move every minute, they make about 525,000
moves a year: so the world will end in more than 30 trillion years!
A 1GHz processor making one move at each clock interval (one billion moves per
second) takes 18 billion seconds, which is about 584 years!

64 Foundations of Computer Science – A. Luchetta 2021-2022


Sorting algorithms
Motivations for sorting

A very frequent problem in data processing is the sorting of the data, according to a
predefined criterion
names in alphabetical order, numbers in ascending order ...
We will study different algorithms for sorting, also introducing an analytical tool to
evaluate their performance
On ordered data, it is then possible to search very efficiently, and we will study
suitable algorithms
The algorithms we will study are recursive and efficient, but not all recursive
algorithms are efficient!

66 Foundations of Computer Science – A. Luchetta 2021-2022


Selection sort
Selection sort

For simplicity, we will first focus on sorting a set of integers stored in an array
we will then see how we can simply extend to the case in which we sort objects
rather than numbers
Let us sort an array a ascending order, as in the example below

11 9 17 5 12 5 9 11 12 17
a[0] a[1] a[2] a[3] a[4] a[0] a[1] a[2] a[3] a[4]
unsorted sorted

68 Foundations of Computer Science – A. Luchetta 2021-2022


Selection sort

11 9 17 5 12 5 9 17 11 12
a[0] a[1] a[2] a[3] a[4]
First, we need to find the smallest element in the array, using the search algorithm
we already know,
in our example the smallest number 5 at index 3
Being a[3] the smallest element, its correct position in the ordered array is at a[0]
the number 11 is stored in a[0], so we have to move it
we don't know what the correct position of 11 is
we temporarily move it to a[3], the position where the minimum is
therefore, we exchange a[3] with a[0]
69 Foundations of Computer Science – A. Luchetta 2021-2022
Selection sort
5 9 17 11 12 The colored side of the array
is already sorted
a[0] a[1] a[2] a[3] a[4]
The left side of the array is already sorted and will no longer be considered, we have
to sort the right side
We sort the right side with the same algorithm
we look for the minimum, which is 9 in position a[1]
since it is already in the first position a[1] of the side to be ordered, there is no need
to move it
5 9 17 11 12
a[0] a[1] a[2] a[3] a[4]
70 Foundations of Computer Science – A. Luchetta 2021-2022
Selection sort

5 9 17 11 12
a[0] a[1] a[2] a[3] a[4]
We go on and sort the side of the array that contains the elements a[2], a[3] and a[4]
the minimum is number 11 in position a[3]
we exchange a [3] with a [2]

5 9 11 17 12 5 9 11 17 12
a[0] a[1] a[2] a[3] a[4] a[0] a[1] a[2] a[3] a[4]
71 Foundations of Computer Science – A. Luchetta 2021-2022
Selection sort

5 9 11 17 12
a[0] a[1] a[2] a[3] a[4]
Now the array to be sorted contains a[3] and a[4]
the minimum is number 12 in position a[4]
we exchange a[4] with a[3]

5 9 11 12 17 5 9 11 12 17
a[0] a[1] a[2] a[3] a[4] a[0] a[1] a[2] a[3] a[4]
At this point the side to be sorted contains only one element, so it is obviously
sorted
72 Foundations of Computer Science – A. Luchetta 2021-2022
public class ArrayAlgorithms // class containing algorithms for arrays
{
public static void selectionSort(int[] a)
{ for (int i = 0; i < a.length - 1; i++)
{ int minPos = findMinPos(a, i);
if (minPos != i) swap(a, minPos, i);
}
}
private static void swap(int[] a, int i, int j)
{ int temp = a[i];
a[i] = a[j];
a[j] = temp; Selection sort
} Selection sort
private static int findMinPos(int[] a, int from)
{ int pos = from;
int min = a[from];
for (int i = from + 1; i < a.length; i++)
{ int temp = a[i]; // complex method
if (temp < min) // to simplify the
{ pos = i; // performance evaluation
min = temp; // see ahead
}
}
return pos;
}
} 73 Foundations of Computer Science – A. Luchetta 2021-2022
Selection sort

The selection sort algorithm is correct and can sort any array of integers
so why are we going to study other sorting algorithms?
what is the need to study different algorithms to solve the same problem?
We will see that there are sorting algorithms that sort faster with the same array size

74 Foundations of Computer Science – A. Luchetta 2021-2022


Measurement of performance

To evaluate the efficiency of an algorithm, we measure the time it takes to run on


ever-larger data sets
Time should not be measured with a stopwatch, because part of the real running
time does not depend only on the algorithm, but also on
loading the JVM
loading of program classes
reading data from standard input
display of results

75 Foundations of Computer Science – A. Luchetta 2021-2022


Measurement of performance

The execution time of an algorithm must be measured within the program


It is very convenient to use the static method
System.currentTimeMillis()
It returns, on each invocation, a number of long type that is
the number of milliseconds that have elapsed since a baseline event (midnight
January 1, 1970, called theEpoch)
What matters is the difference between two values
We will call System.currentTimeMillis() before and after the execution of the
algorithm

76 Foundations of Computer Science – A. Luchetta 2021-2022


Measurement of performance

Let's sort arrays containing random


integers with different length n of
the array
We will note that the execution time

Time – relative units


trend is not linear
if n becomes double, the time
becomes about four times!

77 Foundations of Computer Science – A. Luchetta


n (thousends) 2021-2022
Measurement of performance

The performance of the selection sort algorithm therefore has a quadratic trend as a
function of the size of the array
The next questions we ask ourselves are
Is it possible to evaluate the performance of an algorithm from a theoretical point of
view?
without writing a program and without running it many times, to detect its
execution times and observe ithe time trend?
is there a more efficient algorithm for sorting?

78 Foundations of Computer Science – A. Luchetta 2021-2022


Theoretical analisys of performance

The execution time of an algorithm depends on the number and type of machine
instructions that the processor executes
To do a theoretical analysis
without writing a code for the algorithm, compile it and translate it into machine
language
we have to make drastic simplifications
What we will do is to count the number of read or write accesses to array elements
neglecting the other instructions

79 Foundations of Computer Science – A. Luchetta 2021-2022


Theoretical analisys of performance

When we sort an array of n elements with the selection sort algorithm


n accesses are made to find the minimum
4 accesses are made to exchange two elements (we read and write both)
we neglect the possible case in which the exchange is not needed
in total (n + 4) accesses
At this point we have to sort the remaining part, that is an array of (n-1) elements
therefore ((n-1) + 4) accesses will be needed
and so on up to the step with n = 2, inclusive

80 Foundations of Computer Science – A. Luchetta 2021-2022


Theoretical analisys of performance

The total read access count is then


T(n) = (n + 4) + ((n-1) +4) + ... + (3 + 4) + (2 + 4)
= n + (n-1) +… + 3 + 2 + (n-1) * 4
= n * (n + 1) / 2 - 1 + (n-1) * 4
= (1/2) * n2 + (9/2) * n - 5
A second degree polynomial is then obtained in n, which justifies the parabolic trend
of the times detected experimentally
the Gauss sum formula on the right holds

81 Foundations of Computer Science – A. Luchetta 2021-2022


Theoretical analisys of performance

T(n) = 1/2*n2 + 9/2*n - 5

Theoretical performance analysis led to a quadratic expression


Let's make a further simplification, bearing in mind that we are interested in
performance for high values of n (asymptotic trend)
To understand with bare numbers, if n is 1000
1/2 * n2 is worth 500,000
9/2 * n - 5 is worth 4495, about 1% of the total
therefore we say that the asymptotic trend of the algorithm as a function of n is
1/2 * n2

82 Foundations of Computer Science – A. Luchetta 2021-2022


Theoretical analisys of performance

Let's make a further simplification


we are only interested in evaluating what happens at the execution time T(n) if n, for
example, doubles
in this case the execution time quadruples
T (n) = 1/2 * n2
T (2n) = 1/2 * (2n) 2 = 1/2 * 4 * n2 = 4 * T (n)
So, we observe that T (2n) = 4 * T (n) also for T (n) = n2
regardless of the factor 1/2

83 Foundations of Computer Science – A. Luchetta 2021-2022


Theoretical analisys of performance

We can say that


to sort an array with the selection sort algorithm, a number of accesses is made
which is of the order of n2
To briefly express this concept, we use the "Big O"notation and we say that the
number of accesses is O(n2)
After obtaining a formula that expresses the time dependence of the algorithm, we
move on to the BIG O notation considering only the term that glines more rapidly as
n increases, ignoring the constant coefficients

84 Foundations of Computer Science – A. Luchetta 2021-2022


Theoretical analisys of performance - big O notation
Let be the computation time of an algorithm expressed
as T(n), where n is the size of the problem (e.g. the
number of elements of an array in the case of sorting),
and let F(n) be a function of n
BIG O: we will say that T(n) is O (F(n)) or T(n) ∈ O(F(n)) if
there exist two positive constants c > 0 and n0 > 0 such
that T (n) ≤ c F (n) for n > n0
- in informal terms T (n) glines less than F (n) for large n
Example of BIG O notation: f(x) ∈ O(g(x)) because
esiste c > 0 exists (for instance c = 1) and x0 (for
instance, x0 = 5) such that f(x) ≤ cg(x) for each x ≥ x0.

85 Foundations of Computer Science – A. Luchetta 2021-2022


Theoretical analisys of performance - big omega and big
theta notations

BIG OMEGA: we will say that T(n) is W(F(n) if there exist two positive constants c > 0
and n0 > 0 such that T(n) ≥ cF (n) for n > n0
- in informal terms T (n) glines more than F(n) for large n
BIG THETA: we will say that T(n) is (F (n)) if T(n) is O(F (n)) and also (F (n)
- in informal terms T (n) glines as F(n) for large n

Practically, we will use only the BIG O notation

86 Foundations of Computer Science – A. Luchetta 2021-2022


Theoretical analisys of performance
In practice we will informally use the notation big O to indicate that the glineth of T(n) is equal
to the glineth of F(n) for large n
Observe that the complexity of two algorithms can be in both cases O(F (n)) even if the one is,
for example, a thousand times faster than the other
Therefore the big O notation is useful for determining the principle applicability of an
algorithm, but in practice a more detailed analysis is required
The analysis will also have to take into account the actual practical dimension of the problem.
For example, an algorithm with a better trend for large n might require unacceptable time for
small values of n
In a detailed analysis of the complexity of it will also have to take into account the average case,
that is the average execution time of the algorithm, and the worst case, that is the time required
in the most "unfortunate" cases

87 Foundations of Computer Science – A. Luchetta 2021-2022


Theoretical analisys of performance

The number of memory accesses (time complexity) T(n) to sort an array of n integers
by means of the selection sort selection algorithm is equal to:

T (n) = 1/2 * n2 + 9/2 * n - 5

We will therefore say that the asymptotic trend of the time complexity of the sorting
by selection algorithm is equal to
O (n2)
We can express the same concept by saying that T(n) ∈ O(n2)

88 Foundations of Computer Science – A. Luchetta 2021-2022


Proof of Gauss sum formula

Geometrical proof

Arithmetic proof
89 Foundations of Computer Science – A. Luchetta 2021-2022
Linear search
Linear search
We have already seen an algorithm to identify the position of an element that has a
particular value within an array whose elements are not sorted
Since in the worst case all the elements have to be examined, it is called sequential
or linear search
public class ArrayAlgorithms // usual class
{
public static int linearSearch(int[] a, int v)
{
for (int i = 0; i < a.length; i++)
if (a[i] == v)
return i; // FOUND

return -1; // NON FOUND


}
...
91 Foundations of Computer Science – A. Luchetta 2021-2022
}
Time complexity of linear search

We evaluate the performance of the linear search algorithm


if the searched element is not present in the array, n accesses are required
if the searched element is present in the array, the number of accesses needed to find
it depends on its position
on average they will be T(n) = (n + 1) / 2
T(n) = number of accesses = 1 + 2 +… + n = n (n + 1) / 2
average = total number of accesses / no of cases
In any case, therefore, the performance of the algorithm is in the average and worst
case O(n)

92 Foundations of Computer Science – A. Luchetta 2021-2022


Theoretical analisys of performance

The number of memory accesses (time complexity) T(n) to sort an array of n integers
by means of the selection sort selection algorithm is equal to:

T (n) = 1/2 * n2 + 9/2 * n - 5

We will therefore say that the asymptotic trend of the time complexity of the sorting
by selection algorithm is equal to
O (n2)
We can express the same concept by saying that T(n) ∈ O(n2)

93 Foundations of Computer Science – A. Luchetta 2021-2022


Theoretical analisys of performance

The number of memory accesses (time complexity) T(n) to sort an array of n integers
by means of the selection sort selection algorithm is equal to:

T (n) = 1/2 * n2 + 9/2 * n - 5

We will therefore say that the asymptotic trend of the time complexity of the sorting
by selection algorithm is equal to
O (n2)
We can express the same concept by saying that T(n) ∈ O(n2)

94 Foundations of Computer Science – A. Luchetta 2021-2022


Binary search
Binary search

Finding the position of an element that has a particular value within an array can be
tackled more efficiently if the array is sorted
Let’s find the element with value 12
We compare 12 with the element located (approximately) in the center of the array,
a[2], which is 11
The element we are looking for is greater than 11
if it is present in the array, it will be to the right of 11

5 9 11 12 17 21
a[0] a[1] a[2] a[3] a[4] a[5]
96 Foundations of Computer Science – A. Luchetta 2021-2022
Binary search

Now we have to look for element 12 in the only sub-array that is to the right of a[2]
We use the same algorithm, that is comparing 12 with the element found in the center,
a[4], which is 17
The element we are looking for is less than 17
if it is present in the array, it will be to the left of 17

5 9 11 12 17 21
a[0] a[1] a[2] a[3] a[4] a[5]

97 Foundations of Computer Science – A. Luchetta 2021-2022


Binary search

At this point, we must search for element 12 in the sub-array consisting of only
element a[3]
We use the same algorithm, comparing 12 with the element found in the center, a[3],
which is 12
The element we are looking for is equal to 12
the element we are looking for is present in the array and is in position 3

5 9 11 12 17 21
a[0] a[1] a[2] a[3] a[4] a[5]

98 Foundations of Computer Science – A. Luchetta 2021-2022


Binary search

If the comparison between the element to be searched and the element a[3] had given
a negative result, we would have searched in the empty sub-array on the left or right,
concluding that the searched element is not present in the array
This algorithm is called binary search, because at each step the array is divided into
two parts, and it only works if the array is sorted

5 9 11 12 17 21
a[0] a[1] a[2] a[3] a[4] a[5]

99 Foundations of Computer Science – A. Luchetta 2021-2022


public class ArrayAlgorithms // usual class
{ public static int binarySearch(int[] a, int target)
{
return binSearch(a, 0, a.length - 1, target);
}

private static int binSearch(int[] a, int from, int to, int t)


{
if (from > to)
return -1; // NOT FOUND → base case 1

int mid = (from + to) / 2; // about in the middle


int middle = a[mid];

if (middle == t)
return mid; // FOUND → base case 2
else if (middle < t)
// SEARCH ON THE RIGHT
return binSearch(a, mid + 1, to, t);
else
// SEARCH ON THE LEFT
return binSearch(a, from, mid - 1, t);
}
... 2021-2022
100 } Foundations of Computer Science – A. Luchetta
Evaluation of performance of binary search

To evaluate the performance of the binary search algorithm in a sorted array


we observe that the algorithm is recursive
To search in an array of size n you need
make a comparison with the central element → one access
search in an array of size about n / 2
Therefore, called T[n] the time complexity

T(n) = T(n/2) + 1

101 Foundations of Computer Science – A. Luchetta 2021-2022


Evaluation of performance of binary search

The solution of this functional equation (equation between functions) to obtain T(n) as a function of n is not
simple and goes beyond the scope of this course
However, it can easily be verified, by substitution, that the solution is
T (n) = 1 + log2n O(log n)
The performance of the algorithm is therefore better than that of linear search
This applies in worst cases. i.e. when the element we look for is in the last position of the array or is not
present
We could also prove that the result holds also for the average case
We should compute the number of accesses in n + 1 cases (when the element looked for is in position 0, 1, … n-1, and not present)
and then divide for the number of cases
Note: the base of the algorithm can be omitted as the change from one base to another requires to multiply
by a constant log10 x = log10 2 * log2 x where log10 2 is a constant

102 Foundations of Computer Science – A. Luchetta 2021-2022


T(n) = T(n/2) + 1
Defining n = 2k,
that is k = log2n,
we find
T(n) = 1 + log2n

103 Foundations of Computer Science – A. Luchetta 2021-2022


// returns the index of the element t in the array
// or -1 if t is not found
public static int binarySearch(int[] a, int v)
{
int from = 0; // lower index
int to = a.length – 1; // upper index
// as long as the current portion of the array has
Loop-based implementation

// at least one element


while(from <= to)
{
int mid = (from + to)/2; //ABOUT IN THE MIDDLE
if (a[mid] == v)
of binary search

return mid; //FOUND


else if (a[mid] < v)
from = mid + 1; //SEARCH ON THE RIGHT
else
to = mid - 1; //SEARCH ON THE LEFT
Time
} complexity of the
algorithm O(log n) in
104 return
Foundations of Computer Science –1; // NOT FOUND
– A. Luchetta 2021-2022
average
} and worst cases
Linear search with sentinel
Linear search with sentinel

We have analyzed the time complexity of linear search and found that we have to
perform
1 access in the best case when we find the element we are looking for in the first
position
n accesses in the worst cases when we find the element in the last position or we do not
find the element
(n + 1) /2 accesses in the average case (the element we are looking for is present and is
in any of the possible positions with the same probability)
We have to do two comparisons on each loop iteration, one to check if we have found
the element, the other to check if we have reached the end of the data

106 Foundations of Computer Science – A. Luchetta 2021-2022


Linear search with sentinel

int[] v = new int[…];


v[0] = …;

int target = …;
for (int i = 0; i < vSize; i++)
if (target == v[i])
return i;

return -1;

107 Foundations of Computer Science – A. Luchetta 2021-2022


Linear search with sentinel

We can cut the number of comparisons in half by using a sentinel (also useful in other
circumstances)
Assuming that the array v[] is only partially filled and has at least one free position, we
insert the value to look for at the vSize index
At this point we are sure that the searched element appears in the array at least once;
we can therefore interrupt the search cycle when the data is found without checking if
the vector has run out
At the end of the cycle, if the found element occupies the vSize position, the search
has failed

108 Foundations of Computer Science – A. Luchetta 2021-2022


Linear search with sentinel
Even using a sentinel the
asymptotic trend of the time int[] v = new int[…];
v[0] = …;
complexity remains O(n) …
int target = …;
time v[vSize] = target;
int i = 0;
while (v[i] != target)
i++;

if (i != vSize)
two comparisons ... // found
sentinel else
size n ... // not found

109 Foundations of Computer Science – A. Luchetta 2021-2022


Can we do better?

If we have no other information, there is no way to improve things.


The sentinel linear search algorithm is O(n) in the average and worst case like pure
linear search
If we know that the array is sorted, we can use a recursive (or iterative) divide-and-
conquer strategy that uses the result of each comparison to reduce the size of the
problem we want to be solve

binary Serach

110 Foundations of Computer Science – A. Luchetta 2021-2022

You might also like