Files
C-Plus-Plus/math/linear_recurrence_matrix.cpp
realstealthninja c6af943508 fix: add cstdint header to all files using fixed width integers (#2717)
* fix: add <cstdint> to subset_sum.cpp

* fix: add <cstdint> to subarray_sum.cpp

* fix: add <cstdint> to wildcard_matching.cpp

* fix: add <cstdint> to count_bit_flips.cpp

* fix: add <cstdint> to count_of_set_bits.cpp

* fix: add <cstdint> to trailing_ciphers.cpp

* fix: add <cstdint> to hamming_distance.cpp

* doc: include doc for hamming_distance

* fix: add <cstdint> to next_higher_numebr_with_same_number_of_set_bits.cpp

* fix: add <cstdint> to power_of_2.cpp

* fix: add <cstdint> to set_kth_bit.cpp

* fix: add <cstdint> to bit_manipulation/set_kth_bit.cpp

* fix: add <cstdint> to bit_manipulation/travelling_salesman_using_bit_manipulation.cpp

* fix: add <cstdint> to ciphers/base64_encoding.cpp

* fix: add <cstdint> to ciphers/hill_cipher.cpp

* fix: add <cstdint> to ciphers/uint128_t.hpp

* fix: add <cstdint> to data_structures/dsu_path_compression.cpp

* fix: add <cstdint> to data_structures/dsu_path_compression.cpp

* fix add <cstdint> to datastructures/list_array>cpp

* fix add <cstdint> to datastructures/queue_using_array.cpp

* fix: add <cstdint> to sparse_table.cpp

* fix: add <cstdint> to stack_using_list_queue.cpp

* fix: add <cstdint> to treap.cpp

* fix: add <cstdint> to graham_scan_functions.hpp

* fix: add <cstdint> to graph/**

* fix: add integral typdefs to hashing/**

* fix: add <cstdint> to math/**

* fix: add <cstdint> to numerical_methods/**

* fix: add <cstdint> to other/**

* fix: add <cstdint> to search/**

* fix: add <cstdint> to sorting/**

* fix: add <cstdint> to string/**

* doc: remove include statement from comment

* fix: make tests static

Co-authored-by: David Leal <halfpacho@gmail.com>

* fix: make tests static

Co-authored-by: David Leal <halfpacho@gmail.com>

* chore: use iwyu on backtracking/**.cpp

* chore: use iwyu on bit_manip/**.cpp

* chore: use iwyu on ciphers/**.cpp

* chore: use iwyu on cpu_scheduling_algorithms/**.cpp

* chore: use iwyu on data_structures/**.cpp

* chore: use iwyu on divide_and_conquer/**.cpp

* chore: use iwyu on geometry/**.cpp

* chore: use iwyu on graph/**.cpp

* chore: use iwyu on hashing/**.cpp

* chore: use iwyu on machine_learning/**.cpp

* chore: use iwyu on math/**.cpp

* chore: use iwyu on numerical_methods/**.cpp

* chore: use iwyu on others/**.cpp

* chore: use iwyu on probablity/**.cpp

* chore: use iwyu on search/**.cpp

* chore: use iwyu on sorting/**.cpp

* chore: use iwyu on strings/**.cpp

* Revert "chore: use iwyu on strings/**.cpp"

This reverts commit f2127456a8.

* Revert "chore: use iwyu on sorting/**.cpp"

This reverts commit a290ae7ee2.

* Revert "chore: use iwyu on search/**.cpp"

This reverts commit 19d136ae0f.

* Revert "chore: use iwyu on probablity/**.cpp"

This reverts commit 5dd7f82a34.

* Revert "chore: use iwyu on others/**.cpp"

This reverts commit 8a8fd42383.

* Revert "chore: use iwyu on numerical_methods/**.cpp"

This reverts commit eff2f44a50.

* Revert "chore: use iwyu on math/**.cpp"

This reverts commit c47117ca3f.

* Revert "chore: use iwyu on machine_learning/**.cpp"

This reverts commit c3897d3763.

* Revert "chore: use iwyu on hashing/**.cpp"

This reverts commit 0c6611a835.

* Revert "chore: use iwyu on graph/**.cpp"

This reverts commit dabd6d2591.

* Revert "chore: use iwyu on geometry/**.cpp"

This reverts commit 740bd65932.

* Revert "chore: use iwyu on divide_and_conquer/**.cpp"

This reverts commit 16ee49e086.

* Revert "chore: use iwyu on data_structures/**.cpp"

This reverts commit a3b719e368.

* Revert "chore: use iwyu on cpu_scheduling_algorithms/**.cpp"

This reverts commit 24e597f7e2.

* Revert "chore: use iwyu on ciphers/**.cpp"

This reverts commit 3d80295883.

* Revert "chore: use iwyu on bit_manip/**.cpp"

This reverts commit 7edcb6e458.

* Revert "chore: use iwyu on backtracking/**.cpp"

This reverts commit f0a30d7cdb.

* Update search/binary_search.cpp

* Update backtracking/subarray_sum.cpp

* Update backtracking/subset_sum.cpp

* Update backtracking/wildcard_matching.cpp

* Update bit_manipulation/count_bits_flip.cpp

* Update bit_manipulation/count_of_set_bits.cpp

* Update bit_manipulation/count_of_trailing_ciphers_in_factorial_n.cpp

* Update bit_manipulation/hamming_distance.cpp

* Update bit_manipulation/next_higher_number_with_same_number_of_set_bits.cpp

* Update bit_manipulation/power_of_2.cpp

* Update others/lru_cache.cpp

* Update bit_manipulation/set_kth_bit.cpp

* Update bit_manipulation/travelling_salesman_using_bit_manipulation.cpp

* Update ciphers/base64_encoding.cpp

* Update ciphers/hill_cipher.cpp

* Update ciphers/uint128_t.hpp

* Update cpu_scheduling_algorithms/fcfs_scheduling.cpp

* Update data_structures/dsu_path_compression.cpp

* Update data_structures/dsu_union_rank.cpp

* Update data_structures/list_array.cpp

* Update data_structures/queue_using_array.cpp

* Update data_structures/sparse_table.cpp

* Update data_structures/stack_using_queue.cpp

* Update data_structures/treap.cpp

* Update geometry/graham_scan_functions.hpp

* Update graph/bidirectional_dijkstra.cpp

* Update graph/connected_components_with_dsu.cpp

* Update graph/cycle_check_directed_graph.cpp

* Update graph/is_graph_bipartite2.cpp

* Update graph/travelling_salesman_problem.cpp

* Update hashing/md5.cpp

* Update hashing/sha1.cpp

* Update math/n_choose_r.cpp

* Update strings/z_function.cpp

* Update strings/manacher_algorithm.cpp

* Update sorting/wiggle_sort.cpp

* Update sorting/selection_sort_recursive.cpp

* Update sorting/selection_sort_iterative.cpp

* Update sorting/recursive_bubble_sort.cpp

* Update sorting/radix_sort2.cpp

* Update sorting/dnf_sort.cpp

* Update sorting/cycle_sort.cpp

* Update search/sublist_search.cpp

* Update search/saddleback_search.cpp

* Update search/interpolation_search.cpp

* Update search/floyd_cycle_detection_algo.cpp

* Update search/exponential_search.cpp

* Update search/exponential_search.cpp

* Update math/n_bonacci.cpp

* Update math/aliquot_sum.cpp

* Update math/check_factorial.cpp

* Update math/double_factorial.cpp

* Update math/eulers_totient_function.cpp

* Update math/factorial.cpp

* Update math/fibonacci.cpp

* Update math/fibonacci_matrix_exponentiation.cpp

* Update math/fibonacci_sum.cpp

* Update math/finding_number_of_digits_in_a_number.cpp

* chore: remove "/// for integral typedefs"

* chore: remove for integral typedefs from modular division

* fix: remove comment from include

* fix: add cstdint to gale shapely

---------

Co-authored-by: David Leal <halfpacho@gmail.com>
2024-11-04 17:38:54 +05:30

374 lines
14 KiB
C++

/**
* @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 <cstdint>
#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;
}