
Divisibility by 7 is a Walk on a Graph (2009) - znpy
http://blog.tanyakhovanova.com/2009/08/divisibility-by-7-is-a-walk-on-a-graph-by-david-wilson/
======
sirsar
This "walk on a graph" is known formally as a Deterministic Finite Automata.
One of the interesting results of automata theory is that every FA has an
equivalent regular expression (a subset of the "regexps" in most modern
programming languages).

Where is this going? Behold, a 12,701 character regex for divisibility by 7:
[http://codegolf.stackexchange.com/questions/3503/hard-
code-g...](http://codegolf.stackexchange.com/questions/3503/hard-code-golf-
regex-for-divisibility-by-7)

This doesn't use any recursive features (it is a real Regular Expression in
the theoretic sense) so it can be checked in linear time.

~~~
newsignup
> a 12,701 character regex for divisibility by 7:

I wonder if that's the optimal regex or can be reduced further using some
rules[1] like the ones which we(humans) generally use to do it manually.

[1].
[https://www.math.hmc.edu/funfacts/ffiles/10005.5.shtml](https://www.math.hmc.edu/funfacts/ffiles/10005.5.shtml)

~~~
anderskaseorg
I wrote a 11811-character regex for divisibility by 7 back in August 2008:
[http://web.mit.edu/andersk/Public/regex](http://web.mit.edu/andersk/Public/regex)

~~~
fizbin
And unlike the one on stackexchange, it's actually correct!

(I have a regex -> DFA compiler here that I just threw your regex through.
Seven states, connected as expected)

------
drfuchs
Here's another human-friendly scheme to test divisibility by 7 that I don't
think is popularly known: Write down the number, then cross off the least
significant digit, double it, and subtract that from what's left of the
number. Repeat until you get zero or an obviously divisible-by-7 number, or
not.

Example: 325 becomes 32 - 10 = 22, which is obviously not divisibile by 7.

Better example: 8638 -> 863 - 16 = 847 -> 84 - 14 = 70 -> 7 - 0 -> 7, so we
have a winner.

Learned this as a kid (forget where), but it took decades for me to realize
why it obviously works.

~~~
colinodell
So why does this work?

~~~
Jtsummers
...There was another answer here that seemed close.

I'm not sure the best way to express it, but here's what I've got:

Allow me to use a shorthand, N = AB where AB is shorthand for 10 * A + B, and
B is the least significant digit. IF N is divisible by 7, then AB = 0 (mod 7).
This allows us to do the following manipulations:

    
    
      AB = 0 (mod 7)
      10*A + B = 0 (mod 7)
      3*A + B = 0 (mod 7)
      5*(3*A + B) = 5*0 (mod 7) // EDIT: had -2 on the RHS, that was correct but confusing
      A + 5*B = 0 (mod 7)
      A - 2*B = 0 (mod 7)
    

So we've arrived at drfuchs' method. You only need to repeat the process until
you've proven to yourself that A - 2 * B is divisible by 7 or not.

That's all I've got. I ran a half-marathon yesterday, and I've been up for 16
hours today. My brain might work better tomorrow.

EDIT: changed the steps a bit, had some unneeded ones. Also, this shows that
you can use addition instead of subtraction, but you won't be moving towards
0, but you still have to be able to recognize that the new number is divisible
by 7.

~~~
jacobenget
Thanks for this explanation! I was about to post something similar after
working the math out, but I'm sure my answer would have been twice as long and
less clear.

------
dools
Another interesting feature of dividing by 7 is that the decimal repeats as:

0.142857 = 1/7 0.285714 = 2/7 0.428571 = 3/7 0.571428 = 4/7 0.714285 = 5/7
0.857142 = 6/7

you can see the first digit after the decimal point increases in numerical
order through the available numbers in the pattern, the proceeds to repeat in
the same order

Some other prime denominators behave the same way. For example, 1/17th is
0.0588235294117647 and the first digit after the decimal point increases like
0,1,1,2,2,3,4,4,5,5,6,7,7,8,8,9 ... Where there are 2 of the same number in
the sequence, the first time you encounter that number you start from it's
first ocurrence so for example 3/17 is 0.2941176470588235

This makes for a pretty spooky party trick but I can tell you from experience
it doesn't attract the opposite sex.

------
gizmo686
I had some fun reverse engineering that. Here is my take at how it works.

Label all of the nodes 0 through 6, starting with making the white node 0, and
following the black arrows. We can consider each of these nodes to represent a
congruence mod 7.

Let n be the first digit of the number, and nXXX be the entire number. After
we walk n nodes by following the black arrows, we arrive at the node n' = n
MOD 7. Notice that n'XXX = nXXX MOD 7. More specifically, nXXX is divisible
mod 7 if and only if n'XXX is divisible by 7.

Let m be the second digit. At this point, we reduced the problem to testing
n'mXX MOD 7. Notice that n'mXXX = (10n' \+ m)XX. [0] Notice that following the
white arrow at an arbitrary node, k, is equivilent to following 10k black
arrows. [1] This means that after following the white arrow, we have reduced
the problem to mXX. At this point, we simply recurse to the first step until
we are out of digits.

[0] This construction is slightly improper, in that it involves a single
"digit" having a value greater than 10.

[1] This can be verified by considering the value of 10k MOD 7, and comparing
it with the destination node.

------
Figs
I wrote a post on reddit with some code to generate diagrams with generalized
divisibility testing the last time I saw this idea (about two years ago):
[https://www.reddit.com/r/math/comments/1p9g8t/start_at_the_b...](https://www.reddit.com/r/math/comments/1p9g8t/start_at_the_bottom_pick_any_number_take_the/cd095x4)

Copied here for the lazy:

I wrote a small python script to generate DOT code for graphviz so that you
can just input the base and the number to check division and automatically
draw the graph. (The resulting graphs aren't as pretty as one made by a human,
but it's kind of fun to poke around with it.)

Here's the script:

    
    
        base  = 10
        divby = 7
    
        print "digraph {"
        print "    0 [shape=doublecircle]"
    
        for i in xrange(0, divby):
            print "   ", i, "->", (i+1)%divby
            print "   ", i, "->", (base*i)%divby, "[arrowhead=empty]"
            
        print "}"
    

Save it to a file such as `make_graph.py`. Then, as long as you have the
graphviz binaries on path, you can render an image like:

    
    
        python make_graph.py > graph.dot
        dot -Tpng graph.dot -o out.png
    

Here are some sample outputs:

\- [base = 3, divby =
11]([http://imgur.com/cwUmnME](http://imgur.com/cwUmnME))

\- [base = 10, divby =
7]([http://imgur.com/O7GYw17](http://imgur.com/O7GYw17))

\- [base = 10, divby =
10]([http://imgur.com/k6xC0CQ](http://imgur.com/k6xC0CQ))

\- [base = 10, divby =
11]([http://imgur.com/1SSXKcK](http://imgur.com/1SSXKcK))

\- [base = 16, divby =
7]([http://imgur.com/QZzSyva](http://imgur.com/QZzSyva))

Not all the edges are actually reachable -- e.g. base 10, divby 10 has a black
edge from 9 to 0 even though you can't ever wrap around to 0 since `(10 *
anything)%10` is zero -- so the biggest you can get to is 9, then when you
follow the white edge for 9x you start over at zero... There may also be other
_edge cases_ I haven't thought of ;)

------
Jtsummers
Whenever I'm bored I either doodle or randomly recall and work out random
tidbits of CS theory. One that I did recently was to work out the DFAs that
determine the divisibility by various numbers in base 2. You end up with some
neat patterns emerging with primes versus non-primes and other details. You
also get the modulus for free if you number your states.

------
stevebmark
WHY would you pick an example number that's not divisible by 7?! Academics
facing the real world are the strangest creatures. Also this is really cool.

~~~
keithpeter
I agree the OA would be improved by two examples, one of each.

An answer: Counter-examples are important in Maths.

Anecdote: Oliver Sacks wrote about autistic twins [1] who could not speak or
communicate well to people but who could perform prodigious feats of
calculation, including factorising large pimes. Sacks sat with them with a
table of prime numbers, and swapped some _really_ large ones after which they
took him seriously. Sacks took their reaction to his large primes as proof of
their arithmetical ability.

The mathematician John Conway (the game of life chap) was quoted on an online
forum decades later as saying that Sacks should have suggested some large
_non_ -prime numbers as a test of the twins. I'm searching for a reference!

[1] [http://math-frolic.blogspot.co.uk/2013/02/prime-savants-
fasc...](http://math-frolic.blogspot.co.uk/2013/02/prime-savants-
fascinating.html)

~~~
cjg
I liked your anecdote. It led to this more detailed analysis of the twins
abilities to detect primes:

[http://www.pepijnvanerp.nl/articles/oliver-sackss-twins-
and-...](http://www.pepijnvanerp.nl/articles/oliver-sackss-twins-and-prime-
numbers/)

------
dahart
This exact state machine -- divisibility by 7 -- is one of my favorite state
machines ever! It was a homework problem in college, and one of the few that
stuck with me over the years.

The implication of a DFA for divisibility by 7 is that it's easy (with
practice) to figure divisibility by 7 in your head for arbitrarily long
numbers as fast as someone can say them. Nice party trick! ;) I've also been
having debates with people about whether divisibility testing in your head is
a simpler operation than long division in your head. When you break it down
carefully, long division in your head for a single digit divisor is very
similar, and also surprisingly easy to do, the main difference might be simply
having to remember quotient digits, and nothing more.

I'm curious where the DFA for divisibility by 7 might have originally come
from, but I'm pretty sure it was a homework problem in the text I used,
"Elements of the theory of Computation" by Lewis and Papdimitriou, Prentice
Hall, 1981.

------
escherize
Wow that's awesome. I decided to re-implement divisiblity by 7 in this manner,
since integrating with <horrible system> could wait a few minutes. :)

[https://gist.github.com/62553ded59ec7e289be6](https://gist.github.com/62553ded59ec7e289be6)

Edit: s/division by 7/divisibility by 7

Thanks for pointing that out, omaranto!

~~~
omaranto
That is only a test for divisibility by 7, not 'division by 7', which is
typically understood to compute the quotient.

------
vinchuco
For an explanation on the construction:
[http://math.stackexchange.com/questions/413296/how-does-
the-...](http://math.stackexchange.com/questions/413296/how-does-the-
divisibility-graphs-work)

------
jhurwitz
For a bit of an explanation, see David's follow-up blog post:
[http://blog.tanyakhovanova.com/2010/08/divisibility-
by-7-is-...](http://blog.tanyakhovanova.com/2010/08/divisibility-by-7-is-a-
walk-on-a-graph-ii-2/)

------
joeax
Another quick way to check divisibility by 7 is to multiply each digit by 3^x
where x is the place (right to left). Good way to check divisibility in your
head and useful up to 4-5 digits.

As an example, consider 413: 4x9 + 1x3 + 3 = 36 + 3 + 3 = 42

42 is divisible by 7 therefore 413 is too.

~~~
Jtsummers

      10 = 3 (mod 7)
    

So this works because you're substituting the powers of 10 with powers of 3,
and we're operating under mod 7 arithmetic. Conveniently, this can also be
repeated like drfuchs' method if your numbers are too large to recognize as a
multiple of 7 after the first pass.

    
    
      413 => 4*9+1*3+3 = 42 => 4*3+2 = 14 => 1*3+4=7

------
TeMPOraL
Oh I still have nightmares of this. As others point out, you can use DFAs to
determine divisibility by various numbers (base, well, anything you like). I
remember spending a few hours banging my head against the desk, trying to
figure out how it works with only a bunch of crappy net articles. But it _is_
somewhat fun after you understand it.

------
askbypollankit
Can we build such state diagrams to check divisibility by other digits too?

~~~
omaranto
Yes, it's easy: to test divisibility by n make a graph with n verities labeled
0 through n-1; put black arrows from each vertex k to (k+1) mod n; and put
white arrows from each k to 10k mod n.

(That's for testing divisibility by n of numbers written in base 10; for
others bases just replace the 10 above.)

------
chris_wot
Seems like something Euler should have already done :-) I jest, of course.

------
c3534l
This gave me an unreasonable amount of pleasure doing.

------
zuxfer
help me, i can't get it to work for 1603.

~~~
psyklic
It works. Follow:

(1) 1 black then 1 white.

(6) 6 blacks then 1 white.

(0) 1 white.

(3) 3 blacks then 1 white.

And you end up at the bottom node, indicating it is divisible by 7.

~~~
thaumasiotes
You should be following the 1 white path _before_ the n black paths, not
afterwards. In particular, following "3 blacks then 1 white" in the last step,
when the final digit is three, is an error; it will produce the wrong modulus
for any number that is _not_ divisible by 7.

    
    
        (1) 1 white then 1 black,  ending up in state 1
        (6) 1 white then 6 blacks, ending up in state 2
        (0) 1 white then 0 blacks, ending up in state 6
        (3) 1 white then 3 blacks, ending up in state 0
    

The graph keeps track of ((the number so far) mod 7); following a white arrow
multiplies your total by 10, and following a black arrow increases your total
by 1.

~~~
sean2
I can see how you'd like to hold the order of operations correct for
readability purposes (especially if the states were labeled with digits), but
I don't see where psyklic's method could produce an error for the divisibility
test.

Your initial white doesn't change anything, since it brings you back to the
starting node. Any whites after the the final digit either keep you on the
starting node or keep you in the graph where.

~~~
thaumasiotes
The graph isn't just a divisibility test, it reports the remainder after
division by seven. I specifically acknowledged that this error won't mutate a
"divisible by 7" result into "not divisible by 7" (because any number that is
divisible by seven will still be divisible by seven after multiplying it by
ten), but in every other case it will give you the wrong answer. (e.g.
"remainder 2" will mutate into "remainder 6".)

