add more details and methods
This commit is contained in:
parent
e086b07cfb
commit
2c1613c9db
|
@ -10,12 +10,12 @@
|
|||
"There's a *very* cool way to solve this problem using [generating functions](https://en.wikipedia.org/wiki/Generating_function).\n",
|
||||
"\n",
|
||||
"## Generating functions\n",
|
||||
"If you're not familiar, a generating function is a way of expressing a sequence as coefficients of a power series. For example, the generating function for the Fibonacci sequence is\n",
|
||||
"If you're not familiar, a generating function is a way of expressing a sequence as coefficients of a [formal power series](https://en.wikipedia.org/wiki/Formal_power_series). For example, the generating function for the Fibonacci sequence is\n",
|
||||
"$$0x^0 + 1x^1 + 1x^2 + 2x^3 + 3x^4 + 5x^5 + \\cdots$$\n",
|
||||
"\n",
|
||||
"The seemingly trivial series $(1, 1, 1, 1, \\ldots)$ has an equally simple generating function\n",
|
||||
"$$1 + x + x^2 + x^3 + x^4 + \\cdots$$\n",
|
||||
"which converges to $\\frac{1}{1-x}$ for $-1 < x < 1$ (note that we don't really care about the interval of convergence, because we're not going to be evaluating this function directly).\n",
|
||||
"which converges to $\\frac{1}{1-x}$ for $-1 < x < 1$. (Note that we don't really care about the interval of convergence, because we're not going to be evaluating this function directly. Here, $x$ is an [indeterminate variable](https://en.wikipedia.org/wiki/Indeterminate_(variable)).)\n",
|
||||
"\n",
|
||||
"If this is your first time seeing generating functions, it's probably not obvious how they can help us solve this problem. Let's look at some related, simpler problems to lead us towards understanding.\n",
|
||||
"\n",
|
||||
|
@ -89,18 +89,67 @@
|
|||
"* $x \\times x^6 = x^7$: There's **one way** to make change for **1 cent** with only 1 cent coins, and **one way** to make change for **6 cents** with 2 and 5 cent coins. Therefore, there's **one way** to make change for **7 cents** using 1, 2, and 5 cent coins.\n",
|
||||
"* $x^3 \\times x^4 = x^7$: There's **one way** to make change for **3 cents** with only 1 cent coins, and **one way** to make change for **4 cents** with 2 and 5 cent coins. Therefore, there's **one way** to make change for **7 cents** using 1, 2, and 5 cent coins.\n",
|
||||
"\n",
|
||||
"Notice that each of these select terms represent a different way to make change for 7 cents. Therefore, together they represent 3 ways to make change for 7 cents with 1, 2, and 5 cent coins. There are 3 more products that result in $x^7$ ($x^2 \\times x^5$, $x^5 \\times x^2$, and $x^7 \\times 1$), and when we are done distributing, we combine like terms, which results in $6x^7$. Once again, the exponent represents the amount we are making change for, and the coefficient represents the number of ways to make change for that amount with the denominations we're using.\n",
|
||||
"Notice that each of these select terms represent a different way to make change for 7 cents. Therefore, together they represent 3 ways to make change for 7 cents with 1, 2, and 5 cent coins. There are 3 more products that result in $x^7$ ($x^2 \\times x^5$, $x^5 \\times x^2$, and $x^7 \\times 1$), and when we are done distributing, we combine like terms, which results in $6x^7$. As a reminder, the exponent represents the amount we are making change for, and the coefficient represents the number of ways to make change for that amount with the denominations we're using.\n",
|
||||
"\n",
|
||||
"Note that the polynomial multiplication gives us not just the number ways to make change for 7 cents, but for *all* cent totals from 0 to 7. The higher degree terms are inaccurate since we cut them off in the original series for our computation, but if we didn't do this, and instead worked with \n",
|
||||
"$$(1 + x + x^2 + x^3 + \\cdots)(1 + x^2 + x^4 + x^6 + \\cdots)(1 + x^5 + x^{10} + x^{15} + \\cdots)$$\n",
|
||||
"we would have a generating function for the number of ways to make change for *any* amount using 1, 2, and 5 cent coins. Since these series also have closed forms, we can express the generating function more succinctly as\n",
|
||||
"$$G(x) = \\frac{1}{(1-x)(1-x^2)(1-x^5)}$$\n",
|
||||
"\n",
|
||||
"## The original problem\n",
|
||||
"Just like the simpler problems above, each $n$th term in this generating function gives the number of ways to make change for $n$ pence using 1, 2, 5, 10, 20, 50, 100, and 200 pence coins.\n",
|
||||
"$$G(x) = \\frac{1}{(1-x)(1-x^2)(1-x^5)(1-x^{10})(1-x^{20})(1-x^{50})(1-x^{100})(1-x^{200})}$$\n",
|
||||
"Therefore, to solve this problem, we just need to find $[x^{200}]G(x)$.\n",
|
||||
"\n",
|
||||
"We could proceed like before and cut off each generating function after the $x^{200}$ term, then multiply and find $[x^{200}]G(x)$. However, another possibility is to use the function as written, and instead calculate its 200th [Taylor polynomial](https://en.wikipedia.org/wiki/Taylor_series)."
|
||||
"If we weren't using SageMath, we'd probably want to do something like what we did above, where we cut off each infinite series after the $x^{200}$ term, then multiply those polynomials and find the $x^{200}$ term of the product. Polynomial multiplication is implemented in libraries like [NumPy](https://numpy.org/doc/stable/reference/generated/numpy.polynomial.polynomial.polymul.html), or you can implement it yourself - either by naively distributing like above, or more efficiently with [convolutions](https://en.wikipedia.org/wiki/Convolution), which in turn are implemented using a [Fast Fourier transform](https://en.wikipedia.org/wiki/Fast_Fourier_transform)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "c351c248",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"73682.0"
|
||||
]
|
||||
},
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from numpy.polynomial import Polynomial\n",
|
||||
"\n",
|
||||
"degree = 200\n",
|
||||
"\n",
|
||||
"ones = Polynomial([1 for _ in range(0, degree + 1)])\n",
|
||||
"twos = ones(Polynomial.basis(2)).cutdeg(degree)\n",
|
||||
"fives = ones(Polynomial.basis(5)).cutdeg(degree)\n",
|
||||
"tens = ones(Polynomial.basis(10)).cutdeg(degree)\n",
|
||||
"twenties = ones(Polynomial.basis(20)).cutdeg(degree)\n",
|
||||
"fifties = ones(Polynomial.basis(50)).cutdeg(degree)\n",
|
||||
"hundreds = ones(Polynomial.basis(100)).cutdeg(degree)\n",
|
||||
"twohundreds = ones(Polynomial.basis(200)).cutdeg(degree)\n",
|
||||
"\n",
|
||||
"G = ones * twos * fives * tens * twenties * fifties * hundreds * twohundreds\n",
|
||||
"G.coef[degree]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "60169571",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"With SageMath, another possibility is to use the function as written, and instead calculate its 200th [Taylor polynomial](https://en.wikipedia.org/wiki/Taylor_series)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "33bbc1a4",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
|
@ -110,14 +159,45 @@
|
|||
"73682"
|
||||
]
|
||||
},
|
||||
"execution_count": 2,
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"f(x) = 1/prod(1 - x^k for k in (1, 2, 5, 10, 20, 50, 100, 200))\n",
|
||||
"f(x).taylor(x, 0, 200).coefficient(x^200)"
|
||||
"G(x) = 1/prod(1 - x^k for k in (1, 2, 5, 10, 20, 50, 100, 200))\n",
|
||||
"G(x).taylor(x, 0, 200).coefficient(x^200)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "4584583b",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Yet another possibility is to construct the generating function as a member of the [power series ring](https://en.wikipedia.org/wiki/Formal_power_series) over the integers. This is the most abstract method, but is faster than the Taylor series method."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "c2a5db7e",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"73682"
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"R.<x> = PowerSeriesRing(ZZ, default_prec=201)\n",
|
||||
"G = 1 / prod(1 - x^k for k in (1, 2, 5, 10, 20, 50, 100, 200))\n",
|
||||
"G[200]"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue