|
| 1 | ++++ |
| 2 | +date = "2018-04-10T03:14:00Z" |
| 3 | +math = true |
| 4 | +highlight = true |
| 5 | +tags = ["math", "python", "generating function", "recursion", "combinatorics", "dynamic programming", "multivariate function"] |
| 6 | +title = "Solving a Three-Variable Recursion via Generating Functions" |
| 7 | +draft = false |
| 8 | + |
| 9 | +[header] |
| 10 | +image = "" |
| 11 | +caption = "" |
| 12 | ++++ |
| 13 | + |
| 14 | +This post extends the generating-function technique from the [two-variable recursion][two-var-recursive-func] to a three-variable case. I originally wrote this as an answer to a [Math Stack Exchange question][math-se]; here it is adapted for the blog with clearer exposition and code. |
| 15 | + |
| 16 | +## The Problem |
| 17 | + |
| 18 | +We want to solve the recurrence |
| 19 | +$$ |
| 20 | +\begin{aligned} |
| 21 | +$$a(m,n,k) &= 2a(m-1,n-1,k-1) + a(m-1,n-1,k) \\ |
| 22 | +&+ a(m-1,n,k-1) + a(m,n-1,k-1)$$ |
| 23 | +\end{aligned} |
| 24 | +$$ |
| 25 | +where $m$, $n$, $k$ are nonnegative integers, with boundary conditions: |
| 26 | +
|
| 27 | +- $a(0,0,0) = a(1,0,0) = a(0,1,0) = a(0,0,1) = 1$ |
| 28 | +- $a(m,0,0) = a(0,m,0) = a(0,0,m) = 0$ for any $m > 1$ |
| 29 | +- $a(m,n,k)$ is symmetric in $m$,$n$,$k$ |
| 30 | +
|
| 31 | +A subtlety: $a(0,1,1)$ is not defined by the recurrence alone, since it would require values like $a(-1,0,0)$. We take $a(m,n,k) = 0$ whenever any argument is negative. |
| 32 | +
|
| 33 | +## The Generating Function |
| 34 | +
|
| 35 | +Define |
| 36 | +
|
| 37 | +$$\Phi(x,y,z) = \sum_{m,n,k \geq 0} a(m,n,k) \cdot x^m y^n z^k$$ |
| 38 | + |
| 39 | +Using the initial values above, we can define $a(m,n,k)$ as follows: |
| 40 | + |
| 41 | +$$ |
| 42 | +\begin{aligned} |
| 43 | +a(m, n, k) &= 2a(m-1, n-1, k-1) + a(m-1, n-1, k) \\ |
| 44 | +&+ a(m-1, n, k-1) + a(m, n-1, k-1) \\ |
| 45 | +&+ [m=n=k=0] + [m=n=0 \wedge k=1] + [m=k=0 \wedge n=1] + [n=k=0 \wedge m=1] |
| 46 | +\end{aligned} |
| 47 | +$$ |
| 48 | + |
| 49 | + believe there are still some initial conditions missing, since for example $a(0,1,1)$ is not well defined. Computing its value will result in negative arguments: $a(0,1,1) = 2a(-1, 0, 0) + a(-1, 0, 1) + a(-1, 1, 0) + a(0, 0, 0) = 2a(-1, 0, 0) + 2a(-1, 0, 1) + 1$. |
| 50 | + |
| 51 | +Adding the extra condition that $a(m,n,k)=0$ for any negative argument(s) solves the issue. |
| 52 | + |
| 53 | +Let's move forward and substitute the definition of $a(m,n,k)$ into the generating function: |
| 54 | + |
| 55 | +$$ |
| 56 | +\begin{aligned} |
| 57 | +\Phi(x,y,z) |
| 58 | +&=\sum_{m,n,k}a(m,n,k) \cdot x^m y^n z^k \\ |
| 59 | +&= 2\sum_{m,n,k}a(m,n,k) \cdot x^{m+1} y^{n+1} z^{k+1} + \sum_{m,n,k}a(m,n,k) \cdot x^{m+1} y^{n+1} z^k + \sum_{m,n,k}a(m,n,k) \cdot x^{m+1} y^n z^{k+1}+\sum_{m,n,k}a(m,n,k) \cdot x^m y^{n+1} z^{k+1} + 1 + x + y + z \\ |
| 60 | +&= 2\Phi(x,y,z) \cdot x y z + \Phi(x,y,z)\cdot x y + \Phi(x,y,z) \cdot x z + \Phi(x,y,z) \cdot y z + 1 + x + y + z \\ |
| 61 | +&= \Phi(x,y,z)\left(2 x y z + x y + x z + y z\right) + 1 + x + y + z |
| 62 | +\end{aligned} |
| 63 | +$$ |
| 64 | + |
| 65 | + |
| 66 | +Substituting the recurrence and collecting terms, we get |
| 67 | + |
| 68 | +$$ |
| 69 | +\begin{aligned} |
| 70 | +\Phi(x,y,z) |
| 71 | +&= \sum_{m,n,k} a(m,n,k) \cdot x^m y^n z^k \\ |
| 72 | +&= 2\Phi \cdot xyz + \Phi \cdot xy + \Phi \cdot xz + \Phi \cdot yz + 1 + x + y + z |
| 73 | +\end{aligned} |
| 74 | +$$ |
| 75 | + |
| 76 | +where the boundary terms $1 + x + y + z$ come from $a(0,0,0)$, $a(1,0,0)$, $a(0,1,0)$, $a(0,0,1)$. Solving for $\Phi$: |
| 77 | + |
| 78 | +$$\Phi(x,y,z) = \frac{1 + x + y + z}{1 - 2xyz - xy - xz - yz}$$ |
| 79 | + |
| 80 | +## From Generating Function to Closed Form |
| 81 | + |
| 82 | +Using $\frac{1}{1-\rho} = \sum_{i \geq 0} \rho^i$ and the multinomial expansion |
| 83 | + |
| 84 | +$$(x_1+x_2+x_3+x_4)^N = \sum_{k_1+k_2+k_3+k_4=N} \binom{N}{k_1,k_2,k_3,k_4} x_1^{k_1} x_2^{k_2} x_3^{k_3} x_4^{k_4}$$ |
| 85 | + |
| 86 | +with $\binom{N}{k_1,k_2,k_3,k_4} = \frac{N!}{k_1!\cdot k_2!\cdot k_3!\cdot k_4!}$, we expand the denominator. Let $\rho = 2xyz + xy + xz + yz$. Then |
| 87 | + |
| 88 | +$$ |
| 89 | +\Phi = (1+x+y+z) \sum_{N \geq 0} \rho^N |
| 90 | +$$ |
| 91 | + |
| 92 | + |
| 93 | +$$ |
| 94 | +\begin{aligned} |
| 95 | +&= \frac{1 + x + y + z}{1-2 x y z - x y - x z - y z} \\ |
| 96 | +&= (1 + x + y + z) \sum_{N}(2 x y z + x y + x z + y z)^N \\ |
| 97 | +&= (1 + x + y + z) \sum_{k_1+k_2+k_3+k_4=N} \binom{N} {k_1,k_2,k_3,k_4} (2 x y z)^{k_1} \cdot (x y)^{k_2} \cdot (x z)^{k_3} \cdot (y z)^{k_4} \\ |
| 98 | +&= (1 + x + y + z) \sum_{k_1+k_2+k_3+k_4=N} \binom{N} {k_1,k_2,k_3,k_4} 2^{k_1} x^{k_1+k_2+k_3} y^{k_1+k_2+k_4} z^{k_1+k_3+k_4} \\ |
| 99 | +&= (1 + x + y + z) \sum_{k_1+k_2+k_3\leq N} \binom{N} {k_1,k_2,k_3,N-k_1-k_2-k_3} 2^{k_1} x^{k_1+k_2+k_3} y^{N-k_3} z^{N-k_2} |
| 100 | +\end{aligned} |
| 101 | +$$ |
| 102 | + |
| 103 | + |
| 104 | +Writing $\rho^N$ with terms $(2xyz)^{k_1}(xy)^{k_2}(xz)^{k_3}(yz)^{k_4}$ where $k_1+k_2+k_3+k_4 = N$, and extracting the coefficient of $x^m y^n z^k$, yields the closed form: |
| 105 | + |
| 106 | +$$ |
| 107 | +\begin{aligned} |
| 108 | +a(m,n,k) |
| 109 | +&= \sum_{\max(m,n,k) \leq N \leq \frac{m+n+k}{2}} |
| 110 | + \binom{N}{m+n+k-2N, N-m, N-n, N-k} 2^{m+n+k-2N} \\ |
| 111 | +&\quad + \text{three similar sums from the } 1, x, y, z \text{ terms} |
| 112 | +\end{aligned} |
| 113 | +$$ |
| 114 | + |
| 115 | +The full expression has four sums corresponding to the four terms in the numerator $1+x+y+z$. The exact formulas are: |
| 116 | + |
| 117 | +$$ |
| 118 | +\begin{aligned} |
| 119 | +a(m,n,k) |
| 120 | +&= \sum_{N=\max(m,n,k)}^{\lfloor (m+n+k)/2 \rfloor} |
| 121 | + \binom{N}{m+n+k-2N, N-m, N-n, N-k} 2^{m+n+k-2N} \\ |
| 122 | +&\quad + \sum_{N=\max(m-1,n,k)}^{\lfloor (m+n+k-1)/2 \rfloor} |
| 123 | + \binom{N}{m+n+k-2N-1, N-m+1, N-n, N-k} 2^{m+n+k-2N-1} \\ |
| 124 | +&\quad + \sum_{N=\max(m,n-1,k)}^{\lfloor (m+n+k-1)/2 \rfloor} |
| 125 | + \binom{N}{m+n+k-2N-1, N-m, N-n+1, N-k} 2^{m+n+k-2N-1} \\ |
| 126 | +&\quad + \sum_{N=\max(m,n,k-1)}^{\lfloor (m+n+k-1)/2 \rfloor} |
| 127 | + \binom{N}{m+n+k-2N-1, N-m, N-n, N-k+1} 2^{m+n+k-2N-1} |
| 128 | +\end{aligned} |
| 129 | +$$ |
| 130 | + |
| 131 | +There may be room to simplify this further; the symmetry in $m,n,k$ could help. |
| 132 | + |
| 133 | +## Complexity |
| 134 | + |
| 135 | +- **Recursion with memoization (DP):** $\Theta(m \cdot n \cdot k)$ time and space. |
| 136 | +- **Closed form:** Precompute factorials, then loop over $N$; time and space $\Theta(m+n+k)$, ignoring the cost of arithmetic on large integers. |
| 137 | + |
| 138 | +## Implementation |
| 139 | + |
| 140 | +Both the recursive and closed-form versions in Python: |
| 141 | + |
| 142 | +```python |
| 143 | +import functools |
| 144 | +import math |
| 145 | + |
| 146 | +@functools.lru_cache(maxsize=None) |
| 147 | +def a_rec(m: int, n: int, k: int) -> int: |
| 148 | + if min(m, n, k) < 0: |
| 149 | + return 0 |
| 150 | + if m + n + k == 0 or m + n + k == 1: |
| 151 | + return 1 |
| 152 | + if m + n == 0 or m + k == 0 or n + k == 0: |
| 153 | + return 0 |
| 154 | + return ( |
| 155 | + 2 * a_rec(m - 1, n - 1, k - 1) |
| 156 | + + a_rec(m - 1, n - 1, k) |
| 157 | + + a_rec(m - 1, n, k - 1) |
| 158 | + + a_rec(m, n - 1, k - 1) |
| 159 | + ) |
| 160 | + |
| 161 | +@functools.lru_cache(maxsize=None) |
| 162 | +def _binom4(N: int, a: int, b: int, c: int) -> int: |
| 163 | + r = N - a - b - c |
| 164 | + vals = sorted([a, b, c, r]) |
| 165 | + assert vals[0] >= 0 |
| 166 | + return math.factorial(N) // ( |
| 167 | + math.factorial(vals[0]) * math.factorial(vals[1]) |
| 168 | + * math.factorial(vals[2]) * math.factorial(vals[3]) |
| 169 | + ) |
| 170 | + |
| 171 | +def a_closed(m: int, n: int, k: int) -> int: |
| 172 | + if min(m, n, k) < 0: |
| 173 | + return 0 |
| 174 | + s = 0 |
| 175 | + for N in range(max(m, n, k), (m + n + k) // 2 + 1): |
| 176 | + s += _binom4(N, N - m, N - n, N - k) * 2 ** (m + n + k - 2 * N) |
| 177 | + for N in range(max(m - 1, n, k), (m + n + k - 1) // 2 + 1): |
| 178 | + s += _binom4(N, N - m + 1, N - n, N - k) * 2 ** (m + n + k - 2 * N - 1) |
| 179 | + for N in range(max(m, n - 1, k), (m + n + k - 1) // 2 + 1): |
| 180 | + s += _binom4(N, N - m, N - n + 1, N - k) * 2 ** (m + n + k - 2 * N - 1) |
| 181 | + for N in range(max(m, n, k - 1), (m + n + k - 1) // 2 + 1): |
| 182 | + s += _binom4(N, N - m, N - n, N - k + 1) * 2 ** (m + n + k - 2 * N - 1) |
| 183 | + return s |
| 184 | + |
| 185 | +# Sanity check |
| 186 | +r, r1 = a_rec(100, 200, 210), a_closed(100, 200, 210) |
| 187 | +print(f"Recursive: {r}, Closed: {r1}, Match: {r == r1}") |
| 188 | +``` |
| 189 | + |
| 190 | +We did not exploit the symmetry $a(m,n,k) = a(\sigma(m,n,k))$ for permutations $\sigma$; it could speed up computation but does not obviously simplify the closed expression. |
| 191 | + |
| 192 | +--- |
| 193 | + |
| 194 | +*Originally answered on [Math Stack Exchange][math-se].* |
| 195 | + |
| 196 | +[two-var-recursive-func]: /post/two-var-recursive-func/ |
| 197 | +[math-se]: https://math.stackexchange.com/questions/1093271/how-to-solve-this-multivariable-recursion/2730331#2730331 |
0 commit comments