You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
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.
with $\binom{N}{k_1,k_2,k_3,k_4} = \frac{N!}{k_1!\,k_2!\,k_3!\,k_4!}$, we expand the denominator. Let $\rho = 2xyz + xy + xz + yz$. Then
57
+
58
+
$$
59
+
\Phi = (1+x+y+z) \sum_{N \geq 0} \rho^N
60
+
$$
61
+
62
+
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:
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.
149
+
150
+
---
151
+
152
+
*Originally answered on [Math Stack Exchange][math-se].*
<h2id="perfect-distribution-gcd-in-disguise">Perfect Distribution: GCD in Disguise</h2>
985
-
<p>We discuss an algorithm that distributes $a$ ones among $n$ positions so that the gaps between consecutive ones differ by at most one—a <strong>perfect distribution</strong>. I developed it while working on profiling, stress, and negative testing of a system that needed exactly this kind of uniform spread. I am not aware of prior art; if you know of related work, I would be interested to hear.</p>
984
+
<p>This post extends the generating-function technique from the <ahref="/post/two-var-recursive-func/">two-variable recursion</a> to a three-variable case. I originally wrote this as an answer to a <ahref="https://math.stackexchange.com/questions/1093271/how-to-solve-this-multivariable-recursion/2730331#2730331" target="_blank" rel="noopener">Math Stack Exchange question</a>; here it is adapted for the blog with clearer exposition and code.</p>
<p>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.</p>
986
995
</div>
987
996
</a>
988
997
@@ -1006,7 +1015,7 @@ <h2 id="perfect-distribution-gcd-in-disguise">Perfect Distribution: GCD in Disgu
1006
1015
1007
1016
1008
1017
1009
-
Aug 1, 2017
1018
+
Apr 10, 2018
1010
1019
</span>
1011
1020
1012
1021
@@ -1015,7 +1024,7 @@ <h2 id="perfect-distribution-gcd-in-disguise">Perfect Distribution: GCD in Disgu
1015
1024
1016
1025
<spanclass="middot-divider"></span>
1017
1026
<spanclass="article-reading-time">
1018
-
7 min read
1027
+
4 min read
1019
1028
</span>
1020
1029
1021
1030
@@ -1063,15 +1072,14 @@ <h2 id="perfect-distribution-gcd-in-disguise">Perfect Distribution: GCD in Disgu
<p>In the <ahref="/post/efficient-implementation-non-adjacent-selection/">previous post</a>, we implemented the closed form $F_{n,m} = \binom{n-m+1}{m}$ using Python’s <code>math.factorial</code>, and with <code>scipy</code> and <code>sympy</code>. Here we cover the common competitive-programming case: computing the answer <strong>modulo a large prime</strong> $M$ (e.g. $M = 10^9+7$).</p>
1073
-
<h2id="why-modulo">Why modulo?</h2>
1074
-
<p>In counting problems, the result can be huge even for moderate input. Often the problem asks for the answer modulo a big prime so that it fits in a standard integer type. We could compute the full number and then take the remainder, but that forces expensive long-integer arithmetic. Computing <strong>everything</strong> modulo $M$ from the start is much faster.</p>
1081
+
<h2id="perfect-distribution-gcd-in-disguise">Perfect Distribution: GCD in Disguise</h2>
1082
+
<p>We discuss an algorithm that distributes $a$ ones among $n$ positions so that the gaps between consecutive ones differ by at most one—a <strong>perfect distribution</strong>. I developed it while working on profiling, stress, and negative testing of a system that needed exactly this kind of uniform spread. I am not aware of prior art; if you know of related work, I would be interested to hear.</p>
<p>In the <ahref="/post/two-var-recursive-func/">previous post</a>, we derived the closed form for the non-adjacent selection problem:</p>
1162
-
<p>$$ F_{n, m} = {n - m + 1 \choose m} $$</p>
1163
-
<p>Now we discuss how to implement this efficiently in Python—from a simple factorial-based solution to library implementations. For the common case of computing the answer <strong>modulo a large prime</strong> (e.g. in competitive programming), see the <ahref="/post/binomial-modulo-prime/">next post</a>.</p>
1164
-
<h2id="fast-solutions-based-on-binomials">Fast Solutions Based on Binomials</h2>
1165
-
<p>We can reflect the closed form in very trivial Python code:</p>
1169
+
<p>In the <ahref="/post/efficient-implementation-non-adjacent-selection/">previous post</a>, we implemented the closed form $F_{n,m} = \binom{n-m+1}{m}$ using Python’s <code>math.factorial</code>, and with <code>scipy</code> and <code>sympy</code>. Here we cover the common competitive-programming case: computing the answer <strong>modulo a large prime</strong> $M$ (e.g. $M = 10^9+7$).</p>
1170
+
<h2id="why-modulo">Why modulo?</h2>
1171
+
<p>In counting problems, the result can be huge even for moderate input. Often the problem asks for the answer modulo a big prime so that it fits in a standard integer type. We could compute the full number and then take the remainder, but that forces expensive long-integer arithmetic. Computing <strong>everything</strong> modulo $M$ from the start is much faster.</p>
1166
1172
</div>
1167
1173
</a>
1168
1174
@@ -1186,7 +1192,7 @@ <h2 id="fast-solutions-based-on-binomials">Fast Solutions Based on Binomials</h2
1186
1192
1187
1193
1188
1194
1189
-
Jul 7, 2017
1195
+
Jul 8, 2017
1190
1196
</span>
1191
1197
1192
1198
@@ -1195,7 +1201,7 @@ <h2 id="fast-solutions-based-on-binomials">Fast Solutions Based on Binomials</h2
1195
1201
1196
1202
<spanclass="middot-divider"></span>
1197
1203
<spanclass="article-reading-time">
1198
-
4 min read
1204
+
2 min read
1199
1205
</span>
1200
1206
1201
1207
@@ -1243,19 +1249,17 @@ <h2 id="fast-solutions-based-on-binomials">Fast Solutions Based on Binomials</h2
<p>In this post, we return back to the combinatorial problem discussed in <ahref="/post/intro-to-dp/">Introduction to Dynamic Programming and Memoization</a> post.
1253
-
We will show that generating functions may work great not only for single variable case (see <ahref="/post/gen-func-art/">The Art of Generating Functions</a>),
1254
-
but also could be very useful for hacking two-variable relations (and of course, in general for multivariate case too).</p>
1255
-
<p>For making the post self-contained, we repeat the problem definition here.</p>
1256
-
<h2id="the-problem">The Problem</h2>
1257
-
<blockquote>
1258
-
<p>Compute the number of ways to choose $m$ elements from $n$ elements such that selected elements in one combination are not adjacent.</p>
1258
+
<p>In the <ahref="/post/two-var-recursive-func/">previous post</a>, we derived the closed form for the non-adjacent selection problem:</p>
1259
+
<p>$$ F_{n, m} = {n - m + 1 \choose m} $$</p>
1260
+
<p>Now we discuss how to implement this efficiently in Python—from a simple factorial-based solution to library implementations. For the common case of computing the answer <strong>modulo a large prime</strong> (e.g. in competitive programming), see the <ahref="/post/binomial-modulo-prime/">next post</a>.</p>
1261
+
<h2id="fast-solutions-based-on-binomials">Fast Solutions Based on Binomials</h2>
1262
+
<p>We can reflect the closed form in very trivial Python code:</p>
<p>The notion of generating functions and its application to solving recursive equations are very well-known.
1346
-
For reader who did not have a chance to get familiar with the topics,
1347
-
I recommend to take a look at very good book:
1348
-
<ahref="https://en.wikipedia.org/wiki/Concrete_Mathematics" target="_blank" rel="noopener">Concrete Mathematics: A Foundation for Computer Science, by Ronald L. Graham, Donald E. Knuth, Oren Patashnik</a>.</p>
1349
-
<p>Generating functions are usually applied to single variable recursive equations.
1350
-
But actually, the technique may be extended to multivariate recursive equations, or even to a system of recursive equations.
1351
-
Readers who are familiar with one-variable case, may jump directly to the next post:
1352
-
<ahref="/post/two-var-recursive-func/">Cracking Multivariate Recursive Equations Using Generating Functions</a>.</p>
1349
+
<p>In this post, we return back to the combinatorial problem discussed in <ahref="/post/intro-to-dp/">Introduction to Dynamic Programming and Memoization</a> post.
1350
+
We will show that generating functions may work great not only for single variable case (see <ahref="/post/gen-func-art/">The Art of Generating Functions</a>),
1351
+
but also could be very useful for hacking two-variable relations (and of course, in general for multivariate case too).</p>
1352
+
<p>For making the post self-contained, we repeat the problem definition here.</p>
1353
+
<h2id="the-problem">The Problem</h2>
1354
+
<blockquote>
1355
+
<p>Compute the number of ways to choose $m$ elements from $n$ elements such that selected elements in one combination are not adjacent.</p>
0 commit comments