mirror of
https://hub.njuu.cf/TheAlgorithms/C-Plus-Plus.git
synced 2023-10-11 13:05:55 +08:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
8647a797bb
1
.github/stale.yml
vendored
1
.github/stale.yml
vendored
@ -13,6 +13,7 @@ onlyLabels: []
|
||||
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
|
||||
exemptLabels:
|
||||
- "approved"
|
||||
- "dont-close"
|
||||
|
||||
# Set to true to ignore issues in a project (defaults to false)
|
||||
exemptProjects: false
|
||||
|
48
.github/workflows/codeql_analysis.yml
vendored
Normal file
48
.github/workflows/codeql_analysis.yml
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
name: "CodeQL"
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'cpp' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
||||
# Learn more:
|
||||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@main
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@main
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@main
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@main
|
@ -54,6 +54,7 @@
|
||||
* [Tree 234](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/tree_234.cpp)
|
||||
* [Trie Modern](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/trie_modern.cpp)
|
||||
* [Trie Tree](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/trie_tree.cpp)
|
||||
* [Trie Using Hashmap](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/trie_using_hashmap.cpp)
|
||||
|
||||
## Dynamic Programming
|
||||
* [0 1 Knapsack](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/0_1_knapsack.cpp)
|
||||
@ -161,6 +162,7 @@
|
||||
* [Largest Power](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/largest_power.cpp)
|
||||
* [Lcm Sum](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/lcm_sum.cpp)
|
||||
* [Least Common Multiple](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/least_common_multiple.cpp)
|
||||
* [Linear Recurrence Matrix](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/linear_recurrence_matrix.cpp)
|
||||
* [Magic Number](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/magic_number.cpp)
|
||||
* [Miller Rabin](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/miller_rabin.cpp)
|
||||
* [Modular Division](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/modular_division.cpp)
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
[![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/TheAlgorithms/C-Plus-Plus)
|
||||
[![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/TheAlgorithms/C-Plus-Plus.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/TheAlgorithms/C-Plus-Plus/context:cpp)
|
||||
[![CodeQL CI](https://github.com/TheAlgorithms/C-Plus-Plus/actions/workflows/codeql_analysis.yml/badge.svg)](https://github.com/TheAlgorithms/C-Plus-Plus/actions/workflows/codeql_analysis.yml)
|
||||
[![Gitter chat](https://img.shields.io/badge/Chat-Gitter-ff69b4.svg?label=Chat&logo=gitter&style=flat-square)](https://gitter.im/TheAlgorithms)
|
||||
[![contributions welcome](https://img.shields.io/static/v1.svg?label=Contributions&message=Welcome&color=0059b3&style=flat-square)](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/CONTRIBUTING.md)
|
||||
![GitHub repo size](https://img.shields.io/github/repo-size/TheAlgorithms/C-Plus-Plus?color=red&style=flat-square)
|
||||
@ -11,7 +12,6 @@
|
||||
[![Income](https://img.shields.io/liberapay/receives/TheAlgorithms.svg?logo=liberapay)](https://liberapay.com/TheAlgorithms)
|
||||
[![Donate](https://liberapay.com/assets/widgets/donate.svg)](https://liberapay.com/TheAlgorithms/donate)
|
||||
|
||||
|
||||
## Overview
|
||||
|
||||
The repository is a collection of open-source implementation of a variety of algorithms implemented in C++ and licensed under [MIT License](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/LICENSE). The algorithms span a variety of topics from computer science, mathematics and statistics, data science, machine learning, engineering, etc.. The implementations and the associated documentation are meant to provide a learning resource for educators and students. Hence, one may find more than one implementation for the same objective but using a different algorithm strategies and optimizations.
|
||||
|
345
data_structures/trie_using_hashmap.cpp
Normal file
345
data_structures/trie_using_hashmap.cpp
Normal file
@ -0,0 +1,345 @@
|
||||
/**
|
||||
* @file
|
||||
* @author [Venkata Bharath](https://github.com/bharath000)
|
||||
* @brief Implementation of [Trie](https://en.wikipedia.org/wiki/Trie) data
|
||||
* structure using HashMap for different characters and method for predicting
|
||||
* words based on prefix.
|
||||
* @details The Trie data structure is implemented using unordered map to use
|
||||
* memory optimally, predict_words method is developed to recommend words based
|
||||
* on a given prefix along with other methods insert, delete, search, startwith
|
||||
* in trie.
|
||||
* @see trie_modern.cpp for difference
|
||||
*/
|
||||
#include <cassert> /// for assert
|
||||
#include <iostream> /// for IO operations
|
||||
#include <memory> /// for std::shared_ptr
|
||||
#include <stack> /// for std::stack
|
||||
#include <unordered_map> /// for std::unordered_map
|
||||
#include <vector> /// for std::vector
|
||||
|
||||
/**
|
||||
* @namespace data_structures
|
||||
* @brief Data structures algorithms
|
||||
*/
|
||||
namespace data_structures {
|
||||
|
||||
/**
|
||||
* @namespace trie_using_hashmap
|
||||
* @brief Functions for [Trie](https://en.wikipedia.org/wiki/Trie) data
|
||||
* structure using hashmap implementation
|
||||
*/
|
||||
namespace trie_using_hashmap {
|
||||
|
||||
/**
|
||||
* @brief Trie class, implementation of trie using hashmap in each trie node
|
||||
* for all the characters of char16_t(UTF-16)type with methods to insert,
|
||||
* delete, search, start with and to recommend words based on a given
|
||||
* prefix.
|
||||
*/
|
||||
class Trie {
|
||||
private:
|
||||
/**
|
||||
* @brief struct representing a trie node.
|
||||
*/
|
||||
struct Node {
|
||||
std::unordered_map<char16_t, std::shared_ptr<Node>>
|
||||
children; ///< unordered map with key type char16_t and value is a
|
||||
///< shared pointer type of Node
|
||||
bool word_end = false; ///< boolean variable to represent the node end
|
||||
};
|
||||
|
||||
std::shared_ptr<Node> root_node =
|
||||
std::make_shared<Node>(); ///< declaring root node of trie
|
||||
|
||||
public:
|
||||
///< Constructor
|
||||
Trie() = default;
|
||||
|
||||
/**
|
||||
* @brief insert the string into the trie
|
||||
* @param word string to insert in the trie
|
||||
*/
|
||||
void insert(const std::string& word) {
|
||||
std::shared_ptr<Node> curr = root_node;
|
||||
for (char ch : word) {
|
||||
if (curr->children.find(ch) == curr->children.end()) {
|
||||
curr->children[ch] = std::make_shared<Node>();
|
||||
}
|
||||
curr = curr->children[ch];
|
||||
}
|
||||
|
||||
if (!curr->word_end && curr != root_node) {
|
||||
curr->word_end = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief search a word/string inside the trie
|
||||
* @param word string to search for
|
||||
* @returns `true` if found
|
||||
* @returns `false` if not found
|
||||
*/
|
||||
bool search(const std::string& word) {
|
||||
std::shared_ptr<Node> curr = root_node;
|
||||
for (char ch : word) {
|
||||
if (curr->children.find(ch) == curr->children.end()) {
|
||||
return false;
|
||||
}
|
||||
curr = curr->children[ch];
|
||||
if (!curr) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (curr->word_end) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief search a word/string that starts with a given prefix
|
||||
* @param prefix string to search for
|
||||
* @returns `true` if found
|
||||
* @returns `false` if not found
|
||||
*/
|
||||
bool startwith(const std::string& prefix) {
|
||||
std::shared_ptr<Node> curr = root_node;
|
||||
for (char ch : prefix) {
|
||||
if (curr->children.find(ch) == curr->children.end()) {
|
||||
return false;
|
||||
}
|
||||
curr = curr->children[ch];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief delete a word/string from a trie
|
||||
* @param word string to delete from trie
|
||||
*/
|
||||
void delete_word(std::string word) {
|
||||
std::shared_ptr<Node> curr = root_node;
|
||||
std::stack<std::shared_ptr<Node>> nodes;
|
||||
int cnt = 0;
|
||||
for (char ch : word) {
|
||||
if (curr->children.find(ch) == curr->children.end()) {
|
||||
return;
|
||||
}
|
||||
if (curr->word_end) {
|
||||
cnt++;
|
||||
}
|
||||
|
||||
nodes.push(curr->children[ch]);
|
||||
curr = curr->children[ch];
|
||||
}
|
||||
// Delete only when it's a word, and it has children after
|
||||
// or prefix in the line
|
||||
if (nodes.top()->word_end) {
|
||||
nodes.top()->word_end = false;
|
||||
}
|
||||
// Delete only when it has no children after
|
||||
// and also no prefix in the line
|
||||
while (!(nodes.top()->word_end) && nodes.top()->children.empty()) {
|
||||
nodes.pop();
|
||||
nodes.top()->children.erase(word.back());
|
||||
word.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief helper function to predict/recommend words that starts with a
|
||||
* given prefix from the end of prefix's node iterate through all the child
|
||||
* nodes by recursively appending all the possible words below the trie
|
||||
* @param prefix string to recommend the words
|
||||
* @param element node at the end of the given prefix
|
||||
* @param results list to store the all possible words
|
||||
* @returns list of recommended words
|
||||
*/
|
||||
std::vector<std::string> get_all_words(std::vector<std::string> results,
|
||||
const std::shared_ptr<Node>& element,
|
||||
std::string prefix) {
|
||||
if (element->word_end) {
|
||||
results.push_back(prefix);
|
||||
}
|
||||
if (element->children.empty()) {
|
||||
return results;
|
||||
}
|
||||
for (auto const& x : element->children) {
|
||||
std::string key = "";
|
||||
key = x.first;
|
||||
prefix += key;
|
||||
|
||||
results =
|
||||
get_all_words(results, element->children[x.first], prefix);
|
||||
|
||||
prefix.pop_back();
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief predict/recommend a word that starts with a given prefix
|
||||
* @param prefix string to search for
|
||||
* @returns list of recommended words
|
||||
*/
|
||||
std::vector<std::string> predict_words(const std::string& prefix) {
|
||||
std::vector<std::string> result;
|
||||
std::shared_ptr<Node> curr = root_node;
|
||||
// traversing until the end of the given prefix in trie
|
||||
|
||||
for (char ch : prefix) {
|
||||
if (curr->children.find(ch) == curr->children.end()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
curr = curr->children[ch];
|
||||
}
|
||||
|
||||
// if the given prefix is the only word without children
|
||||
if (curr->word_end && curr->children.empty()) {
|
||||
result.push_back(prefix);
|
||||
return result;
|
||||
}
|
||||
|
||||
result = get_all_words(
|
||||
result, curr,
|
||||
prefix); ///< iteratively and recursively get the recommended words
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
} // namespace trie_using_hashmap
|
||||
} // namespace data_structures
|
||||
|
||||
/**
|
||||
* @brief Self-test implementations
|
||||
* @returns void
|
||||
*/
|
||||
static void test() {
|
||||
data_structures::trie_using_hashmap::Trie obj;
|
||||
// Inserting data into trie using the insert
|
||||
// method and testing it with search method
|
||||
obj.insert("app");
|
||||
obj.insert("abscond");
|
||||
obj.insert("about");
|
||||
obj.insert("apps");
|
||||
obj.insert("apen");
|
||||
obj.insert("apples");
|
||||
obj.insert("apple");
|
||||
obj.insert("approach");
|
||||
obj.insert("bus");
|
||||
obj.insert("buses");
|
||||
obj.insert("Apple");
|
||||
obj.insert("Bounce");
|
||||
|
||||
assert(!obj.search("appy"));
|
||||
std::cout << "appy is not a word in trie" << std::endl;
|
||||
|
||||
assert(!obj.search("car"));
|
||||
std::cout << "car is not a word in trie" << std::endl;
|
||||
assert(obj.search("app"));
|
||||
assert(obj.search("apple"));
|
||||
assert(obj.search("apples"));
|
||||
assert(obj.search("apps"));
|
||||
assert(obj.search("apen"));
|
||||
assert(obj.search("approach"));
|
||||
assert(obj.search("about"));
|
||||
assert(obj.search("abscond"));
|
||||
assert(obj.search("bus"));
|
||||
assert(obj.search("buses"));
|
||||
assert(obj.search("Bounce"));
|
||||
assert(obj.search("Apple"));
|
||||
|
||||
std::cout << "All the Inserted words are present in the trie" << std::endl;
|
||||
|
||||
// test for startwith prefix method
|
||||
assert(!obj.startwith("approachs"));
|
||||
assert(obj.startwith("approach"));
|
||||
assert(obj.startwith("about"));
|
||||
assert(!obj.startwith("appy"));
|
||||
assert(obj.startwith("abscond"));
|
||||
assert(obj.startwith("bus"));
|
||||
assert(obj.startwith("buses"));
|
||||
assert(obj.startwith("Bounce"));
|
||||
assert(obj.startwith("Apple"));
|
||||
assert(obj.startwith("abs"));
|
||||
assert(obj.startwith("b"));
|
||||
assert(obj.startwith("bus"));
|
||||
assert(obj.startwith("Bo"));
|
||||
assert(obj.startwith("A"));
|
||||
assert(!obj.startwith("Ca"));
|
||||
|
||||
assert(!obj.startwith("C"));
|
||||
|
||||
std::cout << "All the tests passed for startwith method" << std::endl;
|
||||
|
||||
// test for predict_words/recommendation of words based on a given prefix
|
||||
|
||||
std::vector<std::string> pred_words = obj.predict_words("a");
|
||||
|
||||
for (const std::string& str : obj.predict_words("a")) {
|
||||
std::cout << str << std::endl;
|
||||
}
|
||||
assert(pred_words.size() == 8);
|
||||
std::cout << "Returned all words that start with prefix a " << std::endl;
|
||||
pred_words = obj.predict_words("app");
|
||||
|
||||
for (const std::string& str : pred_words) {
|
||||
std::cout << str << std::endl;
|
||||
}
|
||||
|
||||
assert(pred_words.size() == 5);
|
||||
std::cout << "Returned all words that start with prefix app " << std::endl;
|
||||
pred_words = obj.predict_words("A");
|
||||
|
||||
for (const std::string& str : pred_words) {
|
||||
std::cout << str << std::endl;
|
||||
}
|
||||
|
||||
assert(pred_words.size() == 1);
|
||||
std::cout << "Returned all words that start with prefix A " << std::endl;
|
||||
pred_words = obj.predict_words("bu");
|
||||
|
||||
for (const std::string& str : pred_words) {
|
||||
std::cout << str << std::endl;
|
||||
}
|
||||
|
||||
assert(pred_words.size() == 2);
|
||||
std::cout << "Returned all words that start with prefix bu " << std::endl;
|
||||
|
||||
// tests for delete method
|
||||
|
||||
obj.delete_word("app");
|
||||
assert(!obj.search("app"));
|
||||
std::cout << "word app is deleted sucessful" << std::endl;
|
||||
|
||||
pred_words = obj.predict_words("app");
|
||||
for (const std::string& str : pred_words) {
|
||||
std::cout << str << std::endl;
|
||||
}
|
||||
assert(pred_words.size() == 4);
|
||||
std::cout << "app is deleted sucessful" << std::endl;
|
||||
|
||||
// test case for Chinese language
|
||||
|
||||
obj.insert("苹果");
|
||||
assert(obj.startwith("苹"));
|
||||
pred_words = obj.predict_words("h");
|
||||
|
||||
assert(pred_words.size() == 0);
|
||||
std::cout << "No word starts with prefix h in trie" << std::endl;
|
||||
|
||||
std::cout << "All tests passed" << std::endl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Main function
|
||||
* @returns 0 on exit
|
||||
*/
|
||||
int main() {
|
||||
test(); // run self-test implementaions
|
||||
return 0;
|
||||
}
|
372
math/linear_recurrence_matrix.cpp
Normal file
372
math/linear_recurrence_matrix.cpp
Normal file
@ -0,0 +1,372 @@
|
||||
/**
|
||||
* @brief Evaluate recurrence relation using [matrix
|
||||
* exponentiation](https://www.hackerearth.com/practice/notes/matrix-exponentiation-1/).
|
||||
* @details
|
||||
* Given a recurrence relation; evaluate the value of nth term.
|
||||
* For e.g., For fibonacci series, recurrence series is `f(n) = f(n-1) + f(n-2)`
|
||||
* where `f(0) = 0` and `f(1) = 1`.
|
||||
* Note that the method used only demonstrates
|
||||
* recurrence relation with one variable (n), unlike `nCr` problem, since it has
|
||||
* two (n, r)
|
||||
*
|
||||
* ### Algorithm
|
||||
* This problem can be solved using matrix exponentiation method.
|
||||
* @see here for simple [number exponentiation
|
||||
* algorithm](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/modular_exponentiation.cpp)
|
||||
* or [explaination
|
||||
* here](https://en.wikipedia.org/wiki/Exponentiation_by_squaring).
|
||||
* @author [Ashish Daulatabad](https://github.com/AshishYUO)
|
||||
*/
|
||||
#include <cassert> /// for assert
|
||||
#include <iostream> /// for IO operations
|
||||
#include <vector> /// for std::vector STL
|
||||
|
||||
/**
|
||||
* @namespace math
|
||||
* @brief Mathematical algorithms
|
||||
*/
|
||||
namespace math {
|
||||
/**
|
||||
* @namespace linear_recurrence_matrix
|
||||
* @brief Functions for [Linear Recurrence
|
||||
* Matrix](https://www.hackerearth.com/practice/notes/matrix-exponentiation-1/)
|
||||
* implementation.
|
||||
*/
|
||||
namespace linear_recurrence_matrix {
|
||||
/**
|
||||
* @brief Implementation of matrix multiplication
|
||||
* @details Multiplies matrix A and B, given total columns in A are equal to
|
||||
* total given rows in column B
|
||||
* @tparam T template type for integer as well as floating values, default is
|
||||
* long long int
|
||||
* @param _mat_a first matrix of size n * m
|
||||
* @param _mat_b second matrix of size m * k
|
||||
* @returns `_mat_c` resultant matrix of size n * k
|
||||
* Complexity: `O(n*m*k)`
|
||||
* @note The complexity in this case will be O(n^3) due to the nature of the
|
||||
* problem. We'll be multiplying the matrix with itself most of the time.
|
||||
*/
|
||||
template <typename T = int64_t>
|
||||
std::vector<std::vector<T>> matrix_multiplication(
|
||||
const std::vector<std::vector<T>>& _mat_a,
|
||||
const std::vector<std::vector<T>>& _mat_b, const int64_t mod = 1000000007) {
|
||||
// assert that columns in `_mat_a` and rows in `_mat_b` are equal
|
||||
assert(_mat_a[0].size() == _mat_b.size());
|
||||
std::vector<std::vector<T>> _mat_c(_mat_a.size(),
|
||||
std::vector<T>(_mat_b[0].size(), 0));
|
||||
/**
|
||||
* Actual matrix multiplication.
|
||||
*/
|
||||
for (uint32_t i = 0; i < _mat_a.size(); ++i) {
|
||||
for (uint32_t j = 0; j < _mat_b[0].size(); ++j) {
|
||||
for (uint32_t k = 0; k < _mat_b.size(); ++k) {
|
||||
_mat_c[i][j] =
|
||||
(_mat_c[i][j] % mod +
|
||||
(_mat_a[i][k] % mod * _mat_b[k][j] % mod) % mod) %
|
||||
mod;
|
||||
}
|
||||
}
|
||||
}
|
||||
return _mat_c;
|
||||
}
|
||||
/**
|
||||
* @brief Returns whether matrix `mat` is a [zero
|
||||
* matrix.](https://en.wikipedia.org/wiki/Zero_matrix)
|
||||
* @tparam T template type for integer as well as floating values, default is
|
||||
* long long int
|
||||
* @param _mat A matrix
|
||||
* @returns true if it is a zero matrix else false
|
||||
*/
|
||||
template <typename T = int64_t>
|
||||
bool is_zero_matrix(const std::vector<std::vector<T>>& _mat) {
|
||||
for (uint32_t i = 0; i < _mat.size(); ++i) {
|
||||
for (uint32_t j = 0; j < _mat[i].size(); ++j) {
|
||||
if (_mat[i][j] != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Implementation of Matrix exponentiation
|
||||
* @details returns the matrix exponentiation `(B^n)` in `k^3 * O(log2(power))`
|
||||
* time, where `k` is the size of matrix (k by k).
|
||||
* @tparam T template type for integer as well as floating values, default is
|
||||
* long long int
|
||||
* @param _mat matrix for exponentiation
|
||||
* @param power the exponent value
|
||||
* @returns the matrix _mat to the power `power (_mat^power)`
|
||||
*/
|
||||
template <typename T = int64_t>
|
||||
std::vector<std::vector<T>> matrix_exponentiation(
|
||||
std::vector<std::vector<T>> _mat, uint64_t power,
|
||||
const int64_t mod = 1000000007) {
|
||||
/**
|
||||
* Initializing answer as identity matrix. For simple binary
|
||||
* exponentiation reference, [see
|
||||
* here](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/modular_exponentiation.cpp)
|
||||
*/
|
||||
if (is_zero_matrix(_mat)) {
|
||||
return _mat;
|
||||
}
|
||||
|
||||
std::vector<std::vector<T>> _mat_answer(_mat.size(),
|
||||
std::vector<T>(_mat.size(), 0));
|
||||
|
||||
for (uint32_t i = 0; i < _mat.size(); ++i) {
|
||||
_mat_answer[i][i] = 1;
|
||||
}
|
||||
// exponentiation algorithm here.
|
||||
while (power > 0) {
|
||||
if (power & 1) {
|
||||
_mat_answer = matrix_multiplication(_mat_answer, _mat, mod);
|
||||
}
|
||||
power >>= 1;
|
||||
_mat = matrix_multiplication(_mat, _mat, mod);
|
||||
}
|
||||
|
||||
return _mat_answer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Implementation of nth recurrence series.
|
||||
* @details Returns the nth term in the recurrence series.
|
||||
* Note that the function assumes definition of base cases from `n = 0`
|
||||
* (e.g., for fibonacci, `f(0)` has a defined value `0`)
|
||||
* @tparam T template type for integer as well as floating values, default is
|
||||
* long long int
|
||||
* @param _mat [square matrix](https://en.m.wikipedia.org/wiki/Square_matrix)
|
||||
* that evaluates the nth term using exponentiation
|
||||
* @param _base_cases 2D array of dimension `1*n` containing values which are
|
||||
* defined for some n (e.g., for fibonacci, `f(0)` and `f(1)` are defined, and
|
||||
* `f(n)` where `n > 1` is evaluated on previous two values)
|
||||
* @param nth_term the nth term of recurrence relation
|
||||
* @param constant_or_sum_included whether the recurrence relation has a
|
||||
* constant value or is evaluating sum of first n terms of the recurrence.
|
||||
* @returns the nth term of the recurrence relation in `O(k^3. log(n))`, where k
|
||||
* is number of rows and columns in `_mat` and `n` is the value of `nth_term`
|
||||
* If constant_or_sum_included is true, returns the sum of first n terms in
|
||||
* recurrence series
|
||||
*/
|
||||
template <typename T = int64_t>
|
||||
T get_nth_term_of_recurrence_series(
|
||||
const std::vector<std::vector<T>>& _mat,
|
||||
const std::vector<std::vector<T>>& _base_cases, uint64_t nth_term,
|
||||
bool constant_or_sum_included = false) {
|
||||
assert(_mat.size() == _base_cases.back().size());
|
||||
|
||||
/**
|
||||
* If nth term is a base case, then return base case directly.
|
||||
*/
|
||||
|
||||
if (nth_term < _base_cases.back().size() - constant_or_sum_included) {
|
||||
return _base_cases.back()[nth_term - constant_or_sum_included];
|
||||
} else {
|
||||
/**
|
||||
* Else evaluate the expression, so multiplying _mat to itself (n -
|
||||
* base_cases.length + 1 + constant_or_sum_included) times.
|
||||
*/
|
||||
std::vector<std::vector<T>> _res_matrix =
|
||||
matrix_exponentiation(_mat, nth_term - _base_cases.back().size() +
|
||||
1 + constant_or_sum_included);
|
||||
|
||||
/**
|
||||
* After matrix exponentiation, multiply with the base case to evaluate
|
||||
* the answer. The answer is always at the end of the array.
|
||||
*/
|
||||
std::vector<std::vector<T>> _res =
|
||||
matrix_multiplication(_base_cases, _res_matrix);
|
||||
|
||||
return _res.back().back();
|
||||
}
|
||||
}
|
||||
} // namespace linear_recurrence_matrix
|
||||
} // namespace math
|
||||
|
||||
/**
|
||||
* @brief Self test-implementations
|
||||
* @returns void
|
||||
*/
|
||||
static void test() {
|
||||
/*
|
||||
* Example 1: [Fibonacci
|
||||
* series](https://en.wikipedia.org/wiki/Fibonacci_number);
|
||||
*
|
||||
* [fn-2 fn-1] [0 1] == [fn-1 (fn-2 + fn-1)] => [fn-1 fn]
|
||||
* [1 1]
|
||||
*
|
||||
* Let A = [fn-2 fn-1], and B = [0 1]
|
||||
* [1 1],
|
||||
*
|
||||
* Since, A.B....(n-1 times) = [fn-1 fn]
|
||||
* we can multiply B with itself n-1 times to obtain the required value
|
||||
*/
|
||||
std::vector<std::vector<int64_t>> fibonacci_matrix = {{0, 1}, {1, 1}},
|
||||
fib_base_case = {{0, 1}};
|
||||
|
||||
assert(math::linear_recurrence_matrix::get_nth_term_of_recurrence_series(
|
||||
fibonacci_matrix, fib_base_case, 11) == 89LL);
|
||||
assert(math::linear_recurrence_matrix::get_nth_term_of_recurrence_series(
|
||||
fibonacci_matrix, fib_base_case, 39) == 63245986LL);
|
||||
/*
|
||||
* Example 2: [Tribonacci series](https://oeis.org/A000073)
|
||||
* [0 0 1]
|
||||
* [fn-3 fn-2 fn-1] [1 0 1] = [(fn-2) (fn-1) (fn-3 + fn-2 + fn-1)]
|
||||
* [0 1 1]
|
||||
* => [fn-2 fn-1 fn]
|
||||
*
|
||||
* [0 0 1]
|
||||
* Let A = [fn-3 fn-2 fn-1], and B = [1 0 1]
|
||||
* [0 1 1]
|
||||
*
|
||||
* Since, A.B....(n-2 times) = [fn-2 fn-1 fn]
|
||||
* we will have multiply B with itself n-2 times to obtain the required
|
||||
* value ()
|
||||
*/
|
||||
|
||||
std::vector<std::vector<int64_t>> tribonacci = {{0, 0, 1},
|
||||
{1, 0, 1},
|
||||
{0, 1, 1}},
|
||||
trib_base_case = {
|
||||
{0, 0, 1}}; // f0 = 0, f1 = 0, f2 = 1
|
||||
|
||||
assert(math::linear_recurrence_matrix::get_nth_term_of_recurrence_series(
|
||||
tribonacci, trib_base_case, 11) == 149LL);
|
||||
assert(math::linear_recurrence_matrix::get_nth_term_of_recurrence_series(
|
||||
tribonacci, trib_base_case, 36) == 615693474LL);
|
||||
|
||||
/*
|
||||
* Example 3: [Pell numbers](https://oeis.org/A000129)
|
||||
* `f(n) = 2* f(n-1) + f(n-2); f(0) = f(1) = 2`
|
||||
*
|
||||
* [fn-2 fn-1] [0 1] = [(fn-1) fn-2 + 2*fn-1)]
|
||||
* [1 2]
|
||||
* => [fn-1 fn]
|
||||
*
|
||||
* Let A = [fn-2 fn-1], and B = [0 1]
|
||||
* [1 2]
|
||||
*/
|
||||
|
||||
std::vector<std::vector<int64_t>> pell_recurrence = {{0, 1}, {1, 2}},
|
||||
pell_base_case = {
|
||||
{2, 2}}; // `f0 = 2, f1 = 2`
|
||||
|
||||
assert(math::linear_recurrence_matrix::get_nth_term_of_recurrence_series(
|
||||
pell_recurrence, pell_base_case, 15) == 551614LL);
|
||||
assert(math::linear_recurrence_matrix::get_nth_term_of_recurrence_series(
|
||||
pell_recurrence, pell_base_case, 23) == 636562078LL);
|
||||
|
||||
/*
|
||||
* Example 4: Custom recurrence relation:
|
||||
* Now the recurrence is of the form `a*f(n-1) + b*(fn-2) + ... + c`
|
||||
* where `c` is the constant
|
||||
* `f(n) = 2* f(n-1) + f(n-2) + 7; f(0) = f(1) = 2, c = 7`
|
||||
*
|
||||
* [1 0 1]
|
||||
* [7, fn-2, fn-1] [0 0 1]
|
||||
* [0 1 2]
|
||||
* = [7, (fn-1), fn-2 + 2*fn-1) + 7]
|
||||
*
|
||||
* => [7, fn-1, fn]
|
||||
* :: Series will be 2, 2, 13, 35, 90, 222, 541, 1311, 3170, 7658, 18493,
|
||||
* 44651, 107802, 260262, 628333, 1516935, 362210, 8841362, 21344941,
|
||||
* 51531251
|
||||
*
|
||||
* Let A = [7, fn-2, fn-1], and B = [1 0 1]
|
||||
* [0 0 1]
|
||||
* [0 1 2]
|
||||
*/
|
||||
|
||||
std::vector<std::vector<int64_t>>
|
||||
custom_recurrence = {{1, 0, 1}, {0, 0, 1}, {0, 1, 2}},
|
||||
custom_base_case = {{7, 2, 2}}; // `c = 7, f0 = 2, f1 = 2`
|
||||
|
||||
assert(math::linear_recurrence_matrix::get_nth_term_of_recurrence_series(
|
||||
custom_recurrence, custom_base_case, 10, 1) == 18493LL);
|
||||
assert(math::linear_recurrence_matrix::get_nth_term_of_recurrence_series(
|
||||
custom_recurrence, custom_base_case, 19, 1) == 51531251LL);
|
||||
|
||||
/*
|
||||
* Example 5: Sum fibonacci sequence
|
||||
* The following matrix evaluates the sum of first n fibonacci terms in
|
||||
* O(27. log2(n)) time.
|
||||
* `f(n) = f(n-1) + f(n-2); f(0) = 0, f(1) = 1`
|
||||
*
|
||||
* [1 0 0]
|
||||
* [s(f, n-1), fn-2, fn-1] [1 0 1]
|
||||
* [1 1 1]
|
||||
* => [(s(f, n-1)+f(n-2)+f(n-1)), (fn-1), f(n-2)+f(n-1)]
|
||||
*
|
||||
* => [s(f, n-1)+f(n), fn-1, fn]
|
||||
*
|
||||
* => [s(f, n), fn-1, fn]
|
||||
*
|
||||
* Sum of first 20 fibonacci series:
|
||||
* 0, 1, 2, 4, 7, 12, 20, 33, 54, 88, 143, 232, 376, 609, 986, 1596, 2583,
|
||||
* 4180, 6764
|
||||
* f0 f1 s(f,1)
|
||||
* Let A = [0 1 1], and B = [0 1 1]
|
||||
* [1 1 1]
|
||||
* [0 0 1]
|
||||
*/
|
||||
|
||||
std::vector<std::vector<int64_t>> sum_fibo_recurrence = {{0, 1, 1},
|
||||
{1, 1, 1},
|
||||
{0, 0, 1}},
|
||||
sum_fibo_base_case = {
|
||||
{0, 1, 1}}; // `f0 = 0, f1 = 1`
|
||||
|
||||
assert(math::linear_recurrence_matrix::get_nth_term_of_recurrence_series(
|
||||
sum_fibo_recurrence, sum_fibo_base_case, 13, 1) == 609LL);
|
||||
assert(math::linear_recurrence_matrix::get_nth_term_of_recurrence_series(
|
||||
sum_fibo_recurrence, sum_fibo_base_case, 16, 1) == 2583LL);
|
||||
/*
|
||||
* Example 6: [Tribonacci sum series](https://oeis.org/A000073)
|
||||
* [0 0 1 1]
|
||||
* [fn-3 fn-2 fn-1 s(f, n-1)] [1 0 1 1]
|
||||
* [0 1 1 1]
|
||||
* [0 0 0 1]
|
||||
*
|
||||
* = [fn-2, fn-1, fn-3 + fn-2 + fn-1, (fn-3 + fn-2 + fn-1 + s(f, n-1))]
|
||||
*
|
||||
* => [fn-2, fn-1, fn, fn + s(f, n-1)]
|
||||
*
|
||||
* => [fn-2, fn-1, fn, s(f, n)]
|
||||
*
|
||||
* Sum of the series is: 0, 0, 1, 2, 4, 8, 15, 28, 52, 96, 177, 326, 600,
|
||||
* 1104, 2031, 3736, 6872, 12640, 23249, 42762
|
||||
*
|
||||
* Let A = [fn-3 fn-2 fn-1 s(f, n-1)], and
|
||||
* [0 0 1 1]
|
||||
* B = [1 0 1 1]
|
||||
* [0 1 1 1]
|
||||
* [0 0 0 1]
|
||||
*
|
||||
* Since, A.B....(n-2 times) = [fn-2 fn-1 fn]
|
||||
* we will have multiply B with itself n-2 times to obtain the required
|
||||
* value
|
||||
*/
|
||||
|
||||
std::vector<std::vector<int64_t>> tribonacci_sum = {{0, 0, 1, 1},
|
||||
{1, 0, 1, 1},
|
||||
{0, 1, 1, 1},
|
||||
{0, 0, 0, 1}},
|
||||
trib_sum_base_case = {{0, 0, 1, 1}};
|
||||
// `f0 = 0, f1 = 0, f2 = 1, s = 1`
|
||||
|
||||
assert(math::linear_recurrence_matrix::get_nth_term_of_recurrence_series(
|
||||
tribonacci_sum, trib_sum_base_case, 18, 1) == 23249LL);
|
||||
assert(math::linear_recurrence_matrix::get_nth_term_of_recurrence_series(
|
||||
tribonacci_sum, trib_sum_base_case, 19, 1) == 42762LL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Main function
|
||||
* @returns 0 on exit
|
||||
*/
|
||||
int main() {
|
||||
test(); // run self-test implementations
|
||||
return 0;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user