
Divisibility by 7 is a Walk on a Graph - how does it work? - ColinWright
http://blog.tanyakhovanova.com/?p=262
======
xyzzyz
This graph is just deterministic finite automaton[1] with some fancy rules to
make it easier to remember and to draw less arrows. What this proves is that
that when you look at natural numbers as a words over {0, 1, ..., 9} alphabet,
numbers divisible by 7 form a regular language[2]. This method generalizes to
divisiblity by any number, not just 7, though it requires some cunning to come
up with a nice way of representing the DFA like in OP's post, without tens or
hundreds of arrows.

Another cool thing in this is that existence of such graph implies existence
of a regular expression testing divisibility by 7. Indeed, it's easy to check
divisibility by 2 or 5 using regular expressions, it's /^.[star](0|2|4|6|8)$/
and /^.[star](0|5)$/, where [star] means star operator (HN markup changes it
to italics). It's a bit less clear how to construct similar regular expression
for 7, but if follow closely the proof of equivalence between regular
languages and languages recognizable by finite state automata, you can
reconstruct the regular expression checking the divisibility by 7 from OP's
graph.

[1] - <http://en.wikipedia.org/wiki/Deterministic_finite_automaton> [2] -
<http://en.wikipedia.org/wiki/Regular_language>

~~~
indiefan
Great comment. Am I the only person waiting for someone to provide the regular
expression intimated at above?

~~~
robinhouston
If you want regular expressions for divisibility, here’s the little toy I made
last time this came up on HN: [http://s3.boskent.com/divisibility-
regex/divisibility-regex....](http://s3.boskent.com/divisibility-
regex/divisibility-regex.html)

You can do any number in any base up to 36 (subject to limitations of patience
and available RAM, naturally).

The resulting regular expressions are not terribly friendly. You have to
remember that converting a DFA to a regex can lead to exponential blow-up in
size.

------
drostie
You asked "how does it work?" so let me answer that. How it works:

Okay, so the black arrows represent adding one, and the white arrows represent
multiplying by ten. The circle that you see represents the fact that after we
hit 7 when adding by one, we "loop around" to 0 again, because 7 has remainder
0 when divided by 7.

So we are constructing (in their example) 325 by saying:

    
    
        Instruction     | Number | Remainder
        start with 0.   |    0   |  0
        add 3.          |    3   |  3
        multiply by 10. |   30   |  2
        add 2.          |   32   |  4
        multiply by 10. |  320   |  5
        add 5.          |  325   |  3
    

Now the only thing you really need to know is that "multiply by 10" is only a
function of the current remainder, it can never depend on the rest of the
digits we've processed before in any other way.

To see this, just write out the number. The number is 7 k + r for some
remainder r. Multiplying by 10 produces 70k + 10 r. Taking the remainder when
you divide by 7 is just... (10 r) % 7, because the first term is divisible by
7. So you only need to keep track of the remainder.

In fact, 10 r is really 7r + 3r, and the 7r is also divisible by 7, which
means that the white arrows really just multiply by 3. That is why 0 maps to
0, 1 maps to 3, 2 maps to 6, and 3 maps to 9 % 7 == 2.

------
omaranto
jowair already said this [1], but here's a less concise explanation:

The black arrows implement the operation of adding one mod 7, the white arrows
implement the operation of multiplying by ten mod 7. The decimal expansion of
a number tells you how to build it out of the operations of adding 1 and
multiplying by 10, for example, to get 321, start at 0 and then do the
operations +1, +1, +1, x10, +1, +1, x10, +1. Of course, if you do the
operations mod 7, by following the arrows in the graph, instead of getting
your number you get its residue mod 7.

[1]: <http://news.ycombinator.com/item?id=3909866>

~~~
aidenn0
A quicker mnemonic for me is that the white arrows are multiplying by 3 mod 7,
which is an equivalent operation to multiplying by 10 mod 7.

------
recursive
Divisibility by any n is a walk on a graph with n nodes, each one representing
one of the modulus equivalence classes 0 .. n-1.

~~~
jowiar
And there are graphs going either way, either starting from most or least
significant digit. (Take previous modulus, *10 mod N, + next digit mod N), or
(Take previous modulus, + 10^(next digit) mod N)

~~~
recursive
In the case of least significant to most significant, that means you need more
nodes, because the position of the current digit is another piece of state you
also have to keep track of.

~~~
jowiar
Yes - you need to work powers of 10 modulo the divisor as well.

------
drfuchs
I've been waiting decades for anyone to be interested in a divisible-by-7 test
that works right-to-left. Only 7 states required:

    
    
      #include <stdio.h>
    
      int x[7][10] = {
        /* 0 */ {0, 2, 4, 6, 1, 3, 5, 0, 2, 4},
        /* 1 */ {5, 0, 2, 4, 6, 1, 3, 5, 0, 2},
        /* 2 */ {3, 5, 0, 2, 4, 6, 1, 3, 5, 0},
        /* 3 */ {1, 3, 5, 0, 2, 4, 6, 1, 3, 5},
        /* 4 */ {6, 1, 3, 5, 0, 2, 4, 6, 1, 3},
        /* 5 */ {4, 6, 1, 3, 5, 0, 2, 4, 6, 1},
        /* 6 */ {2, 4, 6, 1, 3, 5, 0, 2, 4, 6}
      };
    
      int divisible_by_7(char *b)
      {
        char *p = b; while (*p) p++; // skip to NUL
        int state = 0;
        while (--p >= b)
          state = x[state][*p-'0'];
        return !state;
      }
    
      int main(int argc, char *argv[])
      {
        int i;
        for (i = 1; i<argc; i++)
          printf("%s is%s divisible by 7\n", argv[i], divisible_by_7(argv[i]) ? "" : "n't");
        return 0;
      }

------
sopooneo
Would this help solve the ITA Software "luck sevens" puzzle?
(<http://www.itasoftware.com/careers/puzzle_archive.html>) I never understood
how to do that one except by brute force.

"Write a program to compute the sum of all the integers between 1 and 10^11
both divisible by seven and, when the decimal digits are reversed, are still
divisible by seven."

------
forinti
There's a sequence of numbers for jumping over all n-smooth numbers. The
problem is that the sequences get very large very fast:
[http://alquerubim.blogspot.com.br/2011/12/candidatos-
primos....](http://alquerubim.blogspot.com.br/2011/12/candidatos-primos.html)

