2. 7 6 4 5
8 9 11 10
15 14 12 13
0 1 2 3
7 6 5 4
8 9 10 11
15 14 13 12
Task4/task4.cpp
#include <cstdio>
#include <iostream>
using namespace std;
#define MAXROW 4
#define MAXCOL 4
void bidirection_bbsort(int a[], int n, bool ascending)
{
int i,j, temp;
//This is the original bubble sort
// Make SMALL change such that it handles descending
3. sorting too
for (i = n-1; i >= 1; i--) {
for (j = 1; j <= i; j++) {
if (a[j-1] > a[j]) {
temp = a[j-1] ;
a[j-1] = a[j] ;
a[j] = temp ;
}
}
}
}
void column_sort(int m[][MAXCOL], int col)
{
//Use the bidirection_bbsort() once it is implemented
// No need to invent / write a new sorting logic
}
void print_matrix(int m[][MAXCOL], int nRow, int nCol)
{
int i, j;
for (i = 0; i < nRow; i++){
for (j = 0; j < nCol; j++){
printf("%2d ", m[i][j]); //use printf for easier formatting
//cout << setw(2) << m[i][j] << " "; //equivalent c++
output formatting
}
cout << endl;
}
cout << endl;
}
void phase_A(int mat[][MAXCOL], int nRow, int nCol)
{
4. int i;
bool order = true;
for (i = 0; i < nRow; i++, order = !order){
bidirection_bbsort(mat[i], nCol, order);
}
}
void phase_B(int mat[][MAXCOL], int nRow, int nCol)
{
int i;
for (i = 0; i < nCol; i++){
column_sort(mat, i);
}
}
void magic_sort(int mat[][MAXCOL], int nRow, int nCol)
{
int i;
for (i = 1; i < nRow; i *= 2){
phase_A(mat, nRow, nCol);
print_matrix(mat, nRow, nCol);
phase_B(mat, nRow, nCol);
print_matrix(mat, nRow, nCol);
}
phase_A(mat, nRow, nCol);
print_matrix(mat, nRow, nCol);
}
int main()
{
5. int arr[] = { 5, 12, 6, 7, 9, 6, 1};
int i;
int mat[MAXROW][MAXCOL] = {
{5, 2, 13, 9},
{10, 1, 15, 0},
{7, 3, 11, 14},
{12, 8, 4, 6}
};
magic_sort(mat, 4, 4);
return 0;
}
Task3/task3.cpp
#include <cstdio>
#include <iostream>
using namespace std;
struct Point {
int X, Y;
};
void sort(Point a[], int n)
{
//You can adapt any of the insertion / selection / bubble sort
}
int main()
6. {
int i;
Point ptArray[5] = { {11, 34}, {5, 73}, {11, 19}, {13, 5},
{11,
68}};
sort(ptArray, 5);
//The output should match the order given in question
for (i = 0; i < 5; i++){
printf("(%d, %d)n", ptArray[i].X, ptArray[i].Y);
}
return 0;
}
Task2/sample_length_3.txt
***
%%%
%##
%$$
##%
$$%
Task2/sample_length_5.txt
***%%
9. int i;
floor.clear(); //clear the content of string
floor.assign(size, EMPTY); //fill string with "size" copies of
EMPTY '-'
}
void put_tile(string& floor, int loc, Tile* tilePtr)
{
int i;
for (i = 0; i < tilePtr->size; i++){
floor[loc+i] = tilePtr->pattern;
}
}
void remove_tile(string& floor, int loc, Tile* tilePtr)
{
int i;
for (i = 0; i < tilePtr->size; i++){
floor[loc+i] = EMPTY;
}
}
void tile_floor(string& floor, int loc, Tile tileArray[], int
numTiles)
{
//Only nee dto write this function
}
int main()
{
Tile tileSet[4] = { {3, '*'}, {1, '%'}, {2, '#'}, {2, '$'}};
string floor;
10. //This example test a floor length of 3
// You should test additional values to ensure correctness
init_floor(floor, 3);
tile_floor(floor, 0, tileSet, 4);
return 0;
}
Task1/task1.cpp
#include <cstdio>
#include <iostream>
using namespace std;
//used to count the number of times fibonacci is called
int __call_count = 0;
int fibonacci_m(int N, int known_result[])
{
//Do not remove the following line
__call_count++;
//Put your code below
if ( N <= 2){
return 1;
}
return fibonacci_m(N-1, known_result) + fibonacci_m(N-2,
known_result);
}
11. int main()
{
int N;
int known[50] = {0};
cout << "N = ";
cin >> N;
printf("Fibonacci(%d) is %dn", N, fibonacci_m(N, known));
printf("Fibonacci() called %d timesn", __call_count);
return 0;
}
1 | P a g e
National University of Singapore
School of Continuing and Lifelong Education
TIC1002: Introduction to Computing and Programming II
Semester II, 2017/2018
Problem Set 2
Recursion and Sorting
Release date: 3 Feb 2018
Due: 19 Feb 2018, 23:59
Task 1: Memoisation…. Did you just misspelled memorization?
12. [6 marks]
The Fibonacci() example from lecture illustrated that a)
Recursion is indeed simpler /
elegant to code, but also b) Precious time may be wasted due
to the recalculation of known
results. Let us learn a powerful technique to get the best of both
worlds in this task.
Memoization (not a typo!) is a problem solving technique where
we keep previously calculated
results for future use. Combining memoization with recursion gi
ve you a very powerful problem
solving tool!
Let us use factorial() to illustrate the technique:
int factorial_m( int N, int known_result[])
{
if (known_result[N] == 0) {
//Cannot rely on known calculation, have to work
if (N == 0){
//Fill in the result for future use
known_result[0] = 1;
} else {
//Same, record result for future use
known_result[N] = N * factorial_m( N‐1, known_result);
}
}
//At this point, known_result[N] should have N!
// Either it is previously known (i.e. failed the if condition) OR
// It has just been calculated in the recursive portion above.
return known_result[N];
13. }
The factorial function now takes in an array known_result[] whi
ch keeps track of previously
calculated result, known_result[K] stores the answer for K!.
2 | P a g e
At the beginning, the known_result[] should be initialized to all
zeroes to indicate no known
answers (zero is chosen because factorial(N) cannot be zero). Y
ou can see that as answers are
calculated, the respective elements in the known_result
array will be filled. If the same
known_result[] array is used again, we can cut down some of th
e unnecessary calculations.
For example:
int known[20] = {0}; //support up to factorial(19).
factorial_m( 3, known ); // Call ONE
factorial_m( 3, known ); // Call TWO
factorial_m( 5, known ); // Call Three
For Call ONE, each of the recursive calls
(f(3) f(2) f(1) f(0)) will find that
the respective element in known[3], known[2], known[1] and kn
own[0] are all "0", i.e. no
known result. The normal recursion will kick in and fill
up the answers in known[0],
14. known[1], known[2] and known[3]. In this example, there is no
saving at all.
However, at Call TWO, fac(3) will find that known[3] already h
as an answer and proceed to
return it without any recursive call!
For Call THREE, fac(5) will only calculates two values at know
n[5] and known[4]. As soon as
the recursion hits fac(3), the known result will be used instead o
f recursion.
Observations:
1.
Memoisation is not only applicable to recursive functions. You
can use it for non‐
recursive processing too, especially if the calculation takes a lo
ng time.
2. Factorial() can only benefit from memorization if we call
factorial() multiple
times for different inputs. i.e. if we stop
the example after call one, there is no
benefit gained as fac(3), fac(2), fac(1) and fac(0) are calculated
and used
once only.
Point (2) above should give you a hint that memorization will h
elp functions like Fibonacci()
greatly as multiple redundant calculations are performed even in
the same Fibonacci call.
15. Your task
is to utilize memorization technique for the recursive Fibonacci(
) function. Note
that in the template code, we gave the original Fibonacci functio
n with a small addition. There
is a global variable "call_count" which will be incremented ever
y time the Fibonacci function
is called. This is just a simple way
for us to check whether your memoization is implemented
3 | P a g e
properly. For actual usage outside of this task, you can just rem
ove this global variable safely. In
the original recursive Fibonacci() function:
Fibonacci 20 30 40
Result 6765 832040 102334155
Call Count 13529 1664079 204668309
With memoization, the call count reduces drastically:
Fibonacci 20 30 40
Result 6765 832040 102334155
Call Count 20 30 40
Note that the results reported are from independent
single Fibonacci() function call, not
consecutive ones. Once the memorization is properly
16. implemented, you should find that the
recursive function's execution speed is comparable to the iterati
ve version!
Task 2: Pretty Tiles [6 marks]
Nemo has just moved in to his new HDB
flat! He hired you as the interior designer with the
main focus to tile his floor with beautiful patterns. Being a typi
cal choosy person, Nemo asked
you to show him all possible designs so that he can decide on th
e best pattern.
To simplify the problem,
let us consider only one strip of the floor area, represented by a
1D
character string. Each type of tile pattern is
represented by a character and has a size (how
many units of floor area it can cover). For example, we have 4 d
ifferent tiles below:
Index 0 1 2 3
Pattern '*' '%' '#' '$'
Size 3 1 2 2
For example, the tile pattern 0 is a size 3 tile with '*', i.e. "***"
.
If we use the above tile set to fill a floor area of 3 units, the pos
sible patterns are:
***
%%%
%##
17. %$$
##%
$$%
One tile 0
Three tile 1
One tile 1 and One tile 2
etc…..
Note that a valid pattern must fully filled the floor (i.e. has 3 ch
aracters in this example).
4 | P a g e
Write a recursive function to generate all the possible design pa
tterns. The recursive function
has the following header:
void tile_floor(char floor[], int loc,
struct Tile tileArray[], int numTiles)
floor[] is the character array representing the floor. Should be u
sed as a string.
loc is the current location (index) to place a tile on.
tileArrray[] is an array of Tile structure (see below).
numTiles is the number of tiles in the tileArray
This function print all possible design patterns to fully fill the fl
oor from loc onwards.
We use a Tile structure to capture the information about a tile:
struct Tile {
int size;
18. char pattern;
};
In addition, we have written the following helper functions for y
ou:
void put_tile(char floor[], int loc, struct Tile* tilePtr);
Fill in a tile on the floor at location loc. The tile information is
passed in via the structure
pointer tilePtr.
void remove_tile(char floor[], int loc, struct Tile* tilePtr);
Remove a tile on the floor at location loc. The tile information i
s passed in via the structure
pointer tilePtr.
void remove_tile(char floor[], int loc, struct Tile* tilePtr);
Remove a tile on the floor at location loc. The tile information i
s passed in via the structure
pointer tilePtr.
void init_floor(char floor[], int size);
Empty the floor of any tile. In this program, we represent empty
floor locations as '‐'.
Hints:
1. Think of the 3 key ingredients of recursive solution!
2. The solution needs only ~10 lines of code.
3. In this case, there can be a loop in the recursive function.
4. Don’t forget to make use of the provided helper functions.
19. 5 | P a g e
Two sample output for floor length of 3 and 5, named as "sampl
e_length_3.txt" and
"sample_length_5.txt" are provided for your reference.
Ensure your output is exactly the same as the sample output (inc
luding the sequence of
designs).
Task 3: There is no point in sorting! [6 marks]
Sorting algorithm can be easily expanded to any other comparab
le items. As long as there is a
way to say "item A is before item B", then a collection of such i
tems can be sorted.
In this task, we will try to sort an array of 2D points, i.e. reusin
g the Point structure covered in
lecture 2:
struct Point {
int X, Y;
};
The ordering we want to achieve is "sort by X‐ coordinate, tie b
reaks by Y‐coordinate". Below is
an example of sorted array of points:
Point Index 0 1 2 3 4
20. X 5 11 11 11 13
Y 73 19 34 68 5
Note the interesting example of Point 1, 2 and 3: Since they hav
e the same X‐ coordinates,
these points are ordered by the Y coordinate ("Tie breaks by Y‐
coordinate").
You can implement any of the 3 sorting algorithms taught in thi
s course: Insertion, Selection or
Bubble Sort. However, the restriction is that you can only call s
ort() ONCE to achieve the final
result.
Note: The auto‐grader can only confirm the sorting order (i.e. is
it sorted properly), but not the
further requirements (sorting algorithm, cannot use sort more th
an once etc). Hence, the
further requirements will be manually verified after submission
deadline.
6 | P a g e
Task 4: Tada! Magic Sort. [6 marks]
This task looks at a very interesting sorting that works on a 2D
array. Don’t panic! The core logic
is already written in the magic_sort() function. You only need t
21. o provide two helper
functions that are quite straightforward to write:
void bidirection_bbsort( int a[], int N, bool ascending);
Adapt the given bubble sort function so that it can sort either in
ascending order (from
small item to large) or descending order (from large item to sma
ll). Note that the bubble
sort function can already sort in ascending order.
The parameter ascending indicates the sorting order:
true: ascending order OR
false: descending order
void column_sort( int matrix[][MAXCOL], int col);
Sort the col column in the matrix[][] in ascending order.
Restriction: Find a way to reuse the bidirection_bbsort(), i.e. yo
u do not need to
write the sorting logic at all.
Once the functions are implemented, the magic_sort() function s
hould have the following
output:
2 5 9 13
15 10 1 0
3 7 11 14
12 8 6 4
0 1 2 3
7 6 4 5
8 9 11 10
22. 15 14 12 13
2 5 1 0
3 7 6 4
12 8 9 13
15 10 11 14
0 1 2 3
7 6 5 4
8 9 10 11
15 14 13 12
0 1 2 5
7 6 4 3
8 9 12 13
15 14 11 10
See anything special for the final output? (hint: the matrix is no
w sorted in a specific way…
For your exploration: Try placing other values in the original m
atrix in main() and verify that
the "magic sort" always work. This sorting algorithm is known a
s "shear sort".