图片解析应用
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

166 lines
5.0 KiB

  1. """
  2. Recurrences
  3. """
  4. from sympy.core import S, sympify
  5. from sympy.utilities.iterables import iterable
  6. from sympy.utilities.misc import as_int
  7. def linrec(coeffs, init, n):
  8. r"""
  9. Evaluation of univariate linear recurrences of homogeneous type
  10. having coefficients independent of the recurrence variable.
  11. Parameters
  12. ==========
  13. coeffs : iterable
  14. Coefficients of the recurrence
  15. init : iterable
  16. Initial values of the recurrence
  17. n : Integer
  18. Point of evaluation for the recurrence
  19. Notes
  20. =====
  21. Let `y(n)` be the recurrence of given type, ``c`` be the sequence
  22. of coefficients, ``b`` be the sequence of initial/base values of the
  23. recurrence and ``k`` (equal to ``len(c)``) be the order of recurrence.
  24. Then,
  25. .. math :: y(n) = \begin{cases} b_n & 0 \le n < k \\
  26. c_0 y(n-1) + c_1 y(n-2) + \cdots + c_{k-1} y(n-k) & n \ge k
  27. \end{cases}
  28. Let `x_0, x_1, \ldots, x_n` be a sequence and consider the transformation
  29. that maps each polynomial `f(x)` to `T(f(x))` where each power `x^i` is
  30. replaced by the corresponding value `x_i`. The sequence is then a solution
  31. of the recurrence if and only if `T(x^i p(x)) = 0` for each `i \ge 0` where
  32. `p(x) = x^k - c_0 x^(k-1) - \cdots - c_{k-1}` is the characteristic
  33. polynomial.
  34. Then `T(f(x)p(x)) = 0` for each polynomial `f(x)` (as it is a linear
  35. combination of powers `x^i`). Now, if `x^n` is congruent to
  36. `g(x) = a_0 x^0 + a_1 x^1 + \cdots + a_{k-1} x^{k-1}` modulo `p(x)`, then
  37. `T(x^n) = x_n` is equal to
  38. `T(g(x)) = a_0 x_0 + a_1 x_1 + \cdots + a_{k-1} x_{k-1}`.
  39. Computation of `x^n`,
  40. given `x^k = c_0 x^{k-1} + c_1 x^{k-2} + \cdots + c_{k-1}`
  41. is performed using exponentiation by squaring (refer to [1_]) with
  42. an additional reduction step performed to retain only first `k` powers
  43. of `x` in the representation of `x^n`.
  44. Examples
  45. ========
  46. >>> from sympy.discrete.recurrences import linrec
  47. >>> from sympy.abc import x, y, z
  48. >>> linrec(coeffs=[1, 1], init=[0, 1], n=10)
  49. 55
  50. >>> linrec(coeffs=[1, 1], init=[x, y], n=10)
  51. 34*x + 55*y
  52. >>> linrec(coeffs=[x, y], init=[0, 1], n=5)
  53. x**2*y + x*(x**3 + 2*x*y) + y**2
  54. >>> linrec(coeffs=[1, 2, 3, 0, 0, 4], init=[x, y, z], n=16)
  55. 13576*x + 5676*y + 2356*z
  56. References
  57. ==========
  58. .. [1] https://en.wikipedia.org/wiki/Exponentiation_by_squaring
  59. .. [2] https://en.wikipedia.org/w/index.php?title=Modular_exponentiation&section=6#Matrices
  60. See Also
  61. ========
  62. sympy.polys.agca.extensions.ExtensionElement.__pow__
  63. """
  64. if not coeffs:
  65. return S.Zero
  66. if not iterable(coeffs):
  67. raise TypeError("Expected a sequence of coefficients for"
  68. " the recurrence")
  69. if not iterable(init):
  70. raise TypeError("Expected a sequence of values for the initialization"
  71. " of the recurrence")
  72. n = as_int(n)
  73. if n < 0:
  74. raise ValueError("Point of evaluation of recurrence must be a "
  75. "non-negative integer")
  76. c = [sympify(arg) for arg in coeffs]
  77. b = [sympify(arg) for arg in init]
  78. k = len(c)
  79. if len(b) > k:
  80. raise TypeError("Count of initial values should not exceed the "
  81. "order of the recurrence")
  82. else:
  83. b += [S.Zero]*(k - len(b)) # remaining initial values default to zero
  84. if n < k:
  85. return b[n]
  86. terms = [u*v for u, v in zip(linrec_coeffs(c, n), b)]
  87. return sum(terms[:-1], terms[-1])
  88. def linrec_coeffs(c, n):
  89. r"""
  90. Compute the coefficients of n'th term in linear recursion
  91. sequence defined by c.
  92. `x^k = c_0 x^{k-1} + c_1 x^{k-2} + \cdots + c_{k-1}`.
  93. It computes the coefficients by using binary exponentiation.
  94. This function is used by `linrec` and `_eval_pow_by_cayley`.
  95. Parameters
  96. ==========
  97. c = coefficients of the divisor polynomial
  98. n = exponent of x, so dividend is x^n
  99. """
  100. k = len(c)
  101. def _square_and_reduce(u, offset):
  102. # squares `(u_0 + u_1 x + u_2 x^2 + \cdots + u_{k-1} x^k)` (and
  103. # multiplies by `x` if offset is 1) and reduces the above result of
  104. # length upto `2k` to `k` using the characteristic equation of the
  105. # recurrence given by, `x^k = c_0 x^{k-1} + c_1 x^{k-2} + \cdots + c_{k-1}`
  106. w = [S.Zero]*(2*len(u) - 1 + offset)
  107. for i, p in enumerate(u):
  108. for j, q in enumerate(u):
  109. w[offset + i + j] += p*q
  110. for j in range(len(w) - 1, k - 1, -1):
  111. for i in range(k):
  112. w[j - i - 1] += w[j]*c[i]
  113. return w[:k]
  114. def _final_coeffs(n):
  115. # computes the final coefficient list - `cf` corresponding to the
  116. # point at which recurrence is to be evalauted - `n`, such that,
  117. # `y(n) = cf_0 y(k-1) + cf_1 y(k-2) + \cdots + cf_{k-1} y(0)`
  118. if n < k:
  119. return [S.Zero]*n + [S.One] + [S.Zero]*(k - n - 1)
  120. else:
  121. return _square_and_reduce(_final_coeffs(n // 2), n % 2)
  122. return _final_coeffs(n)