On 2018-03-10 02:13, Steven D'Aprano wrote:

But I've stared at this for an hour and I can't see how to extend the
result to three coordinates. I can lay out a grid in the order I want:

1,1,1   1,1,2   1,1,3   1,1,4   ...
2,1,1   2,1,2   2,1,3   2,1,4   ...
1,2,1   1,2,2   1,2,3   1,2,4   ...
3,1,1   3,1,2   3,1,3   3,1,4   ...
2,2,1   2,2,2   2,2,3   2,2,4   ...
...

and applying Cantor's diagonal order will give me what I want, but damned
if I can see how to do it in code.

If you want this exact order, it can be reproduced in the following way.

The triples can be viewed as a pair of a pair and a natural number:

(1,1),1 (1,1),2 (1,1),3 ...
(2,1),1 (2,1),2 (2,1),3 ...
(1,2),1 (1,2),2 (1,2),3 ...
   .       .       .
   .       .       .
   .       .       .

Decomposing this by the diagonals yields

1: (1,1),1
2: (2,1),1  (1,1),2
3: (1,2),1  (2,1),2  (1,1),3
.
.
.

Notice that the first elements of each pair (i.e. the inner pairs) are given by the (inverse of the) original Cantor pairing function, in decreasing order. The second elements are just the natural numbers. Naming the inverse c, we can rewrite the above like this:

1: c(1),1
2: c(2),1  c(1),2
3: c(3),1  c(2),2  c(1),3
.
.
.

Rewriting this to spell out the mapping between the natural numbers and the pairs, we get

1 -> c(1),1
2 -> c(2),1
3 -> c(1),2
4 -> c(3),1
5 -> c(2),2
6 -> c(1),3
.
.
.

Squinting a bit, this might seem familiar. This is exactly the same as the original pairing function, except for an additional application of c to the first element of the pair!

1 -> 1,1
2 -> 2,1
3 -> 1,2
4 -> 3,1
5 -> 2,2
6 -> 1,3

This leads fairly naturally to the implementation.

    from itertools import accumulate, count

    def c(i):
        """
        Inverse of the Cantor pairing function, mapping N → N×N.
        """
        assert i >= 1

        # partial sums of the series 1 + 2 + 3 + ...
        sums = accumulate(count(1))
        n = 0

        while True:
            m = next(sums)
            if m < i:
                n += 1
            else:
                r = m - i
                break

        return r + 1, n - r + 1


    def c3(i):
        """
Inverse of the Cantor pairing function generalization to triples,
        mapping N → N×N×N.
        """
        n, m = c(i)
        return c(n) + (m,)

Applying c3 to the natural numbers gives the sequence you wanted:

s = map(c3, count(1))
pprint([next(s) for _ in range(10)])
[(1, 1, 1),
 (2, 1, 1),
 (1, 1, 2),
 (1, 2, 1),
 (2, 1, 2),
 (1, 1, 3),
 (3, 1, 1),
 (1, 2, 2),
 (2, 1, 3),
 (1, 1, 4)]

--
Denis Kasak
--
https://mail.python.org/mailman/listinfo/python-list

Reply via email to