Adding 2nd assignment - recursion

This commit is contained in:
Christopher Sanden
2025-10-24 16:30:19 +02:00
parent 08a6b25df7
commit f94a8a4409
5 changed files with 266 additions and 15 deletions

View File

@@ -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
)

View File

@@ -0,0 +1,132 @@
#include <iostream>
#include <random>
#include <string>
#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<char>(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<int> dist(minChar, maxChar);
std::string s;
s.reserve(len);
for (int i = 0; i < len; i++)
s.push_back(static_cast<char>(dist(gen)));
return s;
}
int countOccurrences_index(const std::string& s, const char c, int index)
{
if (index >= static_cast<int>(s.size()))
return 0;
const int hit = (s[index] == c ? 1 : 0);
return hit + countOccurrences_index(s, c, index + 1);
}

View File

@@ -0,0 +1,33 @@
#ifndef FUNDAMENTAL_RECURSION_UTILS_H
#define FUNDAMENTAL_RECURSION_UTILS_H
#include <string>
#include <chrono>
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<typename F, typename... Args>
double time_ms(F&& f, Args&&... args)
{
auto t0 = std::chrono::high_resolution_clock::now();
(void)std::forward<F>(f)(std::forward<Args>(args)...);
auto t1 = std::chrono::high_resolution_clock::now();
return std::chrono::duration<double, std::milli>(t1 - t0).count();
}
#endif //FUNDAMENTAL_RECURSION_UTILS_H

View File

@@ -0,0 +1,89 @@
#include <chrono>
#include <iostream>
#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<int>(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;
}
}

View File

@@ -29,15 +29,6 @@
<component name="ChangeListManager">
<list default="true" id="e3758ede-1321-4b2e-94dd-da87753a03f6" name="Changes" comment="Finished">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/CMakeLists.txt" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/TMovie.cpp" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/TMovie.h" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/TMovieList.cpp" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/TMovieList.h" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/TMovieNode.cpp" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/TMovieNode.h" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/cmake-build-debug/CMakeFiles/clion-Debug-log.txt" beforeDir="false" afterPath="$PROJECT_DIR$/cmake-build-debug/CMakeFiles/clion-Debug-log.txt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/main.cpp" beforeDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -91,11 +82,6 @@
}
}</component>
<component name="RunManager" selected="C/C++ File.main.cpp">
<configuration default="true" type="CLionExternalRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true">
<method v="2">
<option name="CLION.EXTERNAL.BUILD" enabled="true" />
</method>
</configuration>
<configuration name="assignment1" type="CMakeRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="assignment1" TARGET_NAME="assignment1" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="assignment1" RUN_TARGET_NAME="assignment1">
<method v="2">
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
@@ -108,8 +94,8 @@
</method>
</configuration>
<list>
<item itemvalue="C/C++ File.main.cpp" />
<item itemvalue="CMake Application.assignment1" />
<item itemvalue="C/C++ File.main.cpp" />
</list>
</component>
<component name="TaskManager">
@@ -123,6 +109,7 @@
<workItem from="1760788441262" duration="11460000" />
<workItem from="1760967500921" duration="5495000" />
<workItem from="1761213959983" duration="4210000" />
<workItem from="1761302251197" duration="113000" />
</task>
<task id="LOCAL-00001" summary="Finished">
<option name="closed" value="true" />