index to coordinates in diagonal (zigzag) traverse
So I have a 4x4 matrix like so
|0 1 2 3
-+-------
0|0 1 3 6
1|2 4 7 a
2|5 8 b d
3|9 c e f
and I am traversing it in the order specified by the hexadecimal characters in it. so start at (0, 0), then (1, 0), (0, 1), (2, 0), (1, 1), (0, 2)...
So here is the code:
def diagonal(n):
for a in range(n):
for b in range(a + 1):
yield a - b, b
for a in range(n - 1):
for b in range(n - a - 1):
yield n - b - 1, b + 1 + a
iterating through this gives
for x, y in diagonal(4):
print((x, y))
# (0, 0)
# (1, 0)
# (0, 1)
# (2, 0)
# (1, 1)
# (0, 2)
# (3, 0)
# (2, 1)
# (1, 2)
# (0, 3)
# (3, 1)
# (2, 2)
# (1, 3)
# (3, 2)
# (2, 3)
# (3, 3)
which is exactly as I intended. The part I am stuck on is trying to make a function where I give it the index, and it gives me the coordinates. So for my 4x4 matrix,(this is still in hexadecimal)
0 -> (0, 0)
1 -> (1, 0)
2 -> (0, 1)
3 -> (2, 0)
4 -> (1, 1)
5 -> (0, 2)
6 -> (3, 0)
7 -> (2, 1)
8 -> (1, 2)
9 -> (0, 3)
a -> (3, 1)
b -> (2, 2)
c -> (1, 3)
d -> (3, 2)
e -> (2, 3)
f -> (3, 3)
I have intentions to move on to variable sized square matrices so I cannot just hard code these values into a dict.
I have been tinkering for hours trying to get this to work but I cannot for the life of me get it to work.
This is not homework, just something I'm working on in my spare time, and it's slowly driving me up the wall.
If anything is not clear please don't hesitate to ask.
Thanks in advance.
Edit:
I presume someone will comment about this post Traverse Matrix in Diagonal strips which is similar but as with my first function this only iterates over the coordinates, and I cannot work out the coordinates from an index.
python matrix
add a comment |
So I have a 4x4 matrix like so
|0 1 2 3
-+-------
0|0 1 3 6
1|2 4 7 a
2|5 8 b d
3|9 c e f
and I am traversing it in the order specified by the hexadecimal characters in it. so start at (0, 0), then (1, 0), (0, 1), (2, 0), (1, 1), (0, 2)...
So here is the code:
def diagonal(n):
for a in range(n):
for b in range(a + 1):
yield a - b, b
for a in range(n - 1):
for b in range(n - a - 1):
yield n - b - 1, b + 1 + a
iterating through this gives
for x, y in diagonal(4):
print((x, y))
# (0, 0)
# (1, 0)
# (0, 1)
# (2, 0)
# (1, 1)
# (0, 2)
# (3, 0)
# (2, 1)
# (1, 2)
# (0, 3)
# (3, 1)
# (2, 2)
# (1, 3)
# (3, 2)
# (2, 3)
# (3, 3)
which is exactly as I intended. The part I am stuck on is trying to make a function where I give it the index, and it gives me the coordinates. So for my 4x4 matrix,(this is still in hexadecimal)
0 -> (0, 0)
1 -> (1, 0)
2 -> (0, 1)
3 -> (2, 0)
4 -> (1, 1)
5 -> (0, 2)
6 -> (3, 0)
7 -> (2, 1)
8 -> (1, 2)
9 -> (0, 3)
a -> (3, 1)
b -> (2, 2)
c -> (1, 3)
d -> (3, 2)
e -> (2, 3)
f -> (3, 3)
I have intentions to move on to variable sized square matrices so I cannot just hard code these values into a dict.
I have been tinkering for hours trying to get this to work but I cannot for the life of me get it to work.
This is not homework, just something I'm working on in my spare time, and it's slowly driving me up the wall.
If anything is not clear please don't hesitate to ask.
Thanks in advance.
Edit:
I presume someone will comment about this post Traverse Matrix in Diagonal strips which is similar but as with my first function this only iterates over the coordinates, and I cannot work out the coordinates from an index.
python matrix
add a comment |
So I have a 4x4 matrix like so
|0 1 2 3
-+-------
0|0 1 3 6
1|2 4 7 a
2|5 8 b d
3|9 c e f
and I am traversing it in the order specified by the hexadecimal characters in it. so start at (0, 0), then (1, 0), (0, 1), (2, 0), (1, 1), (0, 2)...
So here is the code:
def diagonal(n):
for a in range(n):
for b in range(a + 1):
yield a - b, b
for a in range(n - 1):
for b in range(n - a - 1):
yield n - b - 1, b + 1 + a
iterating through this gives
for x, y in diagonal(4):
print((x, y))
# (0, 0)
# (1, 0)
# (0, 1)
# (2, 0)
# (1, 1)
# (0, 2)
# (3, 0)
# (2, 1)
# (1, 2)
# (0, 3)
# (3, 1)
# (2, 2)
# (1, 3)
# (3, 2)
# (2, 3)
# (3, 3)
which is exactly as I intended. The part I am stuck on is trying to make a function where I give it the index, and it gives me the coordinates. So for my 4x4 matrix,(this is still in hexadecimal)
0 -> (0, 0)
1 -> (1, 0)
2 -> (0, 1)
3 -> (2, 0)
4 -> (1, 1)
5 -> (0, 2)
6 -> (3, 0)
7 -> (2, 1)
8 -> (1, 2)
9 -> (0, 3)
a -> (3, 1)
b -> (2, 2)
c -> (1, 3)
d -> (3, 2)
e -> (2, 3)
f -> (3, 3)
I have intentions to move on to variable sized square matrices so I cannot just hard code these values into a dict.
I have been tinkering for hours trying to get this to work but I cannot for the life of me get it to work.
This is not homework, just something I'm working on in my spare time, and it's slowly driving me up the wall.
If anything is not clear please don't hesitate to ask.
Thanks in advance.
Edit:
I presume someone will comment about this post Traverse Matrix in Diagonal strips which is similar but as with my first function this only iterates over the coordinates, and I cannot work out the coordinates from an index.
python matrix
So I have a 4x4 matrix like so
|0 1 2 3
-+-------
0|0 1 3 6
1|2 4 7 a
2|5 8 b d
3|9 c e f
and I am traversing it in the order specified by the hexadecimal characters in it. so start at (0, 0), then (1, 0), (0, 1), (2, 0), (1, 1), (0, 2)...
So here is the code:
def diagonal(n):
for a in range(n):
for b in range(a + 1):
yield a - b, b
for a in range(n - 1):
for b in range(n - a - 1):
yield n - b - 1, b + 1 + a
iterating through this gives
for x, y in diagonal(4):
print((x, y))
# (0, 0)
# (1, 0)
# (0, 1)
# (2, 0)
# (1, 1)
# (0, 2)
# (3, 0)
# (2, 1)
# (1, 2)
# (0, 3)
# (3, 1)
# (2, 2)
# (1, 3)
# (3, 2)
# (2, 3)
# (3, 3)
which is exactly as I intended. The part I am stuck on is trying to make a function where I give it the index, and it gives me the coordinates. So for my 4x4 matrix,(this is still in hexadecimal)
0 -> (0, 0)
1 -> (1, 0)
2 -> (0, 1)
3 -> (2, 0)
4 -> (1, 1)
5 -> (0, 2)
6 -> (3, 0)
7 -> (2, 1)
8 -> (1, 2)
9 -> (0, 3)
a -> (3, 1)
b -> (2, 2)
c -> (1, 3)
d -> (3, 2)
e -> (2, 3)
f -> (3, 3)
I have intentions to move on to variable sized square matrices so I cannot just hard code these values into a dict.
I have been tinkering for hours trying to get this to work but I cannot for the life of me get it to work.
This is not homework, just something I'm working on in my spare time, and it's slowly driving me up the wall.
If anything is not clear please don't hesitate to ask.
Thanks in advance.
Edit:
I presume someone will comment about this post Traverse Matrix in Diagonal strips which is similar but as with my first function this only iterates over the coordinates, and I cannot work out the coordinates from an index.
python matrix
python matrix
edited Nov 23 '18 at 3:35
Gal Avineri
16210
16210
asked Nov 22 '18 at 23:54
dangee1705dangee1705
2,0241722
2,0241722
add a comment |
add a comment |
2 Answers
2
active
oldest
votes
Here is a function that seems to do what you want. An explanation comes after the code.
from math import sqrt
def triangular(n):
return n * (n + 1) // 2
def coords_from_index(ndx, n=4):
if ndx < triangular(n):
basecol = (int(sqrt(8 * ndx + 1)) - 1) // 2
row = ndx - triangular(basecol)
col = basecol - row
else:
oldcol, oldrow = coords_from_index(n**2 - 1 - ndx, n)
row = n - 1 - oldrow
col = n - 1 - oldcol
return col, row
# Test code
n = 4
for ndx in range(n**2):
print(hex(ndx)[2:], '->', coords_from_index(ndx, n))
The printout from that test code is:
0 -> (0, 0)
1 -> (1, 0)
2 -> (0, 1)
3 -> (2, 0)
4 -> (1, 1)
5 -> (0, 2)
6 -> (3, 0)
7 -> (2, 1)
8 -> (1, 2)
9 -> (0, 3)
a -> (3, 1)
b -> (2, 2)
c -> (1, 3)
d -> (3, 2)
e -> (2, 3)
f -> (3, 3)
Here is a brief explanation of my code.
Like your code to generate the coordinates in order, I treat the upper-left triangle of the square differently from the lower-right triangle. Let's look first at the upper-left triangle, which for the square of size 4 includes the indices 0 through 9.
If you look at the top number in each column, you see that those are the "triangular numbers", which are the sum of consecutive integers starting from 0. So the top row is 0, 0+1, 0+1+2, and 0+1+2+3. The well-known formula for those numbers is
triangular(n) = n * (n + 1) // 2
So I wrote a small routine for that. If you know the triangular number (call it ndx) and want to find n, you can use algebra to solve the quadratic equation and you get
n = (sqrt(8 * ndx + 1) - 1) // 2
If you replace the sqrt with int(sqrt( you get that same result for a triangular number, and you also get the "base" number for any ndx that falls between two triangular numbers. You can then use your index and the "base number" to find the corresponding column and row for the index.
Now, if you look at the lower-right triangle, which includes the indices a through f, you see that there is symmetry with the upper-left triangle. I chose to use that symmetry to calculate the row and column for any of these indices. I could have calculated them more directly, but the approach I used works well.
Note that if you use really large values of n and ndx that the int(sqrt( does not always give the correct answer, due to the vagaries of floating-point arithmetic. But ndx needs to be really large, on the order of 2**50, before that happens. Let me know if you need a more reliable routine to calculate the integer square root.
add a comment |
This is basically an algorithmic question :)
I'd suggest the following algorithm:
- Find in what diagonal the requested index relies
- Find the position of the index on that diagonal
- Infer the coordinate
The implementation i'd suggest is the following:
As a initial step, prepare an array of the first linear index in each diagonal.
for example in a 4x4 matrix the array would look like this: [0, 1, 3, 6, 10, 30, 15].
Notice These are basically bins. This means that all the linear indices that maintain a[i] <= index < a[i+1] are found in the i'th diagonal.
Than, when receiving an index:
Find in which diagonal the requested index is found.
You can do this by finding d which fulfillsbin[d] <= index < bin[d]Find the position on that diagonal.
This is distance between bin[d] and index. This means that if index - bin[d] = k, than the index is in the k'th position on that diagonal.
Infer the coordinate.
Up until now you've found that the index is in the k'th position on the d'th diagonal.
Let us denote the size of the matrix is MxM.
if x <= M-1 than we are on the diagonal (k, d - k)
if x > M-1 than we are on the diagonal (d - (m-1) + k, (m-1) - k)
When we will insert k that we've found earlier we will find the requested coordinate.
Implementation
def ind2cor(index):
d = bisect_left(a, index)
if index != a[d]:
d -= 1
k = index - a[d]
if d <= m-1:
return k, d - k
else:
return d - (m - 1) + k, (m - 1) - k
Example:
m = 4
a = [0, 3, 6, 10, 13, 15]
for i in range(16):
print('{}: {}'.format(i, ind2cor(i)))
Yields:
0: (0, 0)
1: (0, 1)
2: (1, 0)
3: (0, 2)
4: (1, 1)
5: (2, 0)
6: (0, 3)
7: (1, 2)
8: (2, 1)
9: (3, 0)
10: (1, 3)
11: (2, 2)
12: (3, 1)
13: (2, 3)
14: (3, 2)
15: (3, 3)
I can also provide a method to generate the indices of the diagonals:
def genereate_diagonal_indices(m):
a = [0]
for i in range(1, m):
a.append(a[-1] + i)
for i in range(m, 1, -1):
a.append(a[-1] + i)
return a
So you could use a = genereate_diagonal_indices(m) in the code above instead.
Out of interest i compared the complexities of the algorithms Rory and I provided. It seems that in time complexity both Rory's algorithm and mine are O(log(m)), where m is the length of one axis of the matrix. In memory complexity Rory's algorithm is O(1) and mine is Theta(m).
– Gal Avineri
Nov 23 '18 at 10:30
I believe you have misunderstood my algorithm. There is oneifstatement and one resulting recursive call, but those happen at most once and do not increase the algorithmic complexity. Thesqrtoperator is done in O(1) in modern CPUs, I believe, though I could be wrong for some CPUs. As a result, my algorithm does straight calculations and thus is O(1) in both time and memory. Unless you mean thatmis the number of bits of the length of one side of the square?
– Rory Daulton
Nov 23 '18 at 12:12
I might have misunderstood, i'd love to discuss it! I claimed your algorithm has a time complexity of O(log(m)) due to the calculation of the sqrt. I searched the internet for the complexity of calculation of sqrt and found this stackoverflow.com/questions/23103847/… which claims it is O(log(m)). However it might be wrong. Could you support the claim that sqrt time complexity is O(1)? :)
– Gal Avineri
Nov 23 '18 at 16:20
This point is debatable. I could claim that I noted that my code is meant forndx < 2**50, so the limitation makes the code O(1). Instead, I'll give the timing results on my computer. Using%timeitonint(sqrt(n))gives295 nsforn=1,299 nsforn=2,313 nsforn=2**25-1, and371 nsforn=2**50-1. This is worse than my claimed O(1) and better than your claimed O(log(n)). I'm not sure what that is--it looks a little better than O(log(log(n))).
– Rory Daulton
Nov 23 '18 at 16:41
If we assumendx < cthan i could also infer thatm < c2and that would make my algorithm O(1) as well. So for the sake of the argument should we continue without this assumption? Thank you for trying to collect timing results! The results are interesting, but i'm not sure we can reliably try to infer the complexity by them due to many other factors which my act. Therefore i'd like to find the theoretical compelxity. I've searched some more and found this cstheory.stackexchange.com/questions/9706/…
– Gal Avineri
Nov 23 '18 at 17:00
|
show 2 more comments
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53439212%2findex-to-coordinates-in-diagonal-zigzag-traverse%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
Here is a function that seems to do what you want. An explanation comes after the code.
from math import sqrt
def triangular(n):
return n * (n + 1) // 2
def coords_from_index(ndx, n=4):
if ndx < triangular(n):
basecol = (int(sqrt(8 * ndx + 1)) - 1) // 2
row = ndx - triangular(basecol)
col = basecol - row
else:
oldcol, oldrow = coords_from_index(n**2 - 1 - ndx, n)
row = n - 1 - oldrow
col = n - 1 - oldcol
return col, row
# Test code
n = 4
for ndx in range(n**2):
print(hex(ndx)[2:], '->', coords_from_index(ndx, n))
The printout from that test code is:
0 -> (0, 0)
1 -> (1, 0)
2 -> (0, 1)
3 -> (2, 0)
4 -> (1, 1)
5 -> (0, 2)
6 -> (3, 0)
7 -> (2, 1)
8 -> (1, 2)
9 -> (0, 3)
a -> (3, 1)
b -> (2, 2)
c -> (1, 3)
d -> (3, 2)
e -> (2, 3)
f -> (3, 3)
Here is a brief explanation of my code.
Like your code to generate the coordinates in order, I treat the upper-left triangle of the square differently from the lower-right triangle. Let's look first at the upper-left triangle, which for the square of size 4 includes the indices 0 through 9.
If you look at the top number in each column, you see that those are the "triangular numbers", which are the sum of consecutive integers starting from 0. So the top row is 0, 0+1, 0+1+2, and 0+1+2+3. The well-known formula for those numbers is
triangular(n) = n * (n + 1) // 2
So I wrote a small routine for that. If you know the triangular number (call it ndx) and want to find n, you can use algebra to solve the quadratic equation and you get
n = (sqrt(8 * ndx + 1) - 1) // 2
If you replace the sqrt with int(sqrt( you get that same result for a triangular number, and you also get the "base" number for any ndx that falls between two triangular numbers. You can then use your index and the "base number" to find the corresponding column and row for the index.
Now, if you look at the lower-right triangle, which includes the indices a through f, you see that there is symmetry with the upper-left triangle. I chose to use that symmetry to calculate the row and column for any of these indices. I could have calculated them more directly, but the approach I used works well.
Note that if you use really large values of n and ndx that the int(sqrt( does not always give the correct answer, due to the vagaries of floating-point arithmetic. But ndx needs to be really large, on the order of 2**50, before that happens. Let me know if you need a more reliable routine to calculate the integer square root.
add a comment |
Here is a function that seems to do what you want. An explanation comes after the code.
from math import sqrt
def triangular(n):
return n * (n + 1) // 2
def coords_from_index(ndx, n=4):
if ndx < triangular(n):
basecol = (int(sqrt(8 * ndx + 1)) - 1) // 2
row = ndx - triangular(basecol)
col = basecol - row
else:
oldcol, oldrow = coords_from_index(n**2 - 1 - ndx, n)
row = n - 1 - oldrow
col = n - 1 - oldcol
return col, row
# Test code
n = 4
for ndx in range(n**2):
print(hex(ndx)[2:], '->', coords_from_index(ndx, n))
The printout from that test code is:
0 -> (0, 0)
1 -> (1, 0)
2 -> (0, 1)
3 -> (2, 0)
4 -> (1, 1)
5 -> (0, 2)
6 -> (3, 0)
7 -> (2, 1)
8 -> (1, 2)
9 -> (0, 3)
a -> (3, 1)
b -> (2, 2)
c -> (1, 3)
d -> (3, 2)
e -> (2, 3)
f -> (3, 3)
Here is a brief explanation of my code.
Like your code to generate the coordinates in order, I treat the upper-left triangle of the square differently from the lower-right triangle. Let's look first at the upper-left triangle, which for the square of size 4 includes the indices 0 through 9.
If you look at the top number in each column, you see that those are the "triangular numbers", which are the sum of consecutive integers starting from 0. So the top row is 0, 0+1, 0+1+2, and 0+1+2+3. The well-known formula for those numbers is
triangular(n) = n * (n + 1) // 2
So I wrote a small routine for that. If you know the triangular number (call it ndx) and want to find n, you can use algebra to solve the quadratic equation and you get
n = (sqrt(8 * ndx + 1) - 1) // 2
If you replace the sqrt with int(sqrt( you get that same result for a triangular number, and you also get the "base" number for any ndx that falls between two triangular numbers. You can then use your index and the "base number" to find the corresponding column and row for the index.
Now, if you look at the lower-right triangle, which includes the indices a through f, you see that there is symmetry with the upper-left triangle. I chose to use that symmetry to calculate the row and column for any of these indices. I could have calculated them more directly, but the approach I used works well.
Note that if you use really large values of n and ndx that the int(sqrt( does not always give the correct answer, due to the vagaries of floating-point arithmetic. But ndx needs to be really large, on the order of 2**50, before that happens. Let me know if you need a more reliable routine to calculate the integer square root.
add a comment |
Here is a function that seems to do what you want. An explanation comes after the code.
from math import sqrt
def triangular(n):
return n * (n + 1) // 2
def coords_from_index(ndx, n=4):
if ndx < triangular(n):
basecol = (int(sqrt(8 * ndx + 1)) - 1) // 2
row = ndx - triangular(basecol)
col = basecol - row
else:
oldcol, oldrow = coords_from_index(n**2 - 1 - ndx, n)
row = n - 1 - oldrow
col = n - 1 - oldcol
return col, row
# Test code
n = 4
for ndx in range(n**2):
print(hex(ndx)[2:], '->', coords_from_index(ndx, n))
The printout from that test code is:
0 -> (0, 0)
1 -> (1, 0)
2 -> (0, 1)
3 -> (2, 0)
4 -> (1, 1)
5 -> (0, 2)
6 -> (3, 0)
7 -> (2, 1)
8 -> (1, 2)
9 -> (0, 3)
a -> (3, 1)
b -> (2, 2)
c -> (1, 3)
d -> (3, 2)
e -> (2, 3)
f -> (3, 3)
Here is a brief explanation of my code.
Like your code to generate the coordinates in order, I treat the upper-left triangle of the square differently from the lower-right triangle. Let's look first at the upper-left triangle, which for the square of size 4 includes the indices 0 through 9.
If you look at the top number in each column, you see that those are the "triangular numbers", which are the sum of consecutive integers starting from 0. So the top row is 0, 0+1, 0+1+2, and 0+1+2+3. The well-known formula for those numbers is
triangular(n) = n * (n + 1) // 2
So I wrote a small routine for that. If you know the triangular number (call it ndx) and want to find n, you can use algebra to solve the quadratic equation and you get
n = (sqrt(8 * ndx + 1) - 1) // 2
If you replace the sqrt with int(sqrt( you get that same result for a triangular number, and you also get the "base" number for any ndx that falls between two triangular numbers. You can then use your index and the "base number" to find the corresponding column and row for the index.
Now, if you look at the lower-right triangle, which includes the indices a through f, you see that there is symmetry with the upper-left triangle. I chose to use that symmetry to calculate the row and column for any of these indices. I could have calculated them more directly, but the approach I used works well.
Note that if you use really large values of n and ndx that the int(sqrt( does not always give the correct answer, due to the vagaries of floating-point arithmetic. But ndx needs to be really large, on the order of 2**50, before that happens. Let me know if you need a more reliable routine to calculate the integer square root.
Here is a function that seems to do what you want. An explanation comes after the code.
from math import sqrt
def triangular(n):
return n * (n + 1) // 2
def coords_from_index(ndx, n=4):
if ndx < triangular(n):
basecol = (int(sqrt(8 * ndx + 1)) - 1) // 2
row = ndx - triangular(basecol)
col = basecol - row
else:
oldcol, oldrow = coords_from_index(n**2 - 1 - ndx, n)
row = n - 1 - oldrow
col = n - 1 - oldcol
return col, row
# Test code
n = 4
for ndx in range(n**2):
print(hex(ndx)[2:], '->', coords_from_index(ndx, n))
The printout from that test code is:
0 -> (0, 0)
1 -> (1, 0)
2 -> (0, 1)
3 -> (2, 0)
4 -> (1, 1)
5 -> (0, 2)
6 -> (3, 0)
7 -> (2, 1)
8 -> (1, 2)
9 -> (0, 3)
a -> (3, 1)
b -> (2, 2)
c -> (1, 3)
d -> (3, 2)
e -> (2, 3)
f -> (3, 3)
Here is a brief explanation of my code.
Like your code to generate the coordinates in order, I treat the upper-left triangle of the square differently from the lower-right triangle. Let's look first at the upper-left triangle, which for the square of size 4 includes the indices 0 through 9.
If you look at the top number in each column, you see that those are the "triangular numbers", which are the sum of consecutive integers starting from 0. So the top row is 0, 0+1, 0+1+2, and 0+1+2+3. The well-known formula for those numbers is
triangular(n) = n * (n + 1) // 2
So I wrote a small routine for that. If you know the triangular number (call it ndx) and want to find n, you can use algebra to solve the quadratic equation and you get
n = (sqrt(8 * ndx + 1) - 1) // 2
If you replace the sqrt with int(sqrt( you get that same result for a triangular number, and you also get the "base" number for any ndx that falls between two triangular numbers. You can then use your index and the "base number" to find the corresponding column and row for the index.
Now, if you look at the lower-right triangle, which includes the indices a through f, you see that there is symmetry with the upper-left triangle. I chose to use that symmetry to calculate the row and column for any of these indices. I could have calculated them more directly, but the approach I used works well.
Note that if you use really large values of n and ndx that the int(sqrt( does not always give the correct answer, due to the vagaries of floating-point arithmetic. But ndx needs to be really large, on the order of 2**50, before that happens. Let me know if you need a more reliable routine to calculate the integer square root.
edited Nov 23 '18 at 0:56
answered Nov 23 '18 at 0:26
Rory DaultonRory Daulton
13.2k41930
13.2k41930
add a comment |
add a comment |
This is basically an algorithmic question :)
I'd suggest the following algorithm:
- Find in what diagonal the requested index relies
- Find the position of the index on that diagonal
- Infer the coordinate
The implementation i'd suggest is the following:
As a initial step, prepare an array of the first linear index in each diagonal.
for example in a 4x4 matrix the array would look like this: [0, 1, 3, 6, 10, 30, 15].
Notice These are basically bins. This means that all the linear indices that maintain a[i] <= index < a[i+1] are found in the i'th diagonal.
Than, when receiving an index:
Find in which diagonal the requested index is found.
You can do this by finding d which fulfillsbin[d] <= index < bin[d]Find the position on that diagonal.
This is distance between bin[d] and index. This means that if index - bin[d] = k, than the index is in the k'th position on that diagonal.
Infer the coordinate.
Up until now you've found that the index is in the k'th position on the d'th diagonal.
Let us denote the size of the matrix is MxM.
if x <= M-1 than we are on the diagonal (k, d - k)
if x > M-1 than we are on the diagonal (d - (m-1) + k, (m-1) - k)
When we will insert k that we've found earlier we will find the requested coordinate.
Implementation
def ind2cor(index):
d = bisect_left(a, index)
if index != a[d]:
d -= 1
k = index - a[d]
if d <= m-1:
return k, d - k
else:
return d - (m - 1) + k, (m - 1) - k
Example:
m = 4
a = [0, 3, 6, 10, 13, 15]
for i in range(16):
print('{}: {}'.format(i, ind2cor(i)))
Yields:
0: (0, 0)
1: (0, 1)
2: (1, 0)
3: (0, 2)
4: (1, 1)
5: (2, 0)
6: (0, 3)
7: (1, 2)
8: (2, 1)
9: (3, 0)
10: (1, 3)
11: (2, 2)
12: (3, 1)
13: (2, 3)
14: (3, 2)
15: (3, 3)
I can also provide a method to generate the indices of the diagonals:
def genereate_diagonal_indices(m):
a = [0]
for i in range(1, m):
a.append(a[-1] + i)
for i in range(m, 1, -1):
a.append(a[-1] + i)
return a
So you could use a = genereate_diagonal_indices(m) in the code above instead.
Out of interest i compared the complexities of the algorithms Rory and I provided. It seems that in time complexity both Rory's algorithm and mine are O(log(m)), where m is the length of one axis of the matrix. In memory complexity Rory's algorithm is O(1) and mine is Theta(m).
– Gal Avineri
Nov 23 '18 at 10:30
I believe you have misunderstood my algorithm. There is oneifstatement and one resulting recursive call, but those happen at most once and do not increase the algorithmic complexity. Thesqrtoperator is done in O(1) in modern CPUs, I believe, though I could be wrong for some CPUs. As a result, my algorithm does straight calculations and thus is O(1) in both time and memory. Unless you mean thatmis the number of bits of the length of one side of the square?
– Rory Daulton
Nov 23 '18 at 12:12
I might have misunderstood, i'd love to discuss it! I claimed your algorithm has a time complexity of O(log(m)) due to the calculation of the sqrt. I searched the internet for the complexity of calculation of sqrt and found this stackoverflow.com/questions/23103847/… which claims it is O(log(m)). However it might be wrong. Could you support the claim that sqrt time complexity is O(1)? :)
– Gal Avineri
Nov 23 '18 at 16:20
This point is debatable. I could claim that I noted that my code is meant forndx < 2**50, so the limitation makes the code O(1). Instead, I'll give the timing results on my computer. Using%timeitonint(sqrt(n))gives295 nsforn=1,299 nsforn=2,313 nsforn=2**25-1, and371 nsforn=2**50-1. This is worse than my claimed O(1) and better than your claimed O(log(n)). I'm not sure what that is--it looks a little better than O(log(log(n))).
– Rory Daulton
Nov 23 '18 at 16:41
If we assumendx < cthan i could also infer thatm < c2and that would make my algorithm O(1) as well. So for the sake of the argument should we continue without this assumption? Thank you for trying to collect timing results! The results are interesting, but i'm not sure we can reliably try to infer the complexity by them due to many other factors which my act. Therefore i'd like to find the theoretical compelxity. I've searched some more and found this cstheory.stackexchange.com/questions/9706/…
– Gal Avineri
Nov 23 '18 at 17:00
|
show 2 more comments
This is basically an algorithmic question :)
I'd suggest the following algorithm:
- Find in what diagonal the requested index relies
- Find the position of the index on that diagonal
- Infer the coordinate
The implementation i'd suggest is the following:
As a initial step, prepare an array of the first linear index in each diagonal.
for example in a 4x4 matrix the array would look like this: [0, 1, 3, 6, 10, 30, 15].
Notice These are basically bins. This means that all the linear indices that maintain a[i] <= index < a[i+1] are found in the i'th diagonal.
Than, when receiving an index:
Find in which diagonal the requested index is found.
You can do this by finding d which fulfillsbin[d] <= index < bin[d]Find the position on that diagonal.
This is distance between bin[d] and index. This means that if index - bin[d] = k, than the index is in the k'th position on that diagonal.
Infer the coordinate.
Up until now you've found that the index is in the k'th position on the d'th diagonal.
Let us denote the size of the matrix is MxM.
if x <= M-1 than we are on the diagonal (k, d - k)
if x > M-1 than we are on the diagonal (d - (m-1) + k, (m-1) - k)
When we will insert k that we've found earlier we will find the requested coordinate.
Implementation
def ind2cor(index):
d = bisect_left(a, index)
if index != a[d]:
d -= 1
k = index - a[d]
if d <= m-1:
return k, d - k
else:
return d - (m - 1) + k, (m - 1) - k
Example:
m = 4
a = [0, 3, 6, 10, 13, 15]
for i in range(16):
print('{}: {}'.format(i, ind2cor(i)))
Yields:
0: (0, 0)
1: (0, 1)
2: (1, 0)
3: (0, 2)
4: (1, 1)
5: (2, 0)
6: (0, 3)
7: (1, 2)
8: (2, 1)
9: (3, 0)
10: (1, 3)
11: (2, 2)
12: (3, 1)
13: (2, 3)
14: (3, 2)
15: (3, 3)
I can also provide a method to generate the indices of the diagonals:
def genereate_diagonal_indices(m):
a = [0]
for i in range(1, m):
a.append(a[-1] + i)
for i in range(m, 1, -1):
a.append(a[-1] + i)
return a
So you could use a = genereate_diagonal_indices(m) in the code above instead.
Out of interest i compared the complexities of the algorithms Rory and I provided. It seems that in time complexity both Rory's algorithm and mine are O(log(m)), where m is the length of one axis of the matrix. In memory complexity Rory's algorithm is O(1) and mine is Theta(m).
– Gal Avineri
Nov 23 '18 at 10:30
I believe you have misunderstood my algorithm. There is oneifstatement and one resulting recursive call, but those happen at most once and do not increase the algorithmic complexity. Thesqrtoperator is done in O(1) in modern CPUs, I believe, though I could be wrong for some CPUs. As a result, my algorithm does straight calculations and thus is O(1) in both time and memory. Unless you mean thatmis the number of bits of the length of one side of the square?
– Rory Daulton
Nov 23 '18 at 12:12
I might have misunderstood, i'd love to discuss it! I claimed your algorithm has a time complexity of O(log(m)) due to the calculation of the sqrt. I searched the internet for the complexity of calculation of sqrt and found this stackoverflow.com/questions/23103847/… which claims it is O(log(m)). However it might be wrong. Could you support the claim that sqrt time complexity is O(1)? :)
– Gal Avineri
Nov 23 '18 at 16:20
This point is debatable. I could claim that I noted that my code is meant forndx < 2**50, so the limitation makes the code O(1). Instead, I'll give the timing results on my computer. Using%timeitonint(sqrt(n))gives295 nsforn=1,299 nsforn=2,313 nsforn=2**25-1, and371 nsforn=2**50-1. This is worse than my claimed O(1) and better than your claimed O(log(n)). I'm not sure what that is--it looks a little better than O(log(log(n))).
– Rory Daulton
Nov 23 '18 at 16:41
If we assumendx < cthan i could also infer thatm < c2and that would make my algorithm O(1) as well. So for the sake of the argument should we continue without this assumption? Thank you for trying to collect timing results! The results are interesting, but i'm not sure we can reliably try to infer the complexity by them due to many other factors which my act. Therefore i'd like to find the theoretical compelxity. I've searched some more and found this cstheory.stackexchange.com/questions/9706/…
– Gal Avineri
Nov 23 '18 at 17:00
|
show 2 more comments
This is basically an algorithmic question :)
I'd suggest the following algorithm:
- Find in what diagonal the requested index relies
- Find the position of the index on that diagonal
- Infer the coordinate
The implementation i'd suggest is the following:
As a initial step, prepare an array of the first linear index in each diagonal.
for example in a 4x4 matrix the array would look like this: [0, 1, 3, 6, 10, 30, 15].
Notice These are basically bins. This means that all the linear indices that maintain a[i] <= index < a[i+1] are found in the i'th diagonal.
Than, when receiving an index:
Find in which diagonal the requested index is found.
You can do this by finding d which fulfillsbin[d] <= index < bin[d]Find the position on that diagonal.
This is distance between bin[d] and index. This means that if index - bin[d] = k, than the index is in the k'th position on that diagonal.
Infer the coordinate.
Up until now you've found that the index is in the k'th position on the d'th diagonal.
Let us denote the size of the matrix is MxM.
if x <= M-1 than we are on the diagonal (k, d - k)
if x > M-1 than we are on the diagonal (d - (m-1) + k, (m-1) - k)
When we will insert k that we've found earlier we will find the requested coordinate.
Implementation
def ind2cor(index):
d = bisect_left(a, index)
if index != a[d]:
d -= 1
k = index - a[d]
if d <= m-1:
return k, d - k
else:
return d - (m - 1) + k, (m - 1) - k
Example:
m = 4
a = [0, 3, 6, 10, 13, 15]
for i in range(16):
print('{}: {}'.format(i, ind2cor(i)))
Yields:
0: (0, 0)
1: (0, 1)
2: (1, 0)
3: (0, 2)
4: (1, 1)
5: (2, 0)
6: (0, 3)
7: (1, 2)
8: (2, 1)
9: (3, 0)
10: (1, 3)
11: (2, 2)
12: (3, 1)
13: (2, 3)
14: (3, 2)
15: (3, 3)
I can also provide a method to generate the indices of the diagonals:
def genereate_diagonal_indices(m):
a = [0]
for i in range(1, m):
a.append(a[-1] + i)
for i in range(m, 1, -1):
a.append(a[-1] + i)
return a
So you could use a = genereate_diagonal_indices(m) in the code above instead.
This is basically an algorithmic question :)
I'd suggest the following algorithm:
- Find in what diagonal the requested index relies
- Find the position of the index on that diagonal
- Infer the coordinate
The implementation i'd suggest is the following:
As a initial step, prepare an array of the first linear index in each diagonal.
for example in a 4x4 matrix the array would look like this: [0, 1, 3, 6, 10, 30, 15].
Notice These are basically bins. This means that all the linear indices that maintain a[i] <= index < a[i+1] are found in the i'th diagonal.
Than, when receiving an index:
Find in which diagonal the requested index is found.
You can do this by finding d which fulfillsbin[d] <= index < bin[d]Find the position on that diagonal.
This is distance between bin[d] and index. This means that if index - bin[d] = k, than the index is in the k'th position on that diagonal.
Infer the coordinate.
Up until now you've found that the index is in the k'th position on the d'th diagonal.
Let us denote the size of the matrix is MxM.
if x <= M-1 than we are on the diagonal (k, d - k)
if x > M-1 than we are on the diagonal (d - (m-1) + k, (m-1) - k)
When we will insert k that we've found earlier we will find the requested coordinate.
Implementation
def ind2cor(index):
d = bisect_left(a, index)
if index != a[d]:
d -= 1
k = index - a[d]
if d <= m-1:
return k, d - k
else:
return d - (m - 1) + k, (m - 1) - k
Example:
m = 4
a = [0, 3, 6, 10, 13, 15]
for i in range(16):
print('{}: {}'.format(i, ind2cor(i)))
Yields:
0: (0, 0)
1: (0, 1)
2: (1, 0)
3: (0, 2)
4: (1, 1)
5: (2, 0)
6: (0, 3)
7: (1, 2)
8: (2, 1)
9: (3, 0)
10: (1, 3)
11: (2, 2)
12: (3, 1)
13: (2, 3)
14: (3, 2)
15: (3, 3)
I can also provide a method to generate the indices of the diagonals:
def genereate_diagonal_indices(m):
a = [0]
for i in range(1, m):
a.append(a[-1] + i)
for i in range(m, 1, -1):
a.append(a[-1] + i)
return a
So you could use a = genereate_diagonal_indices(m) in the code above instead.
edited Nov 23 '18 at 16:53
answered Nov 23 '18 at 1:07
Gal AvineriGal Avineri
16210
16210
Out of interest i compared the complexities of the algorithms Rory and I provided. It seems that in time complexity both Rory's algorithm and mine are O(log(m)), where m is the length of one axis of the matrix. In memory complexity Rory's algorithm is O(1) and mine is Theta(m).
– Gal Avineri
Nov 23 '18 at 10:30
I believe you have misunderstood my algorithm. There is oneifstatement and one resulting recursive call, but those happen at most once and do not increase the algorithmic complexity. Thesqrtoperator is done in O(1) in modern CPUs, I believe, though I could be wrong for some CPUs. As a result, my algorithm does straight calculations and thus is O(1) in both time and memory. Unless you mean thatmis the number of bits of the length of one side of the square?
– Rory Daulton
Nov 23 '18 at 12:12
I might have misunderstood, i'd love to discuss it! I claimed your algorithm has a time complexity of O(log(m)) due to the calculation of the sqrt. I searched the internet for the complexity of calculation of sqrt and found this stackoverflow.com/questions/23103847/… which claims it is O(log(m)). However it might be wrong. Could you support the claim that sqrt time complexity is O(1)? :)
– Gal Avineri
Nov 23 '18 at 16:20
This point is debatable. I could claim that I noted that my code is meant forndx < 2**50, so the limitation makes the code O(1). Instead, I'll give the timing results on my computer. Using%timeitonint(sqrt(n))gives295 nsforn=1,299 nsforn=2,313 nsforn=2**25-1, and371 nsforn=2**50-1. This is worse than my claimed O(1) and better than your claimed O(log(n)). I'm not sure what that is--it looks a little better than O(log(log(n))).
– Rory Daulton
Nov 23 '18 at 16:41
If we assumendx < cthan i could also infer thatm < c2and that would make my algorithm O(1) as well. So for the sake of the argument should we continue without this assumption? Thank you for trying to collect timing results! The results are interesting, but i'm not sure we can reliably try to infer the complexity by them due to many other factors which my act. Therefore i'd like to find the theoretical compelxity. I've searched some more and found this cstheory.stackexchange.com/questions/9706/…
– Gal Avineri
Nov 23 '18 at 17:00
|
show 2 more comments
Out of interest i compared the complexities of the algorithms Rory and I provided. It seems that in time complexity both Rory's algorithm and mine are O(log(m)), where m is the length of one axis of the matrix. In memory complexity Rory's algorithm is O(1) and mine is Theta(m).
– Gal Avineri
Nov 23 '18 at 10:30
I believe you have misunderstood my algorithm. There is oneifstatement and one resulting recursive call, but those happen at most once and do not increase the algorithmic complexity. Thesqrtoperator is done in O(1) in modern CPUs, I believe, though I could be wrong for some CPUs. As a result, my algorithm does straight calculations and thus is O(1) in both time and memory. Unless you mean thatmis the number of bits of the length of one side of the square?
– Rory Daulton
Nov 23 '18 at 12:12
I might have misunderstood, i'd love to discuss it! I claimed your algorithm has a time complexity of O(log(m)) due to the calculation of the sqrt. I searched the internet for the complexity of calculation of sqrt and found this stackoverflow.com/questions/23103847/… which claims it is O(log(m)). However it might be wrong. Could you support the claim that sqrt time complexity is O(1)? :)
– Gal Avineri
Nov 23 '18 at 16:20
This point is debatable. I could claim that I noted that my code is meant forndx < 2**50, so the limitation makes the code O(1). Instead, I'll give the timing results on my computer. Using%timeitonint(sqrt(n))gives295 nsforn=1,299 nsforn=2,313 nsforn=2**25-1, and371 nsforn=2**50-1. This is worse than my claimed O(1) and better than your claimed O(log(n)). I'm not sure what that is--it looks a little better than O(log(log(n))).
– Rory Daulton
Nov 23 '18 at 16:41
If we assumendx < cthan i could also infer thatm < c2and that would make my algorithm O(1) as well. So for the sake of the argument should we continue without this assumption? Thank you for trying to collect timing results! The results are interesting, but i'm not sure we can reliably try to infer the complexity by them due to many other factors which my act. Therefore i'd like to find the theoretical compelxity. I've searched some more and found this cstheory.stackexchange.com/questions/9706/…
– Gal Avineri
Nov 23 '18 at 17:00
Out of interest i compared the complexities of the algorithms Rory and I provided. It seems that in time complexity both Rory's algorithm and mine are O(log(m)), where m is the length of one axis of the matrix. In memory complexity Rory's algorithm is O(1) and mine is Theta(m).
– Gal Avineri
Nov 23 '18 at 10:30
Out of interest i compared the complexities of the algorithms Rory and I provided. It seems that in time complexity both Rory's algorithm and mine are O(log(m)), where m is the length of one axis of the matrix. In memory complexity Rory's algorithm is O(1) and mine is Theta(m).
– Gal Avineri
Nov 23 '18 at 10:30
I believe you have misunderstood my algorithm. There is one
if statement and one resulting recursive call, but those happen at most once and do not increase the algorithmic complexity. The sqrt operator is done in O(1) in modern CPUs, I believe, though I could be wrong for some CPUs. As a result, my algorithm does straight calculations and thus is O(1) in both time and memory. Unless you mean that m is the number of bits of the length of one side of the square?– Rory Daulton
Nov 23 '18 at 12:12
I believe you have misunderstood my algorithm. There is one
if statement and one resulting recursive call, but those happen at most once and do not increase the algorithmic complexity. The sqrt operator is done in O(1) in modern CPUs, I believe, though I could be wrong for some CPUs. As a result, my algorithm does straight calculations and thus is O(1) in both time and memory. Unless you mean that m is the number of bits of the length of one side of the square?– Rory Daulton
Nov 23 '18 at 12:12
I might have misunderstood, i'd love to discuss it! I claimed your algorithm has a time complexity of O(log(m)) due to the calculation of the sqrt. I searched the internet for the complexity of calculation of sqrt and found this stackoverflow.com/questions/23103847/… which claims it is O(log(m)). However it might be wrong. Could you support the claim that sqrt time complexity is O(1)? :)
– Gal Avineri
Nov 23 '18 at 16:20
I might have misunderstood, i'd love to discuss it! I claimed your algorithm has a time complexity of O(log(m)) due to the calculation of the sqrt. I searched the internet for the complexity of calculation of sqrt and found this stackoverflow.com/questions/23103847/… which claims it is O(log(m)). However it might be wrong. Could you support the claim that sqrt time complexity is O(1)? :)
– Gal Avineri
Nov 23 '18 at 16:20
This point is debatable. I could claim that I noted that my code is meant for
ndx < 2**50, so the limitation makes the code O(1). Instead, I'll give the timing results on my computer. Using %timeit on int(sqrt(n)) gives 295 ns for n=1, 299 ns for n=2, 313 ns for n=2**25-1, and 371 ns for n=2**50-1. This is worse than my claimed O(1) and better than your claimed O(log(n)). I'm not sure what that is--it looks a little better than O(log(log(n))).– Rory Daulton
Nov 23 '18 at 16:41
This point is debatable. I could claim that I noted that my code is meant for
ndx < 2**50, so the limitation makes the code O(1). Instead, I'll give the timing results on my computer. Using %timeit on int(sqrt(n)) gives 295 ns for n=1, 299 ns for n=2, 313 ns for n=2**25-1, and 371 ns for n=2**50-1. This is worse than my claimed O(1) and better than your claimed O(log(n)). I'm not sure what that is--it looks a little better than O(log(log(n))).– Rory Daulton
Nov 23 '18 at 16:41
If we assume
ndx < c than i could also infer that m < c2 and that would make my algorithm O(1) as well. So for the sake of the argument should we continue without this assumption? Thank you for trying to collect timing results! The results are interesting, but i'm not sure we can reliably try to infer the complexity by them due to many other factors which my act. Therefore i'd like to find the theoretical compelxity. I've searched some more and found this cstheory.stackexchange.com/questions/9706/…– Gal Avineri
Nov 23 '18 at 17:00
If we assume
ndx < c than i could also infer that m < c2 and that would make my algorithm O(1) as well. So for the sake of the argument should we continue without this assumption? Thank you for trying to collect timing results! The results are interesting, but i'm not sure we can reliably try to infer the complexity by them due to many other factors which my act. Therefore i'd like to find the theoretical compelxity. I've searched some more and found this cstheory.stackexchange.com/questions/9706/…– Gal Avineri
Nov 23 '18 at 17:00
|
show 2 more comments
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53439212%2findex-to-coordinates-in-diagonal-zigzag-traverse%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown