linear_recurrence_matrix_form.sf 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. #!/usr/bin/ruby
  2. # Efficiently compute the n-th term of a given linear recurrence.
  3. # See also:
  4. # http://linear.ups.edu/scla/html/section-recurrence-relations.html
  5. # https://reference.wolfram.com/language/ref/LinearRecurrence.html
  6. # https://the-algorithms.com/algorithm/linear-recurrence-matrix
  7. # Method: from a linear recurrence of the form [5, -10, 10, -5, 1], we build a matrix like:
  8. #
  9. # A = [
  10. # [0, 1, 0, 0, 0],
  11. # [0, 0, 1, 0, 0],
  12. # [0, 0, 0, 1, 0],
  13. # [0, 0, 0, 0, 1],
  14. # [1, -5, 10, -10, 5]
  15. # ]
  16. #
  17. # Let `b` be the column vector of base-cases:
  18. #
  19. # b = [
  20. # [0], # a(0)
  21. # [1], # a(1)
  22. # [9], # a(2)
  23. # [36], # a(3)
  24. # [100] # a(4)
  25. # ]
  26. #
  27. # Then the n-th term a(n) is defined as:
  28. # a(n) = (A^n * b)[0][0]
  29. func linear_recurrence_matrix(ker) {
  30. var A = Matrix.build(ker.len, {|i,j|
  31. (i+1 == j) ? 1 : 0
  32. })
  33. A.set_row(ker.len-1, ker.flip)
  34. return A
  35. }
  36. func linear_recurrence(ker, init, from=0, to=9) {
  37. var A = linear_recurrence_matrix(ker)
  38. var b = Matrix.column_vector(init...)
  39. var c = (A**from * b)
  40. var S = c.transpose.row(0)
  41. var T = ker.flip
  42. from..to -> map {
  43. S << (S ~Z* T -> sum)
  44. S.shift
  45. }
  46. }
  47. say linear_recurrence([5, -10, 10, -5, 1], [0, 1, 9, 36, 100], 0, 10)
  48. say linear_recurrence([5, -10, 10, -5, 1], [0, 1, 16, 81, 256], 0, 10)
  49. assert_eq(linear_recurrence([5, -10, 10, -5, 1], [0, 1, 16, 81, 256], 0, 200).sum, 64802666660)
  50. assert_eq(linear_recurrence([5, -10, 10, -5, 1], [0, 1, 16, 81, 256], 50, 200).sum, 64743249995)
  51. assert_eq(linear_recurrence([-3, 1], [7,2], 0, 10), [7, 2, 1, -1, 4, -13, 43, -142, 469, -1549, 5116])
  52. assert_eq(linear_recurrence([1,1], [1,1], 0, 10), [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89])
  53. assert_eq(linear_recurrence([1,1], [1,1], -6, 6), [5, -3, 2, -1, 1, 0, 1, 1, 2, 3, 5, 8, 13])
  54. assert_eq(linear_recurrence([1,1], [0,1], 0, 10), [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55])
  55. assert_eq(linear_recurrence([0,1,1], [3,0,2], 0, 10), [3, 0, 2, 3, 2, 5, 5, 7, 10, 12, 17])