[Tutorial] DP in Number Theory

Revision en12, by stoyan_malinin, 2021-09-08 21:36:05

In this blog I am going to present you a very interesting concept or a technique, that I don't know exactly what is, because I cannot remember where I learned it from.

The thing that makes it so special for me is its simplicity, because it usually doesn't require a lot of thinking or deep knowledge in number theory or mathematics as general.

As you may have guessed by the title of this article it uses dynamic programming to solve problems related to number theory. More specifically, it, in my opinion, is very appropriate for problems that require us to find the the number of pairs that satisfy some divisibility condition (usually gcd or something like this). Most of the problems presented in this article will probably have a solution using Möbius inversion or some combinatorial approach (usually the inclusion-exclusion principle). It can probably be proven that this trick is equivalent to the inclusion-exclusion principle, but I still believe that it is worth knowing, since it is very intuitive and simple. If you want to study more about Möbius inversion you can check this out.

The technique is really simple and is based on, roughly speaking, overcounting and compensating for it. I will start explaining with a very popular example. Find the number of coprime pairs of integers from $$$1$$$ to $$$n$$$. This, as I mentioned earlier, can be done using Möbius inversion or inclusion-exclusion principle, but I won't talk about them in this blog. However, there are some similarities between the solutions that I will present and the ones using the other techniques, so check them out if you are not familiar with them. So without anything further to do, let's begin with the solution.

Initially we will begin by getting a rough estimate for the actual values. What I mean is that, for example, if two numbers have a greatest common divisor $$$d$$$, both of them should be at least divisible by $$$d$$$. So we can say that all pairs of numbers that have gcd $$$d$$$ will be contained in the set of pairs of numbers, where both of the numbers are divisible by $$$d$$$. More specifically the amount of these pairs is $$$\frac{\lfloor n/d \rfloor(\lfloor n/d \rfloor-1)}{2}$$$. But what exactly does this number contain? If we think about it, it includes all pairs of numbers that have gcd greater than or equal to $$$d$$$, more specifically the greatest common divisors can be expressed as $$$k \cdot d$$$ where $$$k \geq 1$$$. Let's store these values in an array called, for example, $$$dp$$$, where $$$dp[d] = \frac{\lfloor n/d \rfloor(\lfloor n/d \rfloor-1)}{2}$$$. From now on we will try to exclude from $$$dp[d]$$$ all the pairs that don't have exactly $$$d$$$ as gcd.

Let's analyze the array a little bit. We can see that some of the values are actually correct. For example $$$dp[n]$$$ is right, because we cannot have a pair of integers that have gcd $$$2n$$$, simply because all numbers are $$$\leq n$$$. If we try to go a little bit further we can see that for example $$$dp[n/2]$$$ is equal to $$$dp[n/2] - dp[n]$$$ (of course assuming that n is an even number). This is true because $$$dp[n/2]$$$ stores only pairs that have gcd $$$n/2$$$ or $$$n$$$, but $$$dp[n]$$$ has the exact number of pairs, so after all the number that is left in $$$dp[n/2]$$$ becomes correct as well.

So let's try to generalize this a little bit. if we have some $$$d$$$ then $$$dp[d]$$$ will contain the values for $$$d, 2d, 3d, ...$$$ and so on. This means that the only thing that we have to do is to exclude these values and leave only the correct one for $$$d$$$. We can get these values from the other values of the array $$$dp$$$, i.e. — $$$dp[2d], dp[3d], ...$$$ and if some of them are not correct we can compute them first. This will work, because the "chain" of values that we will have to compute will only increase, so it will eventually reach a point, when the value is correct, therefore we can compute all of them.

To summarize, we will outline an algorithm, which solves this particular task and which we will use later as a model for the other problems. Initially we will read $$$n$$$ and will populate the array $$$dp$$$ as we mentioned earlier. We will start iterating a variable $$$d$$$ from $$$n$$$ down to $$$1$$$ and we will do the following thing — start iterating a value $$$k$$$, which start from $$$2d$$$ and increases by $$$d$$$ at each step, until it becomes greater than $$$n$$$. For each step of $$$k$$$ we will decrease the value of $$$dp[d]$$$ by $$$dp[k]$$$ (since we are going in decreasing order of $$$d$$$, $$$dp[k]$$$ is always correct).

Example 1: You are given an array $$$a$$$. Calculate the number of pairs $$$i<j$$$, so that $$$gcd(a[i], a[j])=1.$$$

Solution

Example 2: You are give an array $$$a$$$. Compute the sum of $$$lcm(a[i], a[j])$$$ for $$$i<j$$$.

Solution

Example 3

Solution

I hope that you enjoyed what I have done. Feel free to correct me somewhere if I have made a mistake or add something that may be helpful. Have a nice day!

History

 
 
 
 
Revisions
 
 
  Rev. Lang. By When Δ Comment
en12 English stoyan_malinin 2021-09-08 21:36:05 377 Tiny change: 'echnique, I don't k' -> 'echnique, that I don't k' (published)
en11 English stoyan_malinin 2021-09-08 21:16:53 909 Tiny change: 'perfect $d-power$. For exam' -> 'perfect $d$-power. For exam'
en10 English stoyan_malinin 2021-09-08 16:28:58 300
en9 English stoyan_malinin 2021-09-08 16:23:18 19
en8 English stoyan_malinin 2020-10-30 23:41:54 1090 Tiny change: 'nExample 2\nProblem: Y' -> 'nExample 2Problem: Y'
en7 English stoyan_malinin 2020-10-30 20:45:59 213 Tiny change: '] = \frac{cntDiv[d](cntDiv[d]-1)}{2}$.\n\n' -> '] = \frac{1}{2}$.\n\n'
en6 English stoyan_malinin 2020-10-28 09:29:28 421 Tiny change: 'd] = /fract{cntDiv[d]' -> 'd] = /frac{cntDiv[d]'
en5 English stoyan_malinin 2020-10-17 00:57:00 12 Tiny change: 'ample $dp[\frac{n}{2}]$ is equa' -> 'ample $dp[n/2]$ is equa'
en4 English stoyan_malinin 2020-10-16 23:08:08 2309 Tiny change: '$ where $k>=1$.\n\n\n\' -> '$ where $k\geq1$.\n\n\n\'
en3 English stoyan_malinin 2020-10-16 22:00:22 1062 Tiny change: 's from $$1$$ to $$n$$. \n' -> 's from $$1 to n$$. \n'
en2 English stoyan_malinin 2020-10-16 21:20:26 795
en1 English stoyan_malinin 2020-10-16 21:09:07 173 Initial revision (saved to drafts)