## Ordered Cartesian Coordinates

### April 21, 2015

Today’s exercise is a simple little interview question:

Generate the pairs of cartesian coordinates within a square bounded by (1,1) and (

n,n) ordered by their product in ascending order. For instance, whennis 3, the coordinates are (1,1), (1,2), (2,1), (1,3), (3,1), (2,2), (2,3), (3,2) and (3,3). Can you find a solution with time complexity better than O(n^{2})?

Your task is to write a program to solve the interview question. When you are finished, you are welcome to read or run a suggested solution, or to post your own solution or discuss the exercise in the comments below.

Advertisements

No, we cannot find a solution better than O(n^2).

This is because the result contains n^2 coordinates and we can produces each coordinate in O(1).

But using a Python generator we can produce them on demand, minimizing memory to O(1).

Mm forgot the sorting constraint. In this case we need the entire list in memory anyways.. Sorting is O(n log n) << O(n^2).

def coordinates(n):

for i in range(n):

for j in range(n):

yield (i+1,j+1)

l = sorted( [c for c in coordinates(3)], key=lambda x: x[0]*x[1] )

print l

Sorting is actually O(n^2 log n^2), which is worse than O(n^2).

Mm true. What about the solution built by mr. Programming Praxis?

He uses a sort function (https://programmingpraxis.com/contents/standard-prelude/#sorting) as well (O(n log n), with n=#coordinates)

I didn’t do it, but instead of a comparison-based sort at O(

nlogn) you could do a radix sort or some other O(n) sort. That would truly reduce the overall time to O(n^{2}).This Python, I believe, generates the sequence in O(n) space. There are n generator objects, one for each row of the multiplication table, which the heapq.merge combines to yield each (rk, (r, k)) in increasing order. The sequence is then pulled to memory at once for printing. That’s not merge’s fault.

The enumeration in descending order:

Reversing the sequence involves some straightforward changes in the code.

Forgot to add, the code’s in Haskell; it runs empirically at ~ n^1.08 producing first n=100,000 entries for a similar, slightly different problem (seen here ) at ranges on the order of 10^8.

I wrote this solution (on Python):

def coords(n):

for i in range(n*n):

yield(i//n+1, i%n+1)

So, I think it works for O(n).

In action:

>>> list(coords(4))

[(1, 1), (1, 2), (1, 3), (1, 4), (2, 1), (2, 2), (2, 3), (2, 4), (3, 1), (3, 2), (3, 3), (3, 4), (4, 1), (4, 2), (4, 3), (4, 4)]

Codepad source:

http://codepad.org/68GCPgmN

@Destabilizer, the task was to yield the pairs “ordered by their product”, so (2, 1), for example, should come before (1, 3). It may or may not come before (1, 2), which has the same product. You’ve produced a “row-major” order, or it might be called a “lexicographic order”. (Nice expression of it, though.)

It’s also not O(n) time. Your yield statement is executed n² times which is more than O(n). Do see Praxis’s discussion or first comment above.

class Program

{

static void Main(string[] args)

{

int n = 4;

Hashtable ht = new Hashtable();

int count =0;

for (int i = 1; i <= n; i++)

{

for (int j = 1; j 1)

{

if (!ht.ContainsValue(string.Format(“({0},{1})”, j, i)))

{

count++;

ht.Add(count, string.Format(“({0},{1})”, j, i));

}

}

}

}

}

foreach (int key in ht.Keys.Cast().OrderBy(x=>x))

{

Console.WriteLine(ht[key]);

}

Console.ReadKey();

}

}

Sorry HTML Truncated

class Program

{

static void Main(string[] args)

{

int n = 4;

Hashtable ht = new Hashtable();

int count =0;

for (int i = 1; i <= n; i++)

{

for (int j = 1; j <= n; j++)

{

if (!ht.ContainsValue(string.Format("({0},{1})", i, j)))

{

count++;

ht.Add(count, string.Format("({0},{1})", i, j));

if (j > 1)

{

if (!ht.ContainsValue(string.Format("({0},{1})", j, i)))

{

count++;

ht.Add(count, string.Format("({0},{1})", j, i));

}

}

}

}

}

foreach (int key in ht.Keys.Cast<int>().OrderBy(x=>x))

{

Console.WriteLine(ht[key]);

}

Console.ReadKey();

}

}

My first attempt, which I believe is correct.

I made a second attempt, but this one fails for n ≥ 5.

how about:

int i = 0;

`for (i=1; i<N; i++)`

{

printf("(%d,%d) ",i,i);

printf("(%d,%d) ",i,i+1);

printf("(%d,%d) ",i+1,i);

if ( i+2 <= N )

{

printf("(%d,%d) ",i,i+2);

printf("(%d,%d)",i+2,i);

}

printf("\n");

}

printf("(%d,%d)\n",N,N);