simplify dirichlet convolution approach
This commit is contained in:
@@ -130,137 +130,51 @@
|
|||||||
"The [Dirichlet convolution](https://en.wikipedia.org/wiki/Dirichlet_convolution) of two [arithmetic functions](https://en.wikipedia.org/wiki/Arithmetic_function) $f$ and $g$ is\n",
|
"The [Dirichlet convolution](https://en.wikipedia.org/wiki/Dirichlet_convolution) of two [arithmetic functions](https://en.wikipedia.org/wiki/Arithmetic_function) $f$ and $g$ is\n",
|
||||||
"$$(f * g)(n) = \\sum_{xy=n} f(x) g(y)$$\n",
|
"$$(f * g)(n) = \\sum_{xy=n} f(x) g(y)$$\n",
|
||||||
"\n",
|
"\n",
|
||||||
"Several important mathematical functions have some representation as a Dirichlet convolution - see the above page for a list. An important one for our purposes is $\\phi = \\mathrm{Id} * \\mu$, where $\\mathrm{Id(n) = n}$ is the [identity function](https://en.wikipedia.org/wiki/Identity_function) and $\\mu$ is the [Möbius function](https://en.wikipedia.org/wiki/M%C3%B6bius_function) - if you've never heard of the latter, don't worry about it yet.\n",
|
"Several important mathematical functions have some representation as a Dirichlet convolution - see the above page for a list. An important one for our purposes is $\\phi * 1 = \\mathrm{Id}$, where $1$ is just the constant function $1(n) = 1$ and $\\mathrm{Id}(n) = n$ is the [identity function](https://en.wikipedia.org/wiki/Identity_function) (note that this convolution identity is the same as Gauss's identity presented above).\n",
|
||||||
"\n",
|
"\n",
|
||||||
"## Dirichlet's hyperbola method\n",
|
"## Dirichlet's hyperbola method\n",
|
||||||
"By applying the above Dirichlet convolution to\n",
|
"As mentioned above, it's well-known that\n",
|
||||||
"$$\\Phi(n) = \\sum_{k=1}^n \\phi(n)$$\n",
|
"$$\\sum_{k=1}^n k = \\frac{n(n+1)}{2}$$\n",
|
||||||
"we can derive an equivalent summation:\n",
|
"We can go further by applying the Dirichlet convolution above to obtain\n",
|
||||||
"$$\\Phi(n) = \\sum_{k=1}^n \\sum_{xy=k} x \\mu(y)$$\n",
|
"$$\\frac{n(n+1)}{2} = \\sum_{k=1}^n k = \\sum_{k=1}^n \\sum_{xy=k} \\phi(x)$$\n",
|
||||||
"\n",
|
"\n",
|
||||||
"We're going to transform this summation by applying \n",
|
"We're going to transform this summation by applying \n",
|
||||||
"[Dirichlet's hyperbola method](https://en.wikipedia.org/wiki/Dirichlet_hyperbola_method). Let $1 < a < n$, and let $b = n / a$. Then\n",
|
"[Dirichlet's hyperbola method](https://en.wikipedia.org/wiki/Dirichlet_hyperbola_method). Let $1 < a < n$, and let $b = n / a$. Then\n",
|
||||||
"$$\\Phi(n) = \\sum_{x=1}^a \\sum_{y=1}^{n/x} x \\mu(y) + \\sum_{y=1}^b \\sum_{x=1}^{n/y} x \\mu(y) - \\sum_{x=1}^a \\sum_{y=1}^b x \\mu(y)$$\n",
|
"$$\\frac{n(n+1)}{2} = \\sum_{x=1}^a \\sum_{y=1}^{n/x} \\phi(x) + \\sum_{y=1}^b \\sum_{x=1}^{n/y} \\phi(x) - \\sum_{x=1}^a \\sum_{y=1}^b \\phi(x)$$\n",
|
||||||
"\n",
|
"\n",
|
||||||
"Then with some algebra, we can transform this into\n",
|
"Then with some algebra, we can transform this into\n",
|
||||||
"$$\\Phi(n) = \\sum_{x=1}^a x M\\left(\\left\\lfloor \\frac{n}{x}\\right\\rfloor\\right) + \\sum_{y=1}^b \\mu(y) \\frac{\\left\\lfloor \\frac{n}{y} \\right\\rfloor \\left(\\left\\lfloor \\frac{n}{y}\\right\\rfloor + 1\\right)}{2} - M(\\lfloor b \\rfloor) \\frac{\\lfloor a \\rfloor(\\lfloor a\\rfloor +1)}{2}$$\n",
|
"$$\\frac{n(n+1)}{2} = \\sum_{x=1}^a \\phi(x)\\left\\lfloor\\frac{n}{x}\\right\\rfloor + \\sum_{y=1}^b \\Phi\\left(\\left\\lfloor\\frac{n}{y}\\right\\rfloor\\right) - \\lfloor b \\rfloor\\Phi(\\lfloor a \\rfloor)$$\n",
|
||||||
"where $M(n)$ is the [Mertens function](https://en.wikipedia.org/wiki/Mertens_function), which is just the partial sums of the Möbius function:\n",
|
|
||||||
"$$M(n) = \\sum_{k=1}^n \\mu(k)$$\n",
|
|
||||||
"\n",
|
"\n",
|
||||||
"By using this formula for $\\Phi(n)$, instead of calculating $n$ different totients, our sums only run to $a$ and $b$.\n",
|
"This might seem complicated, but note that in the second summation on the right, when $y=1$, we have $\\Phi(n)$. We can extract that term and rearrange to get a recursive formula for $\\Phi(n)$:\n",
|
||||||
|
"$$\\Phi(n) = \\frac{n(n+1)}{2} + \\lfloor b \\rfloor\\Phi(\\lfloor a \\rfloor) - \\sum_{x=1}^a \\phi(x)\\left\\lfloor\\frac{n}{x}\\right\\rfloor - \\sum_{y=2}^b \\Phi\\left(\\left\\lfloor\\frac{n}{y}\\right\\rfloor\\right)$$\n",
|
||||||
"\n",
|
"\n",
|
||||||
"However, for this formula to be effective, we also need an efficient way to calculate $M(n)$. Fortunately, we can just apply the hyperbola method again, with the convolution $\\epsilon = \\mu * 1$, where $\\epsilon$ is the [unit identity](https://en.wikipedia.org/wiki/Unit_function). Once again, we choose $1 < a < n$ and let $b = n/a$.\n",
|
"The major advantage of this formula is that we only need the values of the totient function up to $a$, instead of up to $n$. We can set $a = \\sqrt{1000000} = 1000$.\n",
|
||||||
"\n",
|
"\n",
|
||||||
"$$\\sum_{k=1}^n \\epsilon(k) = \\sum_{k=1}^n \\sum_{xy=k} \\mu(x)$$\n",
|
"This all leads to this code, yet another implementation of $\\Phi(n)$:"
|
||||||
"\n",
|
|
||||||
"$$1 = \\sum_{x=1}^a \\sum_{y=1}^{n/x} \\mu(x) + \\sum_{y=1}^b \\sum_{x=1}^{n/y} \\mu(x) - \\sum_{x=1}^a \\sum_{y=1}^b \\mu(x)$$\n",
|
|
||||||
"\n",
|
|
||||||
"$$1 = \\sum_{x=1}^a \\mu(x)\\left\\lfloor \\frac{n}{x}\\right\\rfloor + \\sum_{y=1}^b M\\left(\\left\\lfloor \\frac{n}{y}\\right\\rfloor\\right) - \\lfloor b \\rfloor M(\\lfloor a\\rfloor)$$\n",
|
|
||||||
"\n",
|
|
||||||
"$$M(n) = 1 + \\lfloor b\\rfloor M(\\lfloor a\\rfloor) - \\sum_{x=1}^a \\mu(x)\\left\\lfloor \\frac{n}{x}\\right\\rfloor - \\sum_{y=2}^b M\\left(\\left\\lfloor \\frac{n}{y}\\right\\rfloor\\right)$$\n",
|
|
||||||
"\n",
|
|
||||||
"Now we have a recursive implementation of $M(n)$. All that's left is to calculate the values of $\\mu(n)$ that we need. By taking advantage of $\\mu$ being [multiplicative](https://en.wikipedia.org/wiki/Multiplicative_function), we can compute values using a similar strategy to the sieve of Eratosthenes - see [problem 10](https://projecteuler.net/problem=10)."
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 4,
|
"execution_count": 4,
|
||||||
"id": "48c9275b",
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
|
||||||
"def mobius_range(limit):\n",
|
|
||||||
" ms = [n for n in range(0, limit)]\n",
|
|
||||||
"\n",
|
|
||||||
" for n in range(0, limit):\n",
|
|
||||||
" if n == 0 or n == 1 or ms[n] != n:\n",
|
|
||||||
" yield ms[n]\n",
|
|
||||||
" continue\n",
|
|
||||||
" \n",
|
|
||||||
" yield -1\n",
|
|
||||||
" \n",
|
|
||||||
" for k in range(2 * n, limit, n):\n",
|
|
||||||
" if k % n ^ 2 == 0:\n",
|
|
||||||
" ms[k] = 0\n",
|
|
||||||
"\n",
|
|
||||||
" ms[k] //= -n"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "markdown",
|
|
||||||
"id": "f12a8b30",
|
|
||||||
"metadata": {},
|
|
||||||
"source": [
|
|
||||||
"We'll use $a = \\sqrt{1000000} = 1000$ as our upper bound."
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": 5,
|
|
||||||
"id": "c2071ba1",
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
|
||||||
"from math import isqrt\n",
|
|
||||||
"mu = list(mobius_range(isqrt(1000000) + 1))"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "markdown",
|
|
||||||
"id": "a8db5c70",
|
|
||||||
"metadata": {},
|
|
||||||
"source": [
|
|
||||||
"Now we can implement $M(n)$ with our recursive formula:"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": 6,
|
|
||||||
"id": "f04ed69a",
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
|
||||||
"@cache\n",
|
|
||||||
"def M(n):\n",
|
|
||||||
" if n == 0 or n == 1:\n",
|
|
||||||
" return n\n",
|
|
||||||
" \n",
|
|
||||||
" a = sqrt(n)\n",
|
|
||||||
" b = n / a\n",
|
|
||||||
" \n",
|
|
||||||
" total = 1 + floor(b) * M(floor(a))\n",
|
|
||||||
" total -= sum(mu[x] * floor(n/x) for x in range(1, floor(a) + 1))\n",
|
|
||||||
" total -= sum(M(floor(n/y)) for y in range(2, floor(b) + 1))\n",
|
|
||||||
" \n",
|
|
||||||
" return total"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "markdown",
|
|
||||||
"id": "132dad8d",
|
|
||||||
"metadata": {},
|
|
||||||
"source": [
|
|
||||||
"And finally, yet another implementation of $\\Phi(n)$:"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": 7,
|
|
||||||
"id": "394d604a",
|
"id": "394d604a",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"scrolled": true
|
"scrolled": true
|
||||||
},
|
},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
|
"@cache\n",
|
||||||
"def totient_sum(n):\n",
|
"def totient_sum(n):\n",
|
||||||
|
" if n == 1:\n",
|
||||||
|
" return 1\n",
|
||||||
|
"\n",
|
||||||
" a = sqrt(n)\n",
|
" a = sqrt(n)\n",
|
||||||
" b = n / a\n",
|
" b = n / a\n",
|
||||||
"\n",
|
"\n",
|
||||||
" total = sum(x * M(floor(n/x)) for x in range(1, floor(a) + 1))\n",
|
" W = (n*(n+1)) // 2\n",
|
||||||
" total += sum(polygonal_number(3, floor(n/y)) * mu[y] for y in range(1, floor(b) + 1))\n",
|
" X = floor(b) * totient_sum(floor(a))\n",
|
||||||
" total -= M(floor(b)) * polygonal_number(3, floor(a))\n",
|
" Y = sum(euler_phi(x) * (n//x) for x in range(1, floor(a)+1))\n",
|
||||||
"\n",
|
" Z = sum(totient_sum(n//y) for y in range(2, floor(b)+1))\n",
|
||||||
" return total"
|
" return W + X - Y - Z"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -273,7 +187,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 8,
|
"execution_count": 5,
|
||||||
"id": "55f0719b",
|
"id": "55f0719b",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
@@ -283,7 +197,7 @@
|
|||||||
"303963552391"
|
"303963552391"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"execution_count": 8,
|
"execution_count": 5,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"output_type": "execute_result"
|
"output_type": "execute_result"
|
||||||
}
|
}
|
||||||
@@ -309,7 +223,7 @@
|
|||||||
],
|
],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"kernelspec": {
|
"kernelspec": {
|
||||||
"display_name": "SageMath 9.5",
|
"display_name": "SageMath 10.8",
|
||||||
"language": "sage",
|
"language": "sage",
|
||||||
"name": "sagemath"
|
"name": "sagemath"
|
||||||
},
|
},
|
||||||
@@ -323,7 +237,7 @@
|
|||||||
"name": "python",
|
"name": "python",
|
||||||
"nbconvert_exporter": "python",
|
"nbconvert_exporter": "python",
|
||||||
"pygments_lexer": "ipython3",
|
"pygments_lexer": "ipython3",
|
||||||
"version": "3.11.2"
|
"version": "3.12.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nbformat": 4,
|
"nbformat": 4,
|
||||||
|
|||||||
Reference in New Issue
Block a user