diff --git a/Fundamental_Recursion/CMakeLists.txt b/Fundamental_Recursion/CMakeLists.txt new file mode 100644 index 0000000..7147ef8 --- /dev/null +++ b/Fundamental_Recursion/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 4.0) +project(Fundamental_Recursion) + +set(CMAKE_CXX_STANDARD 20) + +add_executable(Fundamental_Recursion + main.cpp + utils.cpp + Utils.h +) diff --git a/Fundamental_Recursion/Utils.cpp b/Fundamental_Recursion/Utils.cpp new file mode 100644 index 0000000..96c42cc --- /dev/null +++ b/Fundamental_Recursion/Utils.cpp @@ -0,0 +1,132 @@ +#include +#include +#include +#include "Utils.h" + +void printNaturalNumber(const int n) +{ + if (n == 0) + return; + // "Digs down" until n = 0, then prints all natural numbers on the way back up + printNaturalNumber(n-1); + std::cout << n << " "; +} + +int calculateFactorial(const int n) +{ + if (n <= 1) + return 1; + // "Digs down" until n <= 1, then multiplies by n for every step back up + return n * calculateFactorial(n-1); +} + +int power(const int base,const int exp) +{ + if (exp == 0 || base == 1 || base == 0) + return 1; + // "Digs down" until exp <= 0, then multiplies base with the base for every step back up + return base * power(base, exp - 1); // The simplest to write, not the most efficient - O(n) + + // int half = power(base / 2, exp); + // if (exp % 2 == 0) + // return half * half; + // else + // return base * half * half; // More efficient implementation, reduces the recursive call by half + // O(log n) + +} + +int fibonacci(const int n) +{ + if (n == 0) + return 0; + if (n == 1) + return 1; + return fibonacci(n - 1) + fibonacci(n - 2); // O(2^n), each call spawns two more recursive calls + // Could be optimised by using iteration instead of recursion + // For loop would be O(n) since it takes the previous number, adding + // current number, then stepping up until it has iterated n times + // Linear growth instead of exponential + // int current = 1; + // int previous = 0; + // for (int i = 2; i <= n; i++) + // { + // int next = previous + current; + // previous = current; + // current = next; + // } + // return current; +} + +int countOccurrences (const std::string& s, const char c) +{ + if (s.empty()) + return 0; + const int count = (s[0] == c ? 1 : 0); + return count + countOccurrences(s.substr(1), c); + // O(n^2) since we have to make a new substring for every index of the original string +} + +int findLargestElement(const int arr[], const int size) +{ + if (size <= 0) + return 0; + if (size == 1) + return arr[0]; + const int mid = size / 2; + const int leftMax = findLargestElement(arr, mid); + const int rightMax = findLargestElement(arr + mid, size - mid); + + return (leftMax > rightMax) ? leftMax : rightMax; + // O(n) time complexity, since we have to compare each element at least once +} + +// Up to, not including, end +void traverseAsciiTable(const char start, const char end) +{ + if (start >= end) + return; + std::cout << start << " "; // Reflects the building + traverseAsciiTable(static_cast(start + 1), end); + + std::cout << " "; // Reflects the unwinding +} + +// Helper funcs to populate and randomise input + +void fillRandomArray(int arr[], const int size, const int minVal, const int maxVal) +{ + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dist(minVal, maxVal); + + for (int i = 0; i < size; i++) + { + arr[i] = dist(gen); + } +} + +std::string makeRandomString(const int len, char minChar = 'a', char maxChar = 'z') +{ + if (len <= 0) + return{}; + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution dist(minChar, maxChar); + + std::string s; + s.reserve(len); + for (int i = 0; i < len; i++) + s.push_back(static_cast(dist(gen))); + return s; +} + +int countOccurrences_index(const std::string& s, const char c, int index) +{ + if (index >= static_cast(s.size())) + return 0; + const int hit = (s[index] == c ? 1 : 0); + return hit + countOccurrences_index(s, c, index + 1); +} + + diff --git a/Fundamental_Recursion/Utils.h b/Fundamental_Recursion/Utils.h new file mode 100644 index 0000000..a8ce3ae --- /dev/null +++ b/Fundamental_Recursion/Utils.h @@ -0,0 +1,33 @@ +#ifndef FUNDAMENTAL_RECURSION_UTILS_H +#define FUNDAMENTAL_RECURSION_UTILS_H + +#include +#include + + +void printNaturalNumber(int n); +int calculateFactorial(int n); +int power(int base, int exp); +int fibonacci(int n); +int countOccurrences(const std::string& s, char c); +int findLargestElement(const int arr[], int size); +void traverseAsciiTable(char start, char end); + + +// Helpers partially generated by ChatGPT +void fillRandomArray(int arr[], int size, int minVal, int maxVal); +std::string makeRandomString(int len, char minChar, char maxChar); +int countOccurrences_index(const std::string& s, char c, int index); + +template +double time_ms(F&& f, Args&&... args) +{ + auto t0 = std::chrono::high_resolution_clock::now(); + (void)std::forward(f)(std::forward(args)...); + auto t1 = std::chrono::high_resolution_clock::now(); + return std::chrono::duration(t1 - t0).count(); +} + + + +#endif //FUNDAMENTAL_RECURSION_UTILS_H \ No newline at end of file diff --git a/Fundamental_Recursion/main.cpp b/Fundamental_Recursion/main.cpp new file mode 100644 index 0000000..30a3e9c --- /dev/null +++ b/Fundamental_Recursion/main.cpp @@ -0,0 +1,89 @@ +#include +#include +#include "Utils.h" + +static void line(const char* title) +{ + std::cout << "\n======= " << title << "=======\n"; +} + +int main() +{ + line("Neutral numbers"); + printNaturalNumber(10); + std::cout << "\n"; + + + line("Factorial"); + const int fact_expect[] = { + 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800 + }; + bool ok_fact = true; + for (int n = 0; n <= 10; ++n) { + int r = calculateFactorial(n); + std::cout << "n=" << n << " -> " << r + << (r == fact_expect[n] ? " OK" : " MISMATCH") << "\n"; + ok_fact &= (r == fact_expect[n]); + } + + + line ("Power"); + struct Case { int base, exp, expect; }; + const Case pow_cases[] = { + {2,0,1}, {2,1,2}, {2,5,32}, {3,4,81}, {5,3,125}, {10,2,100} + }; + bool ok_pow = true; + for (auto c : pow_cases) { + int r = power(c.base, c.exp); + std::cout << c.base << "^" << c.exp << " = " << r + << (r == c.expect ? " OK" : " MISMATCH") << "\n"; + ok_pow &= (r == c.expect); + + line("Fibonacci"); + const int fib_expect[] = {0,1,1,2,3,5,8,13,21,34,55,89,144}; + bool ok_fib = true; + for (int n = 0; n <= 12; ++n) { + int r = fibonacci(n); + std::cout << "F(" << n << ") = " << r + << (r == fib_expect[n] ? " OK" : " MISMATCH") << "\n"; + ok_fib &= (r == fib_expect[n]); + } + + + line("Count occurrences"); + std::string s = makeRandomString(2000, 'a', 'f'); + char target = 'c'; + int rec = countOccurrences(s, target); //<--- recursive + int ref = static_cast(std::count(s.begin(), s.end(), target)); + std::cout << "recursive=" << rec << " std::count=" << ref + << (rec == ref ? " OK" : " MISMATCH") << "\n"; + double t_rec_ms = time_ms([&]{ (void)countOccurrences(s, target); }); + std::cout << "perf (recursive substr): ~" << t_rec_ms << " ms\n"; + + + line ("Find largest element"); + constexpr int N = 100000; + int* arr = new int[N]; + fillRandomArray(arr, N, -100000, 100000); + double t_my_ms = time_ms([&]{ (void)findLargestElement(arr, N); }); + int myMax = findLargestElement(arr, N); + int stdMax = *std::max_element(arr, arr + N); + std::cout << "myMax=" << myMax << " stdMax=" << stdMax + << (myMax == stdMax ? " OK" : " MISMATCH") << "\n"; + std::cout << "perf (recursive): ~" << t_my_ms << " ms\n"; + delete[] arr; + + + line("Traverse Ascii table"); + traverseAsciiTable('A', 'F'); + std::cout << "\n"; + + + line("Summary"); + std::cout << "Factorial: " << (ok_fact ? "OK" : "FAIL") << "\n"; + std::cout << "Power: " << (ok_pow ? "OK" : "FAIL") << "\n"; + std::cout << "Fibonacci: " << (ok_fib ? "OK" : "FAIL") << "\n"; + + return 0; + } +} \ No newline at end of file diff --git a/assignment1/.idea/workspace.xml b/assignment1/.idea/workspace.xml index 97f0bd7..fc78ab8 100644 --- a/assignment1/.idea/workspace.xml +++ b/assignment1/.idea/workspace.xml @@ -29,15 +29,6 @@ - - - - - - - - - - - - - - + @@ -123,6 +109,7 @@ +