├── CPP9.pdf ├── README.md └── project_cpp9 ├── ex00 ├── .DS_Store ├── BitcoinExchange.cpp ├── BitcoinExchange.hpp ├── Makefile └── main.cpp ├── ex01 ├── Makefile ├── RPN.cpp ├── RPN.hpp └── main.cpp └── ex02 ├── .DS_Store ├── Makefile ├── PmergeMe.cpp ├── PmergeMe.hpp └── main.cpp /CPP9.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/42nenuser/Ford-Johnson_algorithm/f71580b5a214123488edb4b8f29a247e43c35917/CPP9.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # C++ Module 09 - PmergeMe 2 | 3 | ## Introduction 4 | i'll focus on the **PmergeMe** exercise, which implements the **Ford-Johnson algorithm**, also known as **merge-insertion sort**. This algorithm aims to minimize the number of comparisons while sorting a collection. Although not always the most practical sorting method, it remains an important reference in computer science, providing insights into efficient comparison-based sorting. 5 | 6 | ## Understanding the Ford-Johnson Algorithm 7 | The Ford-Johnson merge-insertion sort follows a three-step approach: 8 | 9 | 1. **Pairing and Initial Sorting**: 10 | - The input list is split into pairs, and each pair is sorted. 11 | - If the number of elements is odd, the last element remains unpaired. 12 | 13 | 2. **Sorting Pairs by Maximum Value**: 14 | - The highest value from each pair is recursively sorted to form the **main chain**. 15 | - The remaining elements (smaller values in pairs) become **pend elements**. 16 | - The main chain is labeled as `{a1, a2, a3, ..., an/2}`, while the pend elements are `{b1, b2, b3, ..., bn/2}`. 17 | - Each pend element satisfies `bk ≤ ak`. 18 | 19 | 3. **Insertion of Pend Elements**: 20 | - The first pend element (`b1`) is inserted before `a1`, forming `{b1, a1, a2, ..., an/2}`. 21 | - The next pend elements are inserted following a **power of 2 minus 1** pattern. 22 | - The insertion order follows **Jacobsthal numbers**: `{b1, b3, b2, b5, b4, b11, b10, b9, b8, b7, b6, ...}`. 23 | 24 | This structured insertion pattern reduces the number of comparisons required compared to traditional sorting algorithms like quicksort or mergesort. 25 | 26 | ## Features of PmergeMe Implementation 27 | - **Efficient Sorting**: 28 | - Utilizes **two STL containers** for sorting. 29 | - Supports sorting large sequences efficiently (3000+ elements). 30 | - **Time Complexity Optimization**: 31 | - Reduces the number of comparisons compared to standard merge sort. 32 | - **Performance Analysis**: 33 | - Displays execution time for sorting using two different STL containers. 34 | 35 | ## Compilation & Execution 36 | Each exercise includes a **Makefile** with at least the following rules: 37 | ```sh 38 | make # Compile the program 39 | make clean # Remove object files 40 | make fclean # Remove all compiled files 41 | make re # Recompile everything 42 | ``` 43 | Run PmergeMe as follows: 44 | ```sh 45 | ./PmergeMe 3 5 9 7 4 46 | ``` 47 | Example Output: 48 | ``` 49 | Before: 3 5 9 7 4 50 | After: 3 4 5 7 9 51 | Time to process a range of 5 elements with std::vector: 0.00031 us 52 | Time to process a range of 5 elements with std::deque: 0.00014 us 53 | ``` 54 | 55 | ## Learning Outcomes 56 | By completing this exercise, you will: 57 | - Understand **Ford-Johnson sorting** and its theoretical significance. 58 | - Implement efficient sorting using **STL containers**. 59 | - Optimize sorting algorithms for **reduced comparisons**. 60 | 61 | ## Ressources & References : 62 | Decidedlyso. “Merge-Insertion-Sort”. Github. https://github.com/decidedlyso/merge-insertion-sort 63 | 64 | Ford, Lester R., and Selmer M. Johnson. “A Tournament Problem.” The American Mathematical Monthly, vol. 66, no. 5, 1959, pp. 387–389. JSTOR, www.jstor.org/stable/2308750. Accessed 13 June 2020. 65 | 66 | Knuth, Donald E. The Art of Computer Programming: Volume 3: Sorting and Searching. Addison-Wesley Professional; 2 edition (May 4, 1998). 67 | 68 | Mahmoud, Hosam M. Sorting: A Distribution Theory. John Wiley & Sons. (October 14, 2011). 69 | Manacher, Glenn K. 1979. “The Ford-Johnson Sorting Algorithm Is Not Optimal”. J. ACM 26, 3 (july 1979), 441–456. DOI:https://doi.org/10.1145/322139.322145 70 | 71 | Morwenn. “Ford-Johnson Merge-Insertion Sort”. Code Review Stack Exchange. 10 Jan. 2016, https://codereview.stackexchange.com/questions/116367/ford-johnson-merge-insertion-sort 72 | -------------------------------------------------------------------------------- /project_cpp9/ex00/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/42nenuser/Ford-Johnson_algorithm/f71580b5a214123488edb4b8f29a247e43c35917/project_cpp9/ex00/.DS_Store -------------------------------------------------------------------------------- /project_cpp9/ex00/BitcoinExchange.cpp: -------------------------------------------------------------------------------- 1 | #include "BitcoinExchange.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | BitcoinExchange::BitcoinExchange() { 9 | loadDatabase("data.csv"); 10 | } 11 | 12 | BitcoinExchange::BitcoinExchange(const BitcoinExchange& other) : database(other.database) {} 13 | 14 | BitcoinExchange& BitcoinExchange::operator=(const BitcoinExchange& other) { 15 | if (this != &other) { 16 | database = other.database; 17 | } 18 | return *this; 19 | } 20 | 21 | BitcoinExchange::~BitcoinExchange() {} 22 | 23 | void BitcoinExchange::loadDatabase(const std::string& filename) { 24 | std::ifstream file(filename.c_str()); 25 | if (!file.is_open()) { 26 | throw std::runtime_error("Error: could not open database file."); 27 | } 28 | 29 | std::string line; 30 | std::getline(file, line); // Skip header 31 | 32 | while (std::getline(file, line)) { 33 | std::istringstream iss(line); 34 | std::string date; 35 | std::string value; 36 | 37 | if (std::getline(iss, date, ',') && std::getline(iss, value)) { 38 | database[date] = std::atof(value.c_str()); 39 | } 40 | } 41 | } 42 | 43 | bool BitcoinExchange::isValidDate(const std::string& date) const { 44 | if (date.length() != 10) return false; 45 | if (date[4] != '-' || date[7] != '-') return false; 46 | 47 | int year = std::atoi(date.substr(0, 4).c_str()); 48 | int month = std::atoi(date.substr(5, 2).c_str()); 49 | int day = std::atoi(date.substr(8, 2).c_str()); 50 | 51 | if (year < 1 || month < 1 || month > 12 || day < 1 || day > 31) return false; 52 | 53 | return true; 54 | } 55 | 56 | bool BitcoinExchange::isValidValue(const double value) const { 57 | return value >= 0 && value <= 1000; 58 | } 59 | 60 | std::string BitcoinExchange::findClosestDate(const std::string& date) const { 61 | std::map::const_iterator it = database.lower_bound(date); 62 | if (it == database.begin()) { 63 | return it->first; 64 | } else if (it == database.end() || it->first != date) { 65 | --it; 66 | } 67 | return it->first; 68 | } 69 | 70 | void BitcoinExchange::processInputFile(const std::string& filename) const { 71 | std::ifstream file(filename.c_str()); 72 | if (!file.is_open()) { 73 | throw std::runtime_error("Error: could not open input file."); 74 | } 75 | 76 | std::string line; 77 | std::getline(file, line); // Skip header 78 | 79 | while (std::getline(file, line)) { 80 | std::istringstream iss(line); 81 | std::string date; 82 | std::string valueStr; 83 | 84 | if (std::getline(iss, date, '|') && std::getline(iss, valueStr)) { 85 | date.erase(0, date.find_first_not_of(" \t")); 86 | date.erase(date.find_last_not_of(" \t") + 1); 87 | valueStr.erase(0, valueStr.find_first_not_of(" \t")); 88 | valueStr.erase(valueStr.find_last_not_of(" \t") + 1); 89 | 90 | if (!isValidDate(date)) { 91 | std::cout << "Error: bad input => " << date << std::endl; 92 | continue; 93 | } 94 | 95 | double value; 96 | std::istringstream(valueStr) >> value; 97 | 98 | if (!isValidValue(value)) { 99 | if (value < 0) { 100 | std::cout << "Error: not a positive number." << std::endl; 101 | } else { 102 | std::cout << "Error: too large a number." << std::endl; 103 | } 104 | continue; 105 | } 106 | 107 | std::string closestDate = findClosestDate(date); 108 | double exchangeRate = getExchangeRate(closestDate); 109 | double result = value * exchangeRate; 110 | 111 | std::cout << date << " => " << value << " = " << result << std::endl; 112 | } else { 113 | std::cout << "Error: bad input => " << line << std::endl; 114 | } 115 | } 116 | } 117 | 118 | double BitcoinExchange::getExchangeRate(const std::string& date) const { 119 | std::map::const_iterator it = database.find(date); 120 | if (it != database.end()) { 121 | return it->second; 122 | } 123 | return 0.0; 124 | } -------------------------------------------------------------------------------- /project_cpp9/ex00/BitcoinExchange.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BITCOINEXCHANGE_HPP 2 | #define BITCOINEXCHANGE_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class BitcoinExchange { 10 | private: 11 | std::map database; 12 | 13 | void loadDatabase(const std::string& filename); 14 | bool isValidDate(const std::string& date) const; 15 | bool isValidValue(const double value) const; 16 | std::string findClosestDate(const std::string& date) const; 17 | 18 | public: 19 | BitcoinExchange(); 20 | BitcoinExchange(const BitcoinExchange& other); 21 | BitcoinExchange& operator=(const BitcoinExchange& other); 22 | ~BitcoinExchange(); 23 | 24 | void processInputFile(const std::string& filename) const; 25 | double getExchangeRate(const std::string& date) const; 26 | }; 27 | 28 | #endif // BITCOINEXCHANGE_HPP -------------------------------------------------------------------------------- /project_cpp9/ex00/Makefile: -------------------------------------------------------------------------------- 1 | NAME = btc 2 | CC = g++ 3 | CFLAGS = -Wall -Wextra -Werror -std=c++98 4 | 5 | SRCS = main.cpp BitcoinExchange.cpp 6 | OBJS = $(SRCS:.cpp=.o) 7 | 8 | all: $(NAME) 9 | 10 | $(NAME): $(OBJS) 11 | $(CC) $(CFLAGS) $(OBJS) -o $(NAME) 12 | 13 | %.o: %.cpp 14 | $(CC) $(CFLAGS) -c $< -o $@ 15 | 16 | clean: 17 | rm -f $(OBJS) 18 | 19 | fclean: clean 20 | rm -f $(NAME) 21 | 22 | re: fclean all 23 | 24 | .PHONY: all clean fclean re -------------------------------------------------------------------------------- /project_cpp9/ex00/main.cpp: -------------------------------------------------------------------------------- 1 | #include "BitcoinExchange.hpp" 2 | #include 3 | #include 4 | 5 | int main(int argc, char* argv[]) { 6 | if (argc != 2) { 7 | std::cerr << "Error: could not open file." << std::endl; 8 | return 1; 9 | } 10 | 11 | try { 12 | BitcoinExchange exchange; 13 | exchange.processInputFile(argv[1]); 14 | } catch (const std::exception& e) { 15 | std::cerr << "this is Error: " << e.what() << std::endl; 16 | return 1; 17 | } 18 | 19 | return 0; 20 | } -------------------------------------------------------------------------------- /project_cpp9/ex01/Makefile: -------------------------------------------------------------------------------- 1 | NAME = RPN 2 | CXX = c++ 3 | CXXFLAGS = -Wall -Wextra -Werror -std=c++98 4 | 5 | SRCS = main.cpp RPN.cpp 6 | OBJS = $(SRCS:.cpp=.o) 7 | 8 | all: $(NAME) 9 | 10 | $(NAME): $(OBJS) 11 | $(CXX) $(CXXFLAGS) $(OBJS) -o $(NAME) 12 | 13 | %.o: %.cpp 14 | $(CXX) $(CXXFLAGS) -c $< -o $@ 15 | 16 | clean: 17 | rm -f $(OBJS) 18 | 19 | fclean: clean 20 | rm -f $(NAME) 21 | 22 | re: fclean all 23 | 24 | .PHONY: all clean fclean re -------------------------------------------------------------------------------- /project_cpp9/ex01/RPN.cpp: -------------------------------------------------------------------------------- 1 | #include "RPN.hpp" 2 | #include 3 | #include 4 | 5 | RPN::RPN() {} 6 | 7 | RPN::RPN(const RPN& other) : operands(other.operands) {} 8 | 9 | RPN& RPN::operator=(const RPN& other) { 10 | if (this != &other) { 11 | operands = other.operands; 12 | } 13 | return *this; 14 | } 15 | 16 | RPN::~RPN() {} 17 | 18 | bool RPN::isOperator(const std::string& token) const { 19 | return token == "+" || token == "-" || token == "*" || token == "/"; 20 | } 21 | 22 | int RPN::performOperation(int a, int b, const std::string& op) const { 23 | if (op == "+") return a + b; 24 | if (op == "-") return a - b; 25 | if (op == "*") return a * b; 26 | if (op == "/") { 27 | if (b == 0) throw std::runtime_error("Error: Division by zero"); 28 | return a / b; 29 | } 30 | throw std::runtime_error("Error: Unknown operator"); 31 | } 32 | 33 | int RPN::calculate(const std::string& expression) { 34 | std::istringstream iss(expression); 35 | std::string token; 36 | 37 | while (iss >> token) { 38 | if (isOperator(token)) { 39 | if (operands.size() < 2) { 40 | throw std::runtime_error("Error: Insufficient operands"); 41 | } 42 | int b = operands.top(); 43 | operands.pop(); 44 | int a = operands.top(); 45 | operands.pop(); 46 | operands.push(performOperation(a, b, token)); 47 | } else { 48 | char* end; 49 | int num = std::strtol(token.c_str(), &end, 10); 50 | if (*end != '\0' || num < 0 || num > 9) { 51 | throw std::runtime_error("Error: Invalid token: " ); 52 | } 53 | operands.push(num); 54 | } 55 | } 56 | 57 | if (operands.size() != 1) { 58 | throw std::runtime_error("Error: Invalid expression"); 59 | } 60 | 61 | return operands.top(); 62 | } -------------------------------------------------------------------------------- /project_cpp9/ex01/RPN.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RPN_HPP 2 | #define RPN_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class RPN { 9 | private: 10 | std::stack operands; 11 | 12 | bool isOperator(const std::string& token) const; 13 | int performOperation(int a, int b, const std::string& op) const; 14 | 15 | public: 16 | RPN(); 17 | RPN(const RPN& other); 18 | RPN& operator=(const RPN& other); 19 | ~RPN(); 20 | 21 | int calculate(const std::string& expression); 22 | }; 23 | 24 | #endif // RPN_HPP -------------------------------------------------------------------------------- /project_cpp9/ex01/main.cpp: -------------------------------------------------------------------------------- 1 | #include "RPN.hpp" 2 | #include 3 | 4 | int main(int argc, char* argv[]) { 5 | if (argc != 2) { 6 | std::cerr << "Error: Invalid number of arguments" << std::endl; 7 | return 1; 8 | } 9 | 10 | RPN calculator; 11 | try { 12 | int result = calculator.calculate(argv[1]); 13 | std::cout << result << std::endl; 14 | } catch (const std::exception& e) { 15 | std::cerr << e.what() << std::endl; 16 | return 1; 17 | } 18 | 19 | return 0; 20 | } -------------------------------------------------------------------------------- /project_cpp9/ex02/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/42nenuser/Ford-Johnson_algorithm/f71580b5a214123488edb4b8f29a247e43c35917/project_cpp9/ex02/.DS_Store -------------------------------------------------------------------------------- /project_cpp9/ex02/Makefile: -------------------------------------------------------------------------------- 1 | NAME = PmergeMe 2 | CC = g++ 3 | CFLAGS = -Wall -Wextra -Werror -std=c++98 4 | 5 | SRCS = main.cpp PmergeMe.cpp 6 | OBJS = $(SRCS:.cpp=.o) 7 | 8 | all: $(NAME) 9 | 10 | $(NAME): $(OBJS) 11 | $(CC) $(CFLAGS) $(OBJS) -o $(NAME) 12 | 13 | %.o: %.cpp 14 | $(CC) $(CFLAGS) -c $< -o $@ 15 | 16 | clean: 17 | rm -f $(OBJS) 18 | 19 | fclean: clean 20 | rm -f $(NAME) 21 | 22 | re: fclean all 23 | 24 | .PHONY: all clean fclean re 25 | -------------------------------------------------------------------------------- /project_cpp9/ex02/PmergeMe.cpp: -------------------------------------------------------------------------------- 1 | #include "PmergeMe.hpp" 2 | 3 | PmergeMe::PmergeMe(int ac, char **av) { 4 | std::deque inputDeque; 5 | std::list inputList; 6 | 7 | for (int i = 1; i < ac; ++i) { 8 | int value = atoi(av[i]); 9 | if (value <= 0) { 10 | std::cerr << "Error: Invalid input value \"" << av[i] << "\". Only positive integers are allowed." << std::endl; 11 | exit(1); 12 | } 13 | inputDeque.push_back(value); 14 | inputList.push_back(value); 15 | } 16 | std::cout << "Before: "; 17 | display(inputDeque); 18 | 19 | clock_t start1 = clock(); 20 | mergeInsertSortDeque(inputDeque); 21 | clock_t end1 = clock(); 22 | double time1 = static_cast(end1 - start1) / CLOCKS_PER_SEC * 1000; 23 | 24 | clock_t start2 = clock(); 25 | mergeInsertSortList(inputList); 26 | clock_t end2 = clock(); 27 | double time2 = static_cast(end2 - start2) / CLOCKS_PER_SEC * 1000; 28 | 29 | std::cout << "After: "; 30 | display(inputDeque); 31 | std::cout << "Time to process a range of " << inputDeque.size() << " elements with std::deque container: " << time1 << " us" << std::endl; 32 | std::cout << "Time to process a range of " << inputList.size() << " elements with std::list container: " << time2 << " us" << std::endl; 33 | if (inputDeque == std::deque(inputList.begin(), inputList.end())) 34 | std::cout << "The sorted sequences are equal." << std::endl; 35 | else 36 | std::cout << "The sorted sequences are not equal." << std::endl; 37 | } 38 | 39 | template 40 | void PmergeMe::display(const T& container) { 41 | typename T::const_iterator it; 42 | for (it = container.begin(); it != container.end(); ++it) 43 | std::cout << *it << " "; 44 | std::cout << std::endl; 45 | } 46 | 47 | template 48 | void PmergeMe::mergeInsertSort(T& arr) { 49 | if (arr.size() <= 1) return; 50 | 51 | // Step 1: Pair elements 52 | std::vector > pairs; 53 | typename T::iterator it = arr.begin(); 54 | while (it != arr.end() && std::next(it) != arr.end()) { 55 | typename T::value_type first = *it; 56 | typename T::value_type second = *std::next(it); 57 | if (first > second) std::swap(first, second); 58 | pairs.push_back(std::make_pair(first, second)); 59 | std::advance(it, 2); 60 | } 61 | 62 | // Handle odd element if present 63 | typename T::value_type odd_element; 64 | bool has_odd = (arr.size() % 2 != 0); 65 | if (has_odd) odd_element = *it; 66 | 67 | // Step 2: Recursively sort larger elements 68 | T larger_elements; 69 | for (size_t i = 0; i < pairs.size(); ++i) { 70 | larger_elements.push_back(pairs[i].second); 71 | } 72 | mergeInsertSort(larger_elements); 73 | 74 | // Step 3: Construct result 75 | arr.clear(); 76 | arr.push_back(pairs[0].first); 77 | typename T::iterator insert_pos = arr.begin(); 78 | for (typename T::iterator it = larger_elements.begin(); it != larger_elements.end(); ++it) { 79 | insert_pos = arr.insert(std::upper_bound(arr.begin(), arr.end(), *it), *it); 80 | } 81 | 82 | // Step 4: Insert smaller elements 83 | for (size_t i = 1; i < pairs.size(); ++i) { 84 | insert_pos = std::upper_bound(arr.begin(), arr.end(), pairs[i].first); 85 | arr.insert(insert_pos, pairs[i].first); 86 | } 87 | 88 | // Insert odd element if present 89 | if (has_odd) { 90 | insert_pos = std::upper_bound(arr.begin(), arr.end(), odd_element); 91 | arr.insert(insert_pos, odd_element); 92 | } 93 | } 94 | 95 | void PmergeMe::mergeInsertSortDeque(std::deque& arr) { 96 | mergeInsertSort(arr); 97 | } 98 | 99 | void PmergeMe::mergeInsertSortList(std::list& arr) { 100 | mergeInsertSort(arr); 101 | } -------------------------------------------------------------------------------- /project_cpp9/ex02/PmergeMe.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | class PmergeMe { 12 | public: 13 | PmergeMe(int ac, char **av); 14 | template 15 | void display(const T& container); 16 | void mergeInsertSortDeque(std::deque& arr); 17 | void mergeInsertSortList(std::list& arr); 18 | 19 | private: 20 | template 21 | void mergeInsertSort(T& arr); 22 | }; -------------------------------------------------------------------------------- /project_cpp9/ex02/main.cpp: -------------------------------------------------------------------------------- 1 | #include "PmergeMe.hpp" 2 | #include 3 | #include 4 | 5 | int main(int argc, char** argv) { 6 | if (argc < 2) { 7 | std::cerr << "Usage: " << argv[0] << " ..." << std::endl; 8 | return 1; 9 | } 10 | 11 | try { 12 | PmergeMe sorter(argc, argv); 13 | } catch (const std::exception& e) { 14 | std::cerr << "Error: " << e.what() << std::endl; 15 | return 1; 16 | } 17 | 18 | return 0; 19 | } --------------------------------------------------------------------------------