Is there a fast divisibility check for a fixed divisor?
Is there a fast algorithm to check if $d \mid n$ is true for varying $n$, if divisor $d$ is fixed?
Variable $n$ is a $w$-bit binary integer, $d$ is an integer constant.
Solution 1:
Yes, there is an algorithm that only uses multiplication. This algorithm uses a lot of precomputation, but generates a simple expression that can be used to check for divisibility.
For example, if you have an 4 bit integer, and want to check if it's divisible by 3 it's enough to check (using 4-bit modular multiplication):
n * 11 <= 5
The example for $0 \leq n \leq 7$:
0 * 11 = 0 <= 5
1 * 11 = 11
2 * 11 = 6
3 * 11 = 1 <= 5
4 * 11 = 12
5 * 11 = 7
6 * 11 = 2 <= 5
7 * 11 = 13
I will first demonstrate and prove correct a technique for uneven $d$, and then for even $d$. I define $m = 2^w$.
Uneven $d$.
Find the modular multiplicative inverse $a$ of $d$ modulo $m$:
$$ad \equiv 1 \pmod m \tag{1}$$
This exists because $\gcd(d, m) = 1$ since $d$ is uneven. Also find $b$:
$$b = \left\lfloor {m-1\over d}\right\rfloor \tag{2}$$
Using (1) we get the following identity:
$$d(an \bmod m) = n \Leftrightarrow d \mid n \tag{3}$$
Now we create this equivalence, by multiplying both sides by $d$:
$$an \bmod m \leq b \Leftrightarrow d(an \bmod m) \leq bd \tag{4}$$
Because $bd \leq m - 1$ and (1):
$$d(an \bmod m) \leq m-1 \Leftrightarrow d(an \bmod m) = n \tag{5}$$
Combining (3), (4) and (5) gives us our fast check:
$$an \bmod m \leq b \Leftrightarrow d \mid n \tag{6}$$
This is fast because we turned a modulo $n$ into a modulo $m$. Arithmetic modulo $m$ is very cheap
because it's a power of two, and on fixed-width integers (such as uint64_t
in C++) it is free.
Even $d$.
Write $d$ as $2^j\cdot k$ with $k$ odd. Then we will check two conditions:
$$d \mid n \Leftrightarrow k \mid n \wedge 2^j \mid n$$
We check $k \mid n$ through the method for odd numbers above. We can check $2^j \mid n$ by using:
$$2^j \mid n \Leftrightarrow n2^{w-j} \equiv 0 \mod m$$
Since we're multiplying by a power of two we can use a bitshift to further speed things up.
Generating $a$, $b$, $j$.
I'm in a helpful mood, so here's a small Python3 function that given $d$ and $w$ prints the
expressions you need to check to find if $d \mid n$. It assumes that <<
and *
use $w$-bit arithmetic.
import math
def egcd(a, b):
if a == 0:
return (b, 0, 1)
else:
g, y, x = egcd(b % a, a)
return (g, x - (b // a) * y, y)
def fast_div(d, w):
m = 2**w
j = 0
while d % 2 == 0:
d //= 2
j += 1
if j > 0:
print("n << {} == 0".format(w-j))
if d > 1:
print("n * 0x{0:0{2}x} <= 0x{1:0{2}x}".format(egcd(d, m)[1] % m, m // d,
math.ceil(w/4)))
So for example fast_div(76, 64)
prints:
n << 62 == 0
n * 0x86bca1af286bca1b <= 0x0d79435e50d79435