6

I want to generate the pairs of cartesian coordiantes inside a bounded square ordered by their product in descending order. For example, for a square of size 3, the coordinates are:

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

Is there any way to generate this list fast - i.e, a constant-time function that maps integers to the nth coordinate?

MaiaVictor
  • 51,090
  • 44
  • 144
  • 286
  • The first three pairs seem obvious, the fourth not difficult. Spell it out, try to generalise. – greybeard Apr 13 '15 at 18:57
  • 2
    Why have you left out (3,1) and (1,3)? – Paul Boddington Apr 13 '15 at 19:02
  • That's OK. These sequences are very complicated. What rough order of magnitude are the values of N (if the square is NxN) you are using? – Paul Boddington Apr 13 '15 at 19:55
  • Kinda big, 5~10 (so, numbers up to 1000000000). I've managed to find a solution that returns chunks such that all pairs inside a chunk is bigger than any pair on the next chunk, but ordering is not guaranteed inside a chunk. I've done it by getting terms between two constants of the `x * y = k` line. – MaiaVictor Apr 13 '15 at 20:00
  • 'Brensenham'ing between 'circles' through neighbouring points on the squares outline? Which reminds me: the square is from `(1, 1)` to `(N, N)`, to pick up pbabcdefp? – greybeard Apr 14 '15 at 00:34
  • I don't get it. The product of what? x and y? Is the list in your problem supposed to be the desired order? Maybe you could provide an example input and output. – erickson Apr 14 '15 at 21:16
  • Your question and your example does not match. Why does `(3,1)` come later than `(2,1)` even though `3*1 < 2*1`? – Lie Ryan Apr 15 '15 at 01:12
  • 1
    Because stupid. Fixed. – MaiaVictor Apr 15 '15 at 16:30
  • 5^10 = 9765625 ~= 10^7, not 10^9. – Will Ness Apr 20 '15 at 12:56

3 Answers3

1

Perhaps you could elaborate more on your specific needs in terms of how fast you would like the generation and how rapidly you might change the bounds of the square.

This problem is akin to generating the distinct numbers in a multiplication table (the cardinality of which studied Paul Erdos and the fastest known algorithm to calculate exactly is O(n^2)).

One way to consider generating sections of your list (assuming you will not be listing billions of coordinates) is to quickly hash a partial set of i*js in descending order and sort them. To make the hash accurate, we extend it below the chosen range [n,k] until after n * l is lower than k*k for some l. For example, for the range of coordinates from (10,10) to (7,7), we extend our hash to (5,5) so that (10,5), which is greater than (7,7), will be included.

JavaScript code:

function f(n,k){
  var l = k, k2 = k*k;
  while (n*l > k2){
    l--;
  }
  console.log("low bound: " + l);
  var h = {}, h2 = [];
  for (var i=n; i>l; i--){
    for (var j=i; j>l; j--){
      var m = i*j;
       if (h[m]) h[m] = h[m].concat([i,j]);
       else {
         h[m] = [i,j];
         h2.push(m);
      }
    }
  }
  h2.sort(function(a,b){return b-a});
  var i=0;
  while(h2[i] >= k2){
    console.log(h[h2[i++]]);
  }
}

Output:

f(10,6)

low bound: 3

(10,10) 
(10,9) 
(9,9) 
(10,8)
...
(10,4), (8,5)
(9,4), (6,6)

More output:

f(1000000,999995)

low bound: 999990

(1000000,1000000) 
(1000000,999999) 
(999999,999999)
(1000000,999998) 
(999999,999998) 
(1000000,999997) 
(999998,999998) 
(999999,999997) 
(1000000,999996) 
(999998,999997)
(999999,999996) 
(1000000,999995) 
(999997,999997)
(999998,999996)
(999999,999995) 
(1000000,999994)
(999997,999996) 
(999998,999995) 
(999999,999994) 
(1000000,999993) 
(999996,999996)
(999997,999995) 
(999998,999994) 
(999999,999993) 
(1000000,999992) 
(999996,999995) 
(999997,999994) 
(999998,999993) 
(999999,999992) 
(1000000,999991) 
(999995,999995)
גלעד ברקן
  • 23,602
  • 3
  • 25
  • 61
1

I have not tested this idea. You can quickly generate a list of all the coordinates in roughly the correct order by just taking off the diagonals from bottom right to top left, as with the argument for the countability of the rationals. That will give you a nearly sorted list.

There are sorting methods that can take advantage of that to give you a faster sort. See Which sorting algorithm is best suited to re-sort an almost fully sorted list? for a discussion. You could always try different sorting algorithms to see what works best for your data.

Community
  • 1
  • 1
rossum
  • 15,344
  • 1
  • 24
  • 38
1

your enumeration should proceed from the top-right corner to the bottom-left, naturally.

maintain the boundary as a priority queue. start with top right corner being the only one entry in the boundary.

on each step, pop the max element from the PQ and insert its three descendants (West, South, and South-West) into the queue, without creating duplicates (maybe use actual array of arrays to back the queue, but that means additional space... well, there are no more than n of these short (say, vertical) arrays, each no larger than a few elements, and they never grow/move upwards, only downwards).

Length of the queue is O(n) – think "diagonals", even if curved, –

enter image description here

and you produce n2 results, so overall complexity depends on the efficiency of the queue implementation. If that's logarithmic, it'll be O(n2 log n) and if linear (using hash table, as we know the range of the values involved), O(n2), overall; but it will be on-line, – O(1)...O(log n) per produced pair.

If the precision will allow (for your range it looks like it will), precalculate logarithms of your coordinates, and order the pairs by log(x) + log(y) instead of by x * y, trading O(n2) multiplications for n logarithms and O(n2) additions.

edit: see this for an actual Haskell code for another, very similar algorithm; it also contains additional hint how to speed it up by another factor of 2 (xy==yx), so work on a triangular half of the square only -- this will also halve the space needed. And it looks like there's no need to add the SW child to the priority queue, just S and W should be enough!

Community
  • 1
  • 1
Will Ness
  • 70,110
  • 9
  • 98
  • 181
  • from the original question: `i.e, a constant-time function that maps integers to the nth coordinate`. – greybeard Apr 22 '15 at 09:29
  • I think none can, which may be why answers have been slow. – greybeard Apr 22 '15 at 09:34
  • @greybeard and the question opens with *"I want to generate the pairs of cartesian coordiantes inside a bounded square ordered by their product in descending order. "* i.e. it does talk about *generating* the *"pairs"*, plural. -- the OP then says *"Is there any way to generate this list fast"* again, talking about generating the whole *list*. Thus my reading of it. – Will Ness Apr 22 '15 at 09:51
  • Close enough to close this question as a sub-problem :-/ Given this [hyperbola question](http://math.stackexchange.com/questions/172652/a-hyperbola-passing-through-integer-lattice-points) (which I can't discern from [Grid Points on Hyperbolas](http://nrich.maths.org/345) (try _Getting Started_ and, in due time, _Solution_), it might be worth re-asking a properly re-worded variant of either problem on [Mathematics](http://math.stackexchange.com) or [NRICH](http://nrich.maths.org) (including pointers). – greybeard Apr 23 '15 at 01:28