TheAlgorithms/C++ 1.0.0
All the algorithms implemented in C++
Loading...
Searching...
No Matches
linear_recurrence_matrix.cpp
1
20#include <cassert>
21#include <cstdint>
22#include <iostream>
23#include <vector>
24
29namespace math {
50template <typename T = int64_t>
51std::vector<std::vector<T>> matrix_multiplication(
52 const std::vector<std::vector<T>>& _mat_a,
53 const std::vector<std::vector<T>>& _mat_b, const int64_t mod = 1000000007) {
54 // assert that columns in `_mat_a` and rows in `_mat_b` are equal
55 assert(_mat_a[0].size() == _mat_b.size());
56 std::vector<std::vector<T>> _mat_c(_mat_a.size(),
57 std::vector<T>(_mat_b[0].size(), 0));
61 for (uint32_t i = 0; i < _mat_a.size(); ++i) {
62 for (uint32_t j = 0; j < _mat_b[0].size(); ++j) {
63 for (uint32_t k = 0; k < _mat_b.size(); ++k) {
64 _mat_c[i][j] =
65 (_mat_c[i][j] % mod +
66 (_mat_a[i][k] % mod * _mat_b[k][j] % mod) % mod) %
67 mod;
68 }
69 }
70 }
71 return _mat_c;
72}
81template <typename T = int64_t>
82bool is_zero_matrix(const std::vector<std::vector<T>>& _mat) {
83 for (uint32_t i = 0; i < _mat.size(); ++i) {
84 for (uint32_t j = 0; j < _mat[i].size(); ++j) {
85 if (_mat[i][j] != 0) {
86 return false;
87 }
88 }
89 }
90 return true;
91}
92
103template <typename T = int64_t>
104std::vector<std::vector<T>> matrix_exponentiation(
105 std::vector<std::vector<T>> _mat, uint64_t power,
106 const int64_t mod = 1000000007) {
112 if (is_zero_matrix(_mat)) {
113 return _mat;
114 }
115
116 std::vector<std::vector<T>> _mat_answer(_mat.size(),
117 std::vector<T>(_mat.size(), 0));
118
119 for (uint32_t i = 0; i < _mat.size(); ++i) {
120 _mat_answer[i][i] = 1;
121 }
122 // exponentiation algorithm here.
123 while (power > 0) {
124 if (power & 1) {
125 _mat_answer = matrix_multiplication(_mat_answer, _mat, mod);
126 }
127 power >>= 1;
128 _mat = matrix_multiplication(_mat, _mat, mod);
129 }
130
131 return _mat_answer;
132}
133
154template <typename T = int64_t>
155T get_nth_term_of_recurrence_series(
156 const std::vector<std::vector<T>>& _mat,
157 const std::vector<std::vector<T>>& _base_cases, uint64_t nth_term,
158 bool constant_or_sum_included = false) {
159 assert(_mat.size() == _base_cases.back().size());
160
165 if (nth_term < _base_cases.back().size() - constant_or_sum_included) {
166 return _base_cases.back()[nth_term - constant_or_sum_included];
167 } else {
172 std::vector<std::vector<T>> _res_matrix =
173 matrix_exponentiation(_mat, nth_term - _base_cases.back().size() +
174 1 + constant_or_sum_included);
175
180 std::vector<std::vector<T>> _res =
181 matrix_multiplication(_base_cases, _res_matrix);
182
183 return _res.back().back();
184 }
185}
186} // namespace linear_recurrence_matrix
187} // namespace math
188
193static void test() {
194 /*
195 * Example 1: [Fibonacci
196 * series](https://en.wikipedia.org/wiki/Fibonacci_number);
197 *
198 * [fn-2 fn-1] [0 1] == [fn-1 (fn-2 + fn-1)] => [fn-1 fn]
199 * [1 1]
200 *
201 * Let A = [fn-2 fn-1], and B = [0 1]
202 * [1 1],
203 *
204 * Since, A.B....(n-1 times) = [fn-1 fn]
205 * we can multiply B with itself n-1 times to obtain the required value
206 */
207 std::vector<std::vector<int64_t>> fibonacci_matrix = {{0, 1}, {1, 1}},
208 fib_base_case = {{0, 1}};
209
210 assert(math::linear_recurrence_matrix::get_nth_term_of_recurrence_series(
211 fibonacci_matrix, fib_base_case, 11) == 89LL);
212 assert(math::linear_recurrence_matrix::get_nth_term_of_recurrence_series(
213 fibonacci_matrix, fib_base_case, 39) == 63245986LL);
214 /*
215 * Example 2: [Tribonacci series](https://oeis.org/A000073)
216 * [0 0 1]
217 * [fn-3 fn-2 fn-1] [1 0 1] = [(fn-2) (fn-1) (fn-3 + fn-2 + fn-1)]
218 * [0 1 1]
219 * => [fn-2 fn-1 fn]
220 *
221 * [0 0 1]
222 * Let A = [fn-3 fn-2 fn-1], and B = [1 0 1]
223 * [0 1 1]
224 *
225 * Since, A.B....(n-2 times) = [fn-2 fn-1 fn]
226 * we will have multiply B with itself n-2 times to obtain the required
227 * value ()
228 */
229
230 std::vector<std::vector<int64_t>> tribonacci = {{0, 0, 1},
231 {1, 0, 1},
232 {0, 1, 1}},
233 trib_base_case = {
234 {0, 0, 1}}; // f0 = 0, f1 = 0, f2 = 1
235
236 assert(math::linear_recurrence_matrix::get_nth_term_of_recurrence_series(
237 tribonacci, trib_base_case, 11) == 149LL);
238 assert(math::linear_recurrence_matrix::get_nth_term_of_recurrence_series(
239 tribonacci, trib_base_case, 36) == 615693474LL);
240
241 /*
242 * Example 3: [Pell numbers](https://oeis.org/A000129)
243 * `f(n) = 2* f(n-1) + f(n-2); f(0) = f(1) = 2`
244 *
245 * [fn-2 fn-1] [0 1] = [(fn-1) fn-2 + 2*fn-1)]
246 * [1 2]
247 * => [fn-1 fn]
248 *
249 * Let A = [fn-2 fn-1], and B = [0 1]
250 * [1 2]
251 */
252
253 std::vector<std::vector<int64_t>> pell_recurrence = {{0, 1}, {1, 2}},
254 pell_base_case = {
255 {2, 2}}; // `f0 = 2, f1 = 2`
256
257 assert(math::linear_recurrence_matrix::get_nth_term_of_recurrence_series(
258 pell_recurrence, pell_base_case, 15) == 551614LL);
259 assert(math::linear_recurrence_matrix::get_nth_term_of_recurrence_series(
260 pell_recurrence, pell_base_case, 23) == 636562078LL);
261
262 /*
263 * Example 4: Custom recurrence relation:
264 * Now the recurrence is of the form `a*f(n-1) + b*(fn-2) + ... + c`
265 * where `c` is the constant
266 * `f(n) = 2* f(n-1) + f(n-2) + 7; f(0) = f(1) = 2, c = 7`
267 *
268 * [1 0 1]
269 * [7, fn-2, fn-1] [0 0 1]
270 * [0 1 2]
271 * = [7, (fn-1), fn-2 + 2*fn-1) + 7]
272 *
273 * => [7, fn-1, fn]
274 * :: Series will be 2, 2, 13, 35, 90, 222, 541, 1311, 3170, 7658, 18493,
275 * 44651, 107802, 260262, 628333, 1516935, 362210, 8841362, 21344941,
276 * 51531251
277 *
278 * Let A = [7, fn-2, fn-1], and B = [1 0 1]
279 * [0 0 1]
280 * [0 1 2]
281 */
282
283 std::vector<std::vector<int64_t>>
284 custom_recurrence = {{1, 0, 1}, {0, 0, 1}, {0, 1, 2}},
285 custom_base_case = {{7, 2, 2}}; // `c = 7, f0 = 2, f1 = 2`
286
287 assert(math::linear_recurrence_matrix::get_nth_term_of_recurrence_series(
288 custom_recurrence, custom_base_case, 10, 1) == 18493LL);
289 assert(math::linear_recurrence_matrix::get_nth_term_of_recurrence_series(
290 custom_recurrence, custom_base_case, 19, 1) == 51531251LL);
291
292 /*
293 * Example 5: Sum fibonacci sequence
294 * The following matrix evaluates the sum of first n fibonacci terms in
295 * O(27. log2(n)) time.
296 * `f(n) = f(n-1) + f(n-2); f(0) = 0, f(1) = 1`
297 *
298 * [1 0 0]
299 * [s(f, n-1), fn-2, fn-1] [1 0 1]
300 * [1 1 1]
301 * => [(s(f, n-1)+f(n-2)+f(n-1)), (fn-1), f(n-2)+f(n-1)]
302 *
303 * => [s(f, n-1)+f(n), fn-1, fn]
304 *
305 * => [s(f, n), fn-1, fn]
306 *
307 * Sum of first 20 fibonacci series:
308 * 0, 1, 2, 4, 7, 12, 20, 33, 54, 88, 143, 232, 376, 609, 986, 1596, 2583,
309 * 4180, 6764
310 * f0 f1 s(f,1)
311 * Let A = [0 1 1], and B = [0 1 1]
312 * [1 1 1]
313 * [0 0 1]
314 */
315
316 std::vector<std::vector<int64_t>> sum_fibo_recurrence = {{0, 1, 1},
317 {1, 1, 1},
318 {0, 0, 1}},
319 sum_fibo_base_case = {
320 {0, 1, 1}}; // `f0 = 0, f1 = 1`
321
322 assert(math::linear_recurrence_matrix::get_nth_term_of_recurrence_series(
323 sum_fibo_recurrence, sum_fibo_base_case, 13, 1) == 609LL);
324 assert(math::linear_recurrence_matrix::get_nth_term_of_recurrence_series(
325 sum_fibo_recurrence, sum_fibo_base_case, 16, 1) == 2583LL);
326 /*
327 * Example 6: [Tribonacci sum series](https://oeis.org/A000073)
328 * [0 0 1 1]
329 * [fn-3 fn-2 fn-1 s(f, n-1)] [1 0 1 1]
330 * [0 1 1 1]
331 * [0 0 0 1]
332 *
333 * = [fn-2, fn-1, fn-3 + fn-2 + fn-1, (fn-3 + fn-2 + fn-1 + s(f, n-1))]
334 *
335 * => [fn-2, fn-1, fn, fn + s(f, n-1)]
336 *
337 * => [fn-2, fn-1, fn, s(f, n)]
338 *
339 * Sum of the series is: 0, 0, 1, 2, 4, 8, 15, 28, 52, 96, 177, 326, 600,
340 * 1104, 2031, 3736, 6872, 12640, 23249, 42762
341 *
342 * Let A = [fn-3 fn-2 fn-1 s(f, n-1)], and
343 * [0 0 1 1]
344 * B = [1 0 1 1]
345 * [0 1 1 1]
346 * [0 0 0 1]
347 *
348 * Since, A.B....(n-2 times) = [fn-2 fn-1 fn]
349 * we will have multiply B with itself n-2 times to obtain the required
350 * value
351 */
352
353 std::vector<std::vector<int64_t>> tribonacci_sum = {{0, 0, 1, 1},
354 {1, 0, 1, 1},
355 {0, 1, 1, 1},
356 {0, 0, 0, 1}},
357 trib_sum_base_case = {{0, 0, 1, 1}};
358 // `f0 = 0, f1 = 0, f2 = 1, s = 1`
359
360 assert(math::linear_recurrence_matrix::get_nth_term_of_recurrence_series(
361 tribonacci_sum, trib_sum_base_case, 18, 1) == 23249LL);
362 assert(math::linear_recurrence_matrix::get_nth_term_of_recurrence_series(
363 tribonacci_sum, trib_sum_base_case, 19, 1) == 42762LL);
364}
365
370int main() {
371 test(); // run self-test implementations
372 return 0;
373}
void test()
double k(double x)
Another test function.
int main()
Main function.
Functions for Linear Recurrence Matrix implementation.
for assert
uint64_t power(uint64_t a, uint64_t b, uint64_t c)
This function calculates a raised to exponent b under modulo c using modular exponentiation.