How can I convert 1D array into 2D matrix on MATLAB? - arrays

I want to make heatmap with 1D array(s), this is my plan;
Let assume 4 points of center and each has array,
[center #1, L U] = {0, 1, 2, 5, 10, 7, 4, 2, 1, 0} *L R U D = Left, Right, Up, Down
[center #2, R U] = {0, 1, 1, 4, 12, 7, 5, 3, 2, 1}
[center #3, L D] = {0, 1, 3, 4, 11, 7, 4, 2, 1, 0}
[center #4, R D] = {0, 1, 3, 6, 11, 6, 5, 3, 1, 1}
And when 5th index of heatmap, ([#1]=10, [#2]=12, [#3]=11, [#4]=11) heatmap needs to be like this image.
Heatmap image
Also can predict heatmap is all blue when 1st index ([#1]=0, [#2]=0, [#3]=0, [#4]=0) and only right side has color that almost blue when last index. ([#1]=0, [#2]=1, [#3]=0, [#4]=1)
How can I get 2D matrix from 1D arrays on Matlab? Decreasing values from center can be linear or whatever.

Based on your example you wish to produce always 4 n * n matrices, where the center point of each matrix gets the value in your arrays and all its 4-neighbors get a decreasing value until zero. Did I get it right?
Does this create one of the four matrices you wished to create? If so, just modify the parameters and make four matrices and draw them together
% your matrix size
size = 15
center = (size + 1) / 2
center_value = 5
mat_a = zeros(size,size);
mat_a(center,center) = center_value;
%loop all values until zero
for ii=1:center_value -1
current_value = center_value - ii;
update_mat = mat_a;
% loop over matrix, check if 4-neighbors non-zero
for x =1:size
for y =1:size
if ( mat_a(y,x) == 0 )
has_non_zero_neighbor = false;
% case 1
if ( x < size)
if (mat_a(y,x+1) > 0)
has_non_zero_neighbor = true;
endif
endif
% case 2
if ( y < size)
if (mat_a(y+1,x) > 0)
has_non_zero_neighbor = true;
endif
endif
%case 3
if ( x > 1)
if (mat_a(y,x-1) > 0)
has_non_zero_neighbor = true;
endif
endif
% case 4
if ( y > 1)
if (mat_a(y-1,x) > 0)
has_non_zero_neighbor = true;
endif
endif
%if non-zeros, update matrix item value to current value
if (has_non_zero_neighbor == true)
update_mat(y,x) = current_value;
endif
endif
end
end
mat_a = update_mat;
end
figure(1)
imshow(mat_a./center_value)

Related

Minimum number of operations to make two arrays equal

Given 2 arrays of integers, A and B, an operation on array B is defined as follows:
B[i] = B[i]+2 and B[j] = B[j]-2, where i != j
i and j can be any indices and the above operation can be performed
any number of times such that i and j are not equal
a valid operation consists of both the addition and subtraction steps, both parts are mandatory
The array is considered equal if the frequency of all the elements is same, the array need not be ordered, find the minimum operations required
Input:
A = [ 2, 10, 14 ]
B = [ 6, 2, 18 ]
Output: 2
Explanation :
1st operation: select i=0; j=2;
B[i] += 2 i.e B[0]=8;
B[j] -= 2 i.e B[2] = 16;
B after 1st operation [8,2,16]
2nd operation: select i=0; j=2;
B[i] += 2 i.e B[0]=10;
B[j] -= 2 i.e B[2] = 14;
B after 2nd operation [10,2,14]
Order doesnt matter, so we have made the arrays equal return 2;
I am unable get an approach to solve this and couldn't find any similar questions, so posting this here, thanks in advance.
Assuming the arrays are solvable, then sort the arrays (by parity, and then by value), add up the absolute value of the deltas and divide by 4.
E.g.
A = [ 2, 10, 14 ], already sorted
B = [ 6, 2, 18 ], sorted becomes [2, 6, 18]
Sum of absolute value of deltas = 0 + 4 + 4 = 8. 8/4 = 2 so 2 moves.
A = [2, 10, 14]( % 2 == 0)
B = [2, 6, 18]( % 2 == 0)
another example
A = [1, 2, 5] -> [1, 5]( % 2 == 1) & [2]( % 2 == 0)
B = [1, 3, 4] -> [1, 3]( % 2 == 1) & [4]( % 2 == 0)
Notice that (a + k) mod k == a.
Assuming we already have a sorted array.
We divide the array into k parts, according to the mod k value of the element, then we sum all absolute differences, it's four times the answer.
k = 2
A.sort(key=lambda x: x % k)
B.sort(key=lambda x: x % k)
result = 0
n = len(A)
for i in range(n):
result += abs(A[i] - B[i])
print(result >> 2)
# A = [1, 2, 5]
# B = [1, 3, 4]
# result = 1
# A = [2, 10, 14]
# B = [6, 2, 18]
# result = 2
O(N log N) because of sorting.

Ruby Increment array from certain starting point

I feel this is a super simple query but I'm having a real tough time with immutable nums in my arrays.
I'd like to have a super simple method, which increments numbers in an array by distributing them from the max value.
eg [1,3,5,1] becomes [1,3,0,1] and then iterates upwards and back through to create [2,4,1,3]
what I currently have is the following
arr = [1,3,5,1]
with a method of
def increment_from_max_value(arr)
max = arr.max
max_index = arr.index(max)
arr[max_index] = 0
while max >= 0
arr[max_index..-1].each do |element|
element = element += 1
max = max -= 1
end
end
end
Currently the array isn't even updating and just returns the original values. Which I believe is due to the immutability of FixNums in Ruby, but with a method like map!, which is able to modify those values, I can't get it to loop back through from a certain starting point like each will.
Many thanks in advance
I'd use divmod to calculate the increase for each element and the leftover.
For a max value of 5 and array size of 4 you'd get:
5.divmod(4) #=> [1, 1]
i.e. each element has to incremented by 1 (first value) and 1 element (second value) has to be incremented by another 1.
Another example for a max value of 23 and 4 elements:
[1, 3, 23, 1]
23.divmod(4) #=> [5, 3]
each element has to be incremented by 5 and 3 elements have to be incremented by another 1:
[ 1, 3, 23, 1]
# +5 +5 +5 +5
# +1 +1 +1
# = [ 7, 9, 5, 7]
Applied to your method:
def increment_from_max_value(arr)
max = arr.max
max_index = arr.index(max)
arr[max_index] = 0
q, r = max.divmod(arr.size)
arr.each_index { |j| arr[j] += q }
r.times { |j| arr[(max_index + j + 1) % arr.size] += 1 }
end
arr.each_index { |j| arr[j] += q } simply adds q to each element.
r.times { |j| arr[(max_index + j + 1) % arr.size] += 1 } is a little more complicated. It distributes the remainder, starting from 1 after max_index. The modulo operation ensures that the index will wrap around:
0 % 4 #=> 0
1 % 4 #=> 1
2 % 4 #=> 2
3 % 4 #=> 3
4 % 4 #=> 0
5 % 4 #=> 1
6 % 4 #=> 2
# ...
I think there is something wrong with the while loop. I did not investigate but you can see that this line arr[max_index] = 0 mutates the array.
I don't know if I've understood the logic, but this should return the desired output:
def increment_from_max_value(arr)
max = arr.max
arr[arr.index(max)] = 0
indexes = (0...arr.size).to_a.reverse
max.times do
arr[indexes.first] += 1
indexes.rotate!
end
end
The value of the block variable element in arr[max_index..-1].each do |element| is changed by element = element += 1 but that has no effect on arr.
You could achieve your objective as follows.
def increment_from_max_value(arr)
mx, mx_idx = arr.each_with_index.max_by(&:first)
sz = arr.size
arr[mx_idx] = 0
arr.rotate!(mx_idx + 1).map!.with_index do |e,i|
begin_nbr = mx - i
e += (begin_nbr <= 0 ? 0 : 1 + ((begin_nbr - 1)/sz))
end.rotate!(-mx_idx - 1)
end
arr = [1,3,5,1]
increment_from_max_value(arr)
arr
#=> [2, 4, 1, 3]
arr = [1,2,3,2,1]
increment_from_max_value(arr)
arr
#=> [2, 2, 0, 3, 2]
After computing the maximum value of arr, mx, and its index, mx_idx, and setting arr[mx_idx] to zero, I rotate the array (counter-clockwise) by mx_idx + 1, making the position of mx last. That way the "allocations" of mx begin with the first element of the rotated array. After performing the allocations I then rotate the array clockwise by the same mx_idx + 1.
begin_nbr equals mx minus the number of indices that precede i; in effect, the portion of mx that remains "unallocated" at index i in the first round of allocations.
I can best explain how this works by salting the method with puts statements.
def increment_from_max_value(arr)
mx, mx_idx = arr.each_with_index.max_by(&:first)
sz = arr.size
puts "mx = #{mx}, mx_idx = #{mx_idx}, sz = #{sz}"
arr[mx_idx] = 0
arr.rotate!(mx_idx + 1).
tap { |a| puts "arr rotated to make mx position last = #{a}" }.
map!.with_index do |e,i|
begin_nbr = mx - i
puts "e before = #{e}, i = #{i}, begin_nbr = #{begin_nbr}"
e += (begin_nbr <= 0 ? 0 : 1 + ((begin_nbr - 1)/sz))
e.tap { |f| puts "e after change = #{f}" }
end.
tap { |a| puts "arr after changes = #{a}" }.
rotate!(-mx_idx - 1).
tap { |a| puts "arr after rotating back = #{a}" }
end
arr = [1,3,5,1]
increment_from_max_value(arr)
mx = 5, mx_idx = 2, sz = 4
arr rotated to make mx position last = [1, 1, 3, 0]
e before = 1, i = 0, begin_nbr = 5
e after change = 3
e before = 1, i = 1, begin_nbr = 4
e after change = 2
e before = 3, i = 2, begin_nbr = 3
e after change = 4
e before = 0, i = 3, begin_nbr = 2
e after change = 1
arr after changes = [3, 2, 4, 1]
arr after rotating back = [2, 4, 1, 3]
#=> [2, 4, 1, 3]

Removing elements from an array

Problem:
I have two arrays A and B:
A = [0, 1, 2, 3]; %A will always be from 0 to N where N in this case is 3.
B = [0, 1, 3, 1, 9, 4, 6, 2, 5, 9, 10, 11, 3, 8, 1, 5, 9, 10];
weights_B = [3, 4, 5, 6];
I want to compare the first element of A to the first 3 elements of B and the second element of A to the next 4 elements of B. If the elements of A are equal I remove it from B. So in example:
if (A(1) == B(1:3))
remove A(1) from B
Similarly,
I want to compare A(2) to the next 4 elements of B i.e. to B(4:7):
if (A(2) == B(4:7))
remove A(2) from B
I want to compare A(3) to the next 5 elements of B i.e. to B(8:12)
if (A(3) == B(8:12))
remove A(3) from B
I want to compare A(4) to the next 6 elements of B i.e. to B(13:18)
if (A(4) == B(13:18))
remove A(4) from B
Note: The array weights_B determines the number of elements in B that should be respectively compared to A(1), A(2), .. , A(4)
So in the end B should have the following elements:
B = [1, 3, 9, 4, 6, 5, 9, 10, 11, 8, 1, 5, 9, 10];
Needed Solution:
Is there any way I can do this without having to hard-code the indices?
Here's a way without hard-coding:
Bw = mat2cell(B, 1, weights_B); % split into chunks
result = cell(size(Bw)); % initiallize result
for k = 1: numel(A)
result{k} = Bw{k}(Bw{k}~=A(k)); % fill each chunk of the result
end
result = [result{:}]; % concatenate into a row vector
For the sake of diversity, here's a way to do this using splitapply:
function out = q50982235
A = 0:3;
B = [0, 1, 3, 1, 9, 4, 6, 2, 5, 9, 10, 11, 3, 8, 1, 5, 9, 10];
weights_B = [3, 4, 5, 6];
a_ind = 0; % acts as a "global" variable for the inner function
G = repelem( 1:numel(weights_B), weights_B ); % this creates a vector of groups
out = cell2mat( splitapply(#movdif, B, G) );
function out = movdif(B)
a_ind = a_ind + 1;
out = {B(B ~= A(a_ind))};
end
end
The above works because the order of processed groups is predictable.
This solution requires R2015b.
Try this
A = [0, 1, 2, 3];
B = [0, 1, 3, 1, 9, 4, 6, 2, 5, 9, 10, 11, 3, 8, 1, 5, 9, 10];
weights_B = A + A(end);
border_0 = zeros(size(A));
border_1 = zeros(size(A));
border_0(1) = 1;
border_1(end) = length(B);
for i= 2:length(A)
border_0(i) = border_0(i-1) + weights_B(i-1);
border_1(i-1) = border_0(i)-1;
end
C = [];
for i= 1:length(border_0)
shift = 0;
if (i > 1)
shift = border_1(i-1);
end
C = [C B( find(B(border_0(i):border_1(i))~=A(i)) + shift )]
end
A = [0, 1];
B = [0, 1, 3, 1, 4, 5, 6];
% Split B into cells
C{1} = B(1:3) ; % this can be coded if more splits are required
C{2} = B(4:end) ;
% removing the lements
for i = 1:2
C{i}(C{i}==A(i))=[] ; % remove the elements in C{i} present in A(i)
end
cell2mat(C)
Since you want to compare the elements of A with first 3 and then 4 elements of B respectively, you would need to involve indexes.
You could simply use loop for it.
for(int i=0;i<B.length;i++){
if((A[0]==B[i])&&i<3){
B[i]=B[i+1];
}
else if((A[0]==B[i])&&i>3){}
B[i]=B[i+1];
}
Then adjust the updated size of array B.

How to calculate the sum of specific Array Elements using vb.net

I have two arrays
x = [1 1 1 0 2 3 1 1]
y = [1 2 3 4 5 6 7 8]
How to calculate the sum of y's elements for all x(i) = 1 to get the result 1+2+3+7+8 ?
I have used for loop and if then method to calculate the sum value like this
if x(i) = 1 then sum = sum + y(i)
Have other methods to get the results (sum, average, count ...) ?
Thank you.
Since you only want to sum the numbers in y corresponding to 1s in x, but not 0s in x, you can multiply x * y which looks a bit cleaner than the If. Here are a few ways
Dim x = {1, 1, 1, 0, 0, 0, 1, 1}
Dim y = {1, 2, 3, 4, 5, 6, 7, 8}
' using a for loop
Dim sum1 As Integer = 0
For i = 0 To x.Length - 1
sum1 += If(x(i) = 1, 1, 0) * y(i)
Next
Console.WriteLine(sum1)
' using LINQ #1
Dim sum2 As Integer = x.Select(Function(i, index) If(i = 1, 1, 0) * y(index)).Sum()
Console.WriteLine(sum2)
' using LINQ #2
Dim sum3 As Integer = x.Zip(y, Function(x1, y1) If(x1 = 1, 1, 0) * y1).Sum()
Console.WriteLine(sum3)
' using LINQ #3
Dim sum4 As Integer = Enumerable.Range(0, x.Length).Sum(Function(i) If(x(i) = 1, 1, 0) * y(i))
Console.WriteLine(sum4)
Console.ReadLine()
The For is very clear so I don't know why you wouldn't use it, but you can use LINQ for this as well:
Sub Main
Dim x = {1, 1, 1, 0, 2, 3, 1, 1}
Dim y = {1, 2, 3, 4, 5, 6, 7, 8}
Dim sum = y.Where(Function(v, i) x(i) = 1).Sum()
Console.WriteLine("Sum is {0}", sum)
End Sub
Prints
Sum is 21

3d array traversal originating from center

I'm trying to find a traversal order for a 3d array with uniform dimension n.The traversal order should hereby be sorted ascending by it's distance to the cube's center (order of cells with equal indices is arbitrary).
Example for a 2d-Array:
7 4 8
3 0 1
6 2 5
Which basically is the distance in Manhattan metric:
2 1 2
1 0 1
2 1 2
Traversal as relative coordinates with respect to the origin:
[ 0, 0]
[ 1, 0]
[ 0,-1]
[-1, 0]
[ 0, 1]
[ 1,-1]
[-1,-1]
[-1, 1]
[ 1, 1]
I know of some ways to solve this, for instance precalculating all indices and sorting them according to their distance to the origin. However, since this algorithm is intended to perform on GPU, there are some restrictions:
No recursion (I'm aware of the possibility resolving recursion into
an iterative algorithm - maintaining a stack is however not a
suitable solution in my experience)
No offline calculation (= calculation on the CPU and transferring the result to the GPU). The solution needs to be as flexible as
possible
While searching for solutions I stumbled upon this question, which is exactly the problem I tend to solve, the accepted answer albeit envolves a tree structure, which does not fit the specified requirements: 3D Array Traversal in Different Order
I also thought of a way to create the indices using spherical coordinates, which unfortunately does not yield the correct order. What is an appropriate algorithm to generate the given traversal order for 3d-arrays?
Edit: Stormwind provided an excellent alternative description for the given Problem: "[The problem] is actually about converting addressing from one space to another. Converting between a 1-dimensional and 2-dimensional is simple, like 1,2,3,... to (1,1),(1,2)(2,1)... But this is more like converting from an ascending 1-dimensional (or at least square) to an "ascending octahedron layered" space, when "ascending" means "innermost layer first" in addition to an existing (though arbitrary) incrementing order at each layer surface."
After thinking for a while I've came up with an idea to represent 3d array as a sequence of nodes with directions: +i, -i, +j, -j, +k, -k.
Approach
For 2-dimensional array it would be sufficient to have only three rules:
Each iteration over each node moves it along its axis in its direction, i.e. node +j will increment 2nd index, node -i will decrement 1st index.
There are two kinds of nodes: Main and Secondary. Main axis have one index 0. Each iteration over Main i and j axis nodes (I'll call them I and J) produces Secondary node rotated clockwise 90 degrees:
+I -> -j
-J -> -i
-I -> +j
+J -> +i
Each node has lifetime which decrements every iteration. Lifetime of the node equals (n-1)/2 for odd values of n (for even values see code below). After lifetime goes to 0 the node should be removed.
To enable 3rd dimension another rule should be applied:
Third type of nodes with direction along k axis (here - depth) produces set of I and J axis in each iteration:
+K -> +I, -J, -I, +J
-K -> +I, -J, -I, +J
Here is how it will look:
With such approach elemnts will be automatically sorted by Manhattan distance as in Arturo Menchaca solution.
Implementation
Here is python code that does what I've described. There is a lot of space for improvements this is just a proof of concept. It has no sorting, no recursion and I don't see any offline calculations.
It also contains few tests. Run
NO = ( 0, 0, 0, 2, 0)
Pi = (+1, 0, 0, 0, 0)
PI = (+1, 0, 0, 0, 1)
Pj = ( 0,+1, 0, 0, 0)
PJ = ( 0,+1, 0, 0, 1)
PK = ( 0, 0,+1, 0, 2)
Mi = (-1, 0, 0, 1, 0)
MI = (-1, 0, 0, 1, 1)
Mj = ( 0,-1, 0, 1, 0)
MJ = ( 0,-1, 0, 1, 1)
MK = ( 0, 0,-1, 1, 2)
# i j k ^ ^
# | Id for comparison
# Lifetime index
PRODUCE = {
PI: [ Mj ], # +I -> -j
MJ: [ Mi ], # -J -> -i
MI: [ Pj ], # -I -> +j
PJ: [ Pi ], # +J -> +i
NO: [ NO ],
Pi: [ NO ],
Pj: [ NO ],
Mi: [ NO ],
Mj: [ NO ],
PK: [ PI, MI, PJ, MJ ], # +K -> +I, -J, -I, +J
MK: [ PI, MI, PJ, MJ ], # -K -> +I, -J, -I, +J
}
class Index:
LastDistance = 0
NumberOfVisits = 0
MinIndex = 0
MaxIndex = 0
def __init__(self, i, j, k, lifetime, direction):
self.globalLifetime = lifetime
self.direction = direction
# Assign parent's position
self.i = i
self.j = j
self.k = k
# Step away from parent
self.lifetime = lifetime[direction[3]]
self.step()
def isLive(self):
return self.lifetime > 0
def visit(self):
Index.NumberOfVisits += 1
distance = self.distance()
if distance < Index.LastDistance:
raise NameError("Order is not preserved")
Index.LastDistance = distance
Index.MinIndex = min(self.i, Index.MinIndex)
Index.MinIndex = min(self.j, Index.MinIndex)
Index.MinIndex = min(self.k, Index.MinIndex)
Index.MaxIndex = max(self.i, Index.MaxIndex)
Index.MaxIndex = max(self.j, Index.MaxIndex)
Index.MaxIndex = max(self.k, Index.MaxIndex)
print("[{}, {}, {}]".format(self.i, self.j, self.k))
def step(self):
# Move in your direction
self.i += self.direction[0]
self.j += self.direction[1]
self.k += self.direction[2]
def iterate(self):
self.lifetime -= 1
def produce(self, result):
for direction in PRODUCE[self.direction]:
self.create(direction, result)
def create(self, direction, result):
index = Index(self.i, self.j, self.k, self.globalLifetime, direction)
if index.isLive():
result.append(index)
def distance(self):
# Manhattan Distance
return abs(self.i) + abs(self.j) + abs(self.k)
def Traverse(N):
TotalNumber = N*N*N
halfA = halfB = (N-1)/2
if N % 2 == 0:
halfA = N/2
halfB = N/2-1
MinIndex = -min(halfB, halfA)
MaxIndex = max(halfB, halfA)
lifetime = (halfA, halfB, 0)
SecondaryNodes = []
MainNodes = []
KNodes = []
# visit center
center = Index(0, 0, 0, lifetime, NO)
center.visit()
center.create(PI, MainNodes)
center.create(MI, MainNodes)
center.create(PJ, MainNodes)
center.create(MJ, MainNodes)
center.create(PK, KNodes)
center.create(MK, KNodes)
while len(SecondaryNodes) + len(MainNodes) + len(KNodes) > 0:
# First - visit all side nodes
temp = []
for m in SecondaryNodes:
m.visit()
m.step()
m.iterate()
# Save node only if it is alive
if m.isLive():
temp.append(m)
SecondaryNodes = temp
# Second - visit all main nodes as they may produce secondary nodes
temp = []
for m in MainNodes:
m.visit() # 1 - Visit
m.produce(SecondaryNodes) # 2 - Produce second
m.step() # 3 - Step
m.iterate() # 4 - Lose a life
if m.isLive():
temp.append(m)
MainNodes = temp
# Third - visit all K nodes as they may produce main nodes
temp = []
for m in KNodes:
m.visit()
m.produce(MainNodes)
m.step()
m.iterate()
if m.isLive():
temp.append(m)
KNodes = temp
if TotalNumber != Index.NumberOfVisits:
raise NameError("Number of visited elements does not match {}/{}".format(Index.NumberOfVisits, TotalNumber))
if MinIndex != Index.MinIndex:
raise NameError("Minimal index is out of bounds {}/{}".format(Index.MinIndex, MinIndex))
if MaxIndex != Index.MaxIndex:
raise NameError("Maximal index is out of bounds {}/{}".format(Index.MaxIndex, MaxIndex))
Traverse(6)
Implementation Simplified
Helper class to store index:
class Index:
def __init__(self, i, j, k, lifetime):
self.i = i
self.j = j
self.k = k
self.lifetime = lifetime
def visit(self):
print("[{}, {}, {}]".format(self.i, self.j, self.k))
Set of functions to iterate Main nodes in proper direction:
def StepMainPlusI(mainPlusI, minusJ, lifetime):
result = []
for m in mainPlusI:
if lifetime > 0:
minusJ.append(Index(m.i, m.j-1, m.k, lifetime))
m.lifetime -= 1
m.i += 1
if m.lifetime > 0:
result.append(m)
return result
def StepMainMinusJ(mainMinusJ, minusI, lifetime):
result = []
for m in mainMinusJ:
if lifetime > 0:
minusI.append(Index(m.i-1, m.j, m.k, lifetime))
m.lifetime -= 1
m.j -= 1
if m.lifetime > 0:
result.append(m)
return result
def StepMainMinusI(mainMinusI, plusJ, lifetime):
result = []
for m in mainMinusI:
if lifetime > 0:
plusJ.append(Index(m.i, m.j+1, m.k, lifetime))
m.lifetime -= 1
m.i -= 1
if m.lifetime > 0:
result.append(m)
return result
def StepMainPlusJ(mainPlusJ, plusI, lifetime):
result = []
for m in mainPlusJ:
if lifetime > 0:
plusI.append(Index(m.i+1, m.j, m.k, lifetime))
m.lifetime -= 1
m.j += 1
if m.lifetime > 0:
result.append(m)
return result
Set of functions to iterate a third dimensional K nodes:
def StepMainPlusK(mainPlusK, mainPlusI, mainMinusI, mainPlusJ, mainMinusJ, lifetimeA, lifetimeB):
result = []
for m in mainPlusK:
if lifetimeA > 0:
mainPlusI.append(Index(+1, 0, m.k, lifetimeA))
mainPlusJ.append(Index(0, +1, m.k, lifetimeA))
if lifetimeB > 0:
mainMinusI.append(Index(-1, 0, m.k, lifetimeB))
mainMinusJ.append(Index(0, -1, m.k, lifetimeB))
m.lifetime -= 1
m.k += 1
if m.lifetime > 0:
result.append(m)
return result
def StepMainMinusK(mainMinusK, mainPlusI, mainMinusI, mainPlusJ, mainMinusJ, lifetimeA, lifetimeB):
result = []
for m in mainMinusK:
if lifetimeA > 0:
mainPlusI.append(Index(+1, 0, m.k, lifetimeA))
mainPlusJ.append(Index(0, +1, m.k, lifetimeA))
if lifetimeB > 0:
mainMinusI.append(Index(-1, 0, m.k, lifetimeB))
mainMinusJ.append(Index(0, -1, m.k, lifetimeB))
m.lifetime -= 1
m.k -= 1
if m.lifetime > 0:
result.append(m)
return result
These two functions have two different lifetime parameters for the case when n is odd and one half can be less than another. I've divided them by sign - negatively oriented will have lower half of indexes.
Set of functions to iterate Secondary nodes:
def StepPlusI(plusI):
result = []
for m in plusI:
m.i += 1
m.lifetime -= 1
if m.lifetime > 0:
result.append(m)
return result
def StepMinusI(minusI):
result = []
for m in minusI:
m.i -= 1
m.lifetime -= 1
if m.lifetime > 0:
result.append(m)
return result
def StepPlusJ(plusJ):
result = []
for m in plusJ:
m.j += 1
m.lifetime -= 1
if m.lifetime > 0:
result.append(m)
return result
def StepMinusJ(minusJ):
result = []
for m in minusJ:
m.j -= 1
m.lifetime -= 1
if m.lifetime > 0:
result.append(m)
return result
And the main function:
def Traverse(N):
halfA = halfB = (N-1)/2
if N % 2 == 0: # size is even
halfA = N/2
halfB = N/2-1
# visit center
Index(0,0,0,0).visit()
# Secondary nodes
PlusI = []
MinusI = []
PlusJ = []
MinusJ = []
# Main nodes
MainPlusI = []
MainMinusI = []
MainPlusJ = []
MainMinusJ = []
MainPlusK = []
MainMinusK = []
# Add Main nodes
if halfA > 0:
MainPlusI.append( Index(+1, 0, 0, halfA) )
MainPlusJ.append( Index(0, +1, 0, halfA) )
MainPlusK.append( Index(0, 0, +1, halfA) )
if halfB > 0:
MainMinusI.append( Index(-1, 0, 0, halfB) )
MainMinusJ.append( Index(0, -1, 0, halfB) )
MainMinusK.append( Index(0, 0, -1, halfB) )
# Finish condition flag
visited = True
while visited:
visited = False
# visit all Main nodes
for m in MainPlusI:
m.visit()
visited = True
for m in MainMinusI:
m.visit()
visited = True
for m in MainPlusJ:
m.visit()
visited = True
for m in MainMinusJ:
m.visit()
visited = True
for m in MainPlusK:
m.visit()
visited = True
for m in MainMinusK:
m.visit()
visited = True
# Visit all Secondary nodes
for m in PlusI:
m.visit()
visited = True
for m in MinusI:
m.visit()
visited = True
for m in PlusJ:
m.visit()
visited = True
for m in MinusJ:
m.visit()
visited = True
# Iterate Secondary nodes first
PlusI = StepPlusI(PlusI)
MinusI = StepMinusI(MinusI)
PlusJ = StepPlusJ(PlusJ)
MinusJ = StepMinusJ(MinusJ)
# Iterate all Main nodes as they might generate Secondary nodes
MainPlusI = StepMainPlusI(MainPlusI, MinusJ, halfB)
MainMinusJ = StepMainMinusJ(MainMinusJ, MinusI, halfB)
MainMinusI = StepMainMinusI(MainMinusI, PlusJ, halfA)
MainPlusJ = StepMainPlusJ(MainPlusJ, PlusI, halfA)
# Iterate K nodes last as they might produce Main nodes
MainPlusK = StepMainPlusK(MainPlusK, MainPlusI, MainMinusI, MainPlusJ, MainMinusJ, halfA, halfB)
MainMinusK = StepMainMinusK(MainMinusK, MainPlusI, MainMinusI, MainPlusJ, MainMinusJ, halfA, halfB)
And the live example Code
Octant symmetry
The cells in a cubic matrix which are at a certain Manhattan-distance from the center form an octahedron, which is symmetrical about the xy, xz and yz planes going through the center of the cube.
This means that you only need to find the cells which form one face of the octahedron, in the first octant of the cube, and mirror them to get the cells in the other 7 octants. So the problem is reduced to traversing the first octant of the cube (which itself is a cube) diagonally, from the center (distance 0) to the corner cell (maximum distance = 3 × n/2).
Algorithm to find coordinates
Finding the cells which are at a certain Manhattan-distance from the (0,0,0) cell in the first octant (i.e. the cells which form one face of the octahedron, perpendicular to the diagonal of the cube), means finding cells whose coordinates (x,y,z) sum to that distance. So, in the example of a 5x5x5 octant, the cells at distance 3 are the cells with coordinates:
(3,0,0) (2,1,0) (1,2,0) (0,3,0)
(2,0,1) (1,1,1) (0,2,1)
(1,0,2) (0,1,2)
(0,0,3)
You'll notice the similarity to a partitioning of the distance (actually, it's a so-called weak composition with a bounded length of 3).
Finding these combinations can be easily achieved with three nested loops; the only complication is that the distance in each dimension is restricted to n/2, so you have to skip values for x and/or y for which there exists no value for z so that x, y and z sum to the distance; that is what the min() and max() in the JavaScript code example, and the xmin, xmax, ymin and ymax variables in the C code example are for.
The mirroring of the cells in an even-sized cube is straightforward; in an odd-sized cube, the cells are not mirrored in the dimension for which their coordinate is zero (i.e. when the cell lies in the plane of symmetry). That is what the checks whether x, y or z equal zero are for in the code examples.
Parallel programming
I don't know much about GPU programming, but I assume you can completely parallellise the algorithm. For every iteration of the outer loop (i.e. for every distance), once the minimum and maximum value of x has been calculated, the iterations with different values of x can be run in parallel. Then for each value of x, once the minimum and maximum value of y has been calculated, the iterations with different values of y can be run in parallel. And finally, for each coordinate set of (x,y,z) the mirroring to the other octants can be run in parallel.
Code example 1 (JavaScript)
(Run the code snippet to see the inside-out traversal of a 9x9x9 matrix as shown in the diagrams.)
function insideOut(n) {
var half = Math.ceil(n / 2) - 1;
for (var d = 0; d <= 3 * half; d++) {
for (var x = Math.max(0, d - 2 * half); x <= Math.min(half, d); x++) {
for (var y = Math.max(0, d - x - half); y <= Math.min(half, d - x); y++) {
document.write("<br>distance " + d + " (±" + x + ",±" + y + ",±" + (d - x - y) + ") → ");
n % 2 ? mirrorOdd(x, y, d - x - y) : mirrorEven(x, y, d - x - y);
}
}
}
function mirrorEven(x, y, z) {
for (var i = 1; i >= 0; --i, x *= -1) {
for (var j = 1; j >= 0; --j, y *= -1) {
for (var k = 1; k >= 0; --k, z *= -1) {
visit(half + x + i, half + y + j, half + z + k);
}
}
}
}
function mirrorOdd(x, y, z) {
for (var i = 0; i < (x ? 2 : 1); ++i, x *= -1) {
for (var j = 0; j < (y ? 2 : 1); ++j, y *= -1) {
for (var k = 0; k < (z ? 2 : 1); ++k, z *= -1) {
visit(half + x, half + y, half + z);
}
}
}
}
function visit(x, y, z) {
document.write("(" + x + "," + y + "," + z + ") " );
}
}
insideOut(9);
Code example 2 (C)
The mirroring functions can be unrolled for simplicity. In fact, the whole algorithm consists of nothing more than 3 nested loops and simple integer calculations.
void mirrorEven(unsigned int x, unsigned int y, unsigned int z, unsigned int h) {
visit(h+x+1, h+y+1, h+z+1);
visit(h+x+1, h+y+1, h-z);
visit(h+x+1, h-y, h+z+1);
visit(h+x+1, h-y, h-z);
visit(h-x, h+y+1, h+z+1);
visit(h-x, h+y+1, h-z);
visit(h-x, h-y, h+z+1);
visit(h-x, h-y, h-z);
}
void mirrorOdd(unsigned int x, unsigned int y, unsigned int z, unsigned int h) {
visit(h+x, h+y, h+z);
if ( z) visit(h+x, h+y, h-z);
if ( y ) visit(h+x, h-y, h+z);
if ( y && z) visit(h+x, h-y, h-z);
if (x ) visit(h-x, h+y, h+z);
if (x && z) visit(h-x, h+y, h-z);
if (x && y ) visit(h-x, h-y, h+z);
if (x && y && z) visit(h-x, h-y, h-z);
}
void insideOut(unsigned int n) {
unsigned int d, x, xmin, xmax, y, ymin, ymax, half = (n-1)/2;
for (d = 0; d <= 3*half; d++) {
xmin = d < 2*half ? 0 : d-2*half;
xmax = d < half ? d : half;
for (x = xmin; x <= xmax; x++) {
ymin = d < x+half ? 0 : d-x-half;
ymax = d > x+half ? half : d-x;
for (y = ymin; y <= ymax; y++) {
if (n%2) mirrorOdd(x, y, d-x-y, half);
else mirrorEven(x, y, d-x-y, half);
}
}
}
}
[I'm using the Manhattan distance in the solution]
For simplicity, let start assuming 3D arrays of odd dimension ([2N+1, 2N+1, 2N+1])
Using manhattan distance the greatest distance between the center ([0,0,0]) and a point is 3N ([N,N,N], [N,N,-N], ...)
So, basically the idea is find a way to generate all coordinates that have a specific distance. Then starting from distance 0 to 3N generating those coordinates.
To generate coordinates [X,Y,Z] that distance to center in some value K, what we need is all numbers X, Y, Z between -N and N such that ABS(X) + ABS(Y) + ABS(Z) == K. This can be done with this:
FUNC COORDS_AT_DIST(K)
FOR X = -MIN(N, K) TO MIN(N, K)
FOR Y = -MIN(N, K - ABS(X)) TO MIN(N, K - ABS(X))
LET Z = K - ABS(X) - ABS(Y)
IF Z <= N
VISIT(X, Y, Z)
IF Z != 0
VISIT(X, Y, -Z)
Then, use this function like so:
FOR K = 0 TO 3N
COORDS_AT_DIST(K)
This code visit all coordinates with values between [-N,-N,-N] and [N,N,N] sorted according to the distance to [0,0,0].
Now, to handle even dimensions too, we need some extra checks since the values in coordinates for a dimension L goes between [-(L/2-1),-(L/2-1),-(L/2-1)] and [L/2,L/2,L/2].
Something like this:
FUNC VISIT_COORDS_FOR_DIM(L)
LET N = L/2 //Integer division
FOR K = 0 TO 3N
FOR X = -MIN(N - REM(L+1, 2), K) TO MIN(N, K)
FOR Y = -MIN(N - REM(L+1, 2), K - ABS(X)) TO MIN(N, K - ABS(X))
LET Z = K - ABS(X) - ABS(Y)
IF Z <= N
VISIT(X, Y, Z)
IF Z != 0 && (REM(L, 2) != 0 || Z < N)
VISIT(X, Y, -Z)
Just for clarity:
MIN(X, Y): Minimum value between X and Y
ABS(X): Absolute value of X
REM(X, Y): Remainder after division of X by Y
VISIT(X, Y, Z): Visit the generated coordinate (X, Y, Z)
Using VISIT_COORDS_FOR_DIM function with L=3 you get this:
1. [0, 0, 0] DISTANCE: 0
2. [-1, 0, 0] DISTANCE: 1
3. [0, -1, 0] DISTANCE: 1
4. [0, 0, -1] DISTANCE: 1
5. [0, 0, 1] DISTANCE: 1
6. [0, 1, 0] DISTANCE: 1
7. [1, 0, 0] DISTANCE: 1
8. [-1, -1, 0] DISTANCE: 2
9. [-1, 0, -1] DISTANCE: 2
10. [-1, 0, 1] DISTANCE: 2
11. [-1, 1, 0] DISTANCE: 2
12. [0, -1, -1] DISTANCE: 2
13. [0, -1, 1] DISTANCE: 2
14. [0, 1, -1] DISTANCE: 2
15. [0, 1, 1] DISTANCE: 2
16. [1, -1, 0] DISTANCE: 2
17. [1, 0, -1] DISTANCE: 2
18. [1, 0, 1] DISTANCE: 2
19. [1, 1, 0] DISTANCE: 2
20. [-1, -1, -1] DISTANCE: 3
21. [-1, -1, 1] DISTANCE: 3
22. [-1, 1, -1] DISTANCE: 3
23. [-1, 1, 1] DISTANCE: 3
24. [1, -1, -1] DISTANCE: 3
25. [1, -1, 1] DISTANCE: 3
26. [1, 1, -1] DISTANCE: 3
27. [1, 1, 1] DISTANCE: 3
And for L=4:
1. [0, 0, 0] DISTANCE: 0 33. [1, -1, -1] DISTANCE: 3
2. [-1, 0, 0] DISTANCE: 1 34. [1, -1, 1] DISTANCE: 3
3. [0, -1, 0] DISTANCE: 1 35. [1, 0, 2] DISTANCE: 3
4. [0, 0, -1] DISTANCE: 1 36. [1, 1, -1] DISTANCE: 3
5. [0, 0, 1] DISTANCE: 1 37. [1, 1, 1] DISTANCE: 3
6. [0, 1, 0] DISTANCE: 1 38. [1, 2, 0] DISTANCE: 3
7. [1, 0, 0] DISTANCE: 1 39. [2, -1, 0] DISTANCE: 3
8. [-1, -1, 0] DISTANCE: 2 40. [2, 0, -1] DISTANCE: 3
9. [-1, 0, -1] DISTANCE: 2 41. [2, 0, 1] DISTANCE: 3
10. [-1, 0, 1] DISTANCE: 2 42. [2, 1, 0] DISTANCE: 3
11. [-1, 1, 0] DISTANCE: 2 43. [-1, -1, 2] DISTANCE: 4
12. [0, -1, -1] DISTANCE: 2 44. [-1, 1, 2] DISTANCE: 4
13. [0, -1, 1] DISTANCE: 2 45. [-1, 2, -1] DISTANCE: 4
14. [0, 0, 2] DISTANCE: 2 46. [-1, 2, 1] DISTANCE: 4
15. [0, 1, -1] DISTANCE: 2 47. [0, 2, 2] DISTANCE: 4
16. [0, 1, 1] DISTANCE: 2 48. [1, -1, 2] DISTANCE: 4
17. [0, 2, 0] DISTANCE: 2 49. [1, 1, 2] DISTANCE: 4
18. [1, -1, 0] DISTANCE: 2 50. [1, 2, -1] DISTANCE: 4
19. [1, 0, -1] DISTANCE: 2 51. [1, 2, 1] DISTANCE: 4
20. [1, 0, 1] DISTANCE: 2 52. [2, -1, -1] DISTANCE: 4
21. [1, 1, 0] DISTANCE: 2 53. [2, -1, 1] DISTANCE: 4
23. [2, 0, 0] DISTANCE: 2 54. [2, 0, 2] DISTANCE: 4
23. [-1, -1, -1] DISTANCE: 3 55. [2, 1, -1] DISTANCE: 4
24. [-1, -1, 1] DISTANCE: 3 56. [2, 1, 1] DISTANCE: 4
25. [-1, 0, 2] DISTANCE: 3 57. [2, 2, 0] DISTANCE: 4
26. [-1, 1, -1] DISTANCE: 3 58. [-1, 2, 2] DISTANCE: 5
27. [-1, 1, 1] DISTANCE: 3 59. [1, 2, 2] DISTANCE: 5
28. [-1, 2, 0] DISTANCE: 3 60. [2, -1, 2] DISTANCE: 5
29. [0, -1, 2] DISTANCE: 3 61. [2, 1, 2] DISTANCE: 5
30. [0, 1, 2] DISTANCE: 3 62. [2, 2, -1] DISTANCE: 5
31. [0, 2, -1] DISTANCE: 3 63. [2, 2, 1] DISTANCE: 5
32. [0, 2, 1] DISTANCE: 3 64. [2, 2, 2] DISTANCE: 6
This solution has as benefits that no requires any special data-structure, not even an array.
Another solution could be if you can use a queue (it is not hard to implement with an array) and 3D boolean (or int) array is do like a BFS starting from the center.
Firstly, to define what is a neighbor, you can use movement arrays, for example:
Two cells are neighbors if share a common side (Manhattan distance):
DX = { 1, 0, 0, -1, 0, 0 }
DY = { 0, 1, 0, 0, -1, 0 }
DZ = { 0, 0, 1, 0, 0, -1 }
Two cells are neighbors if share a common edge:
DX = { 1, 0, 0, -1, 0, 0, 1, 1, 0, -1, -1, 0, 1, 1, 0, -1, -1, 0 }
DY = { 0, 1, 0, 0, -1, 0, 1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0, -1 }
DZ = { 0, 0, 1, 0, 0, -1, 0, 1, 1, 0, 1, 1, 0, -1, -1, 0, -1, -1 }
Two cells are neighbors if share a common corner (Chebyshev distance):
DX = { 1, 0, 0, -1, 0, 0, 1, 1, 0, -1, -1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 1, 1, -1, -1, 1, -1 }
DY = { 0, 1, 0, 0, -1, 0, 1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0, -1, 1, 1, -1, 1, -1, 1, -1, -1 }
DZ = { 0, 0, 1, 0, 0, -1, 0, 1, 1, 0, 1, 1, 0, -1, -1, 0, -1, -1, 1, 1, 1, -1, 1, -1, -1, -1 }
Now, using a queue you can start from center position, then add the neighbors, then neighbors of the neighbors and so on. In each iteration you can visit each generated position.
Something like this:
DX = { 1, 0, 0, -1, 0, 0 }
DY = { 0, 1, 0, 0, -1, 0 }
DZ = { 0, 0, 1, 0, 0, -1 }
VISIT_COORDS_FOR_DIM(L):
LET N = L/2
IF (REM(L, 2) == 0)
N--
V: BOOLEAN[L, L, L]
Q: QUEUE<POINT3D>
V[N, N, N] = TRUE
ENQUEUE(Q, POINT3D(N, N, N))
WHILE COUNT(Q) > 0
P = DEQUEUE(Q)
VISIT(P.X - N, P.Y - N, P.Z - N) //To Transform coords to [-N, N] range.
FOR I = 0 TO LENGTH(DX) - 1
LET X = P.X + DX[I]
LET Y = P.Y + DY[I]
LET Z = P.Z + DZ[I]
IF IS_VALID_POS(L, X, Y, Z) && V[X, Y, Z] == FALSE
V[X, Y, Z] = TRUE
ENQUEUE(Q, POINT3D(X, Y, Z))
IS_VALID_POS(L, X, Y, Z)
RETURN X >= 0 && X < L &&
Y >= 0 && Y < L &&
Z >= 0 && Z < L
Used functions:
REM(X, Y): Remainder after division of X by Y
ENQUEUE(Q, X): Enqueue element X in queue Q
DEQUEUE(Q): Dequeue first element from queue Q
COUNT(Q): Number of elements in queue Q
VISIT(X, Y, Z): Visit the generated coordinate (X, Y, Z)
This solution has as benefits that you can define when two position are neighbors using movement arrays.
The key to getting an efficient algorithm to this question is to see the geometry underlying it. What you are asking for is to solve the Diophantine equation N = a^2 + b^2 + c^2 for each successive value of N, enumerating such solutions in any order. The solutions to this equation are integral points on a sphere of radius N. So in one sense your problem is enumerating spheres.
First, though, it should be clear that the hard problem here is enumerating nonnegative solutions for coordinates (a,b,c). For each such coordinate, there are eight other solutions from mirror symmetry around the coordinate planes, since a^2 = (-a)^2 etc. (In general. If one or more of a,b,c are zero, you get fewer mirror points.) There's a further symmetry by permuting the coordinates so that a <= b <= c. This is the easy part of the enumeration.
The essence of the sphere enumeration algorithm is to consider two sets of points that approximate a sphere of radius N: one which is consists of points with norm "slightly less" than N and one which consists of their lattice neighbors with norm "slightly greater" than N or equal to N. "Slightly less" means that for a point (a,b,c), a^2 + b^2 + c^2 < N, but one or more of the points (a+1,b,c), (a,b+1,c), or (a,b,c+1) has norm >= N. As far as code, you don't need to represent the "slightly less" set; it's already been processed. It's sufficient to create a heap of the "slightly greater" set, sorted by their norm.
Each step of the algorithm changes to the "slightly greater" set for N into one for N+1. Remove the least element of the heap, say, (a,b,c). Now add its nearest neighbor points with greater norm to the heap, the three points (a+1,b,c), (a,b+1,c), and (a,b,c+1). Some of them may already be there; I'll come back to that. When you add an incremental point onto the heap, you need its norm. You do not, however, need to calculate it from scratch. Rely on the identity (a+1)^2 - a^2 = 2a + 1. In other words, you don't need any multiplication operations to compute these norms. Depending on your GPU, you can compute expressions a << 1 + 1 or maybe a + a + 1.
You can also optimize checking for existing points on the heap. Each point has six immediate lattice neighbors. The lattice neighbor with the least norm will be the first one to add it. Suppose a < b < c for a point (a,b,c). Its neighbor with the least norm is (a,b,c-1). Thus when enumerating the point (a-1,b,c), the point (a,b,c) is already on the heap; you don't need even to check that it's there. Pay attention to special cases where the some of the coordinates are equal.
This algorithm enumerates spheres, not cubes. It's easy enough to restrict attention to a cube of with maximum index D. If one of the coordinates is equal to D, then don't add three points, but fewer. The enumeration ends on the point (D,D,D), when there are no more valid neighbor points to add.
The performance of this algorithm should be very fast. It needs a heap of size O(N^2). If you enumerate all the points beforehand you need storage O(N^3). Furthermore, it needs no multiplication, for a further constant speed up.
If it is just centers: There are a lot of different valid orders. Just compute a 3d map, with elements sorted in order. Offset by origin. Make the map:
for x,y,z -domain, domain
map.add ( x,y,z, distance(x,y,z) )
map.sort ( distance )
Then at point x,y,z to traverse
for ( i=0; i++ )
visit ( map[i].xyz + x,y,z )
If it's real distance and not voxel centers, it gets a lot harder.
generating indexes in Manhatan distance order is similar to subset sum problem so just compute the max distance (the sum) and then separate axises to reduce the problem. Here C++ example:
int x,y,z,d,dx,dy,dz,D;
// center idx
int cx=n>>1;
int cy=n>>1;
int cz=n>>1;
// min idx
int x0=-cx;
int y0=-cy;
int z0=-cz;
// max idx
int x1=n-1-cx;
int y1=n-1-cy;
int z1=n-1-cz;
// max distance
x=max(x0,x1);
y=max(y0,y1);
z=max(z0,z1);
D=x+y+z;
// here do your stuff
#define traverse(x,y,z) { /* do something with the array beware x,y,z are signed !!! */ }
// traversal
for (d=0;d<=D;d++) // distance
for (dx=d ,x=-dx;x<=dx;x++) if ((x>=x0)&&(x<=x1)) // x axis separation
for (dy=d-abs(x) ,y=-dy;y<=dy;y++) if ((y>=y0)&&(y<=y1)) // y axis separation
{
dz=d-abs(x)-abs(y); // z axis have only 1 or 2 options
z=-dz; if (z>=z0) traverse(x,y,z);
z=+dz; if ((z)&&(z<=z1)) traverse(x,y,z);
}
#undef traverse
You can replace the traverse(x,y,z) macro by any stuff or function you want. beware x,y,z are signed so can be negative to get C++ style indexes you need to use (x+cx,y+cy,z+cz).
This can handle booth even and odd indexes and also non cube resolutions (if you simply convert n to nx,ny,nz in the first few constants computations). Also the [0,0,0] can be everywhere (not in center) so it is easily applicable to any needs I can think of...
Here example output for n=5
[ 0, 0, 0] = 0
[-1, 0, 0] = 1
[ 0,-1, 0] = 1
[ 0, 0,-1] = 1
[ 0, 0, 1] = 1
[ 0, 1, 0] = 1
[ 1, 0, 0] = 1
[-2, 0, 0] = 2
[-1,-1, 0] = 2
[-1, 0,-1] = 2
[-1, 0, 1] = 2
[-1, 1, 0] = 2
[ 0,-2, 0] = 2
[ 0,-1,-1] = 2
[ 0,-1, 1] = 2
[ 0, 0,-2] = 2
[ 0, 0, 2] = 2
[ 0, 1,-1] = 2
[ 0, 1, 1] = 2
[ 0, 2, 0] = 2
[ 1,-1, 0] = 2
[ 1, 0,-1] = 2
[ 1, 0, 1] = 2
[ 1, 1, 0] = 2
[ 2, 0, 0] = 2
[-2,-1, 0] = 3
[-2, 0,-1] = 3
[-2, 0, 1] = 3
[-2, 1, 0] = 3
[-1,-2, 0] = 3
[-1,-1,-1] = 3
[-1,-1, 1] = 3
[-1, 0,-2] = 3
[-1, 0, 2] = 3
[-1, 1,-1] = 3
[-1, 1, 1] = 3
[-1, 2, 0] = 3
[ 0,-2,-1] = 3
[ 0,-2, 1] = 3
[ 0,-1,-2] = 3
[ 0,-1, 2] = 3
[ 0, 1,-2] = 3
[ 0, 1, 2] = 3
[ 0, 2,-1] = 3
[ 0, 2, 1] = 3
[ 1,-2, 0] = 3
[ 1,-1,-1] = 3
[ 1,-1, 1] = 3
[ 1, 0,-2] = 3
[ 1, 0, 2] = 3
[ 1, 1,-1] = 3
[ 1, 1, 1] = 3
[ 1, 2, 0] = 3
[ 2,-1, 0] = 3
[ 2, 0,-1] = 3
[ 2, 0, 1] = 3
[ 2, 1, 0] = 3
[-2,-2, 0] = 4
[-2,-1,-1] = 4
[-2,-1, 1] = 4
[-2, 0,-2] = 4
[-2, 0, 2] = 4
[-2, 1,-1] = 4
[-2, 1, 1] = 4
[-2, 2, 0] = 4
[-1,-2,-1] = 4
[-1,-2, 1] = 4
[-1,-1,-2] = 4
[-1,-1, 2] = 4
[-1, 1,-2] = 4
[-1, 1, 2] = 4
[-1, 2,-1] = 4
[-1, 2, 1] = 4
[ 0,-2,-2] = 4
[ 0,-2, 2] = 4
[ 0, 2,-2] = 4
[ 0, 2, 2] = 4
[ 1,-2,-1] = 4
[ 1,-2, 1] = 4
[ 1,-1,-2] = 4
[ 1,-1, 2] = 4
[ 1, 1,-2] = 4
[ 1, 1, 2] = 4
[ 1, 2,-1] = 4
[ 1, 2, 1] = 4
[ 2,-2, 0] = 4
[ 2,-1,-1] = 4
[ 2,-1, 1] = 4
[ 2, 0,-2] = 4
[ 2, 0, 2] = 4
[ 2, 1,-1] = 4
[ 2, 1, 1] = 4
[ 2, 2, 0] = 4
[-2,-2,-1] = 5
[-2,-2, 1] = 5
[-2,-1,-2] = 5
[-2,-1, 2] = 5
[-2, 1,-2] = 5
[-2, 1, 2] = 5
[-2, 2,-1] = 5
[-2, 2, 1] = 5
[-1,-2,-2] = 5
[-1,-2, 2] = 5
[-1, 2,-2] = 5
[-1, 2, 2] = 5
[ 1,-2,-2] = 5
[ 1,-2, 2] = 5
[ 1, 2,-2] = 5
[ 1, 2, 2] = 5
[ 2,-2,-1] = 5
[ 2,-2, 1] = 5
[ 2,-1,-2] = 5
[ 2,-1, 2] = 5
[ 2, 1,-2] = 5
[ 2, 1, 2] = 5
[ 2, 2,-1] = 5
[ 2, 2, 1] = 5
[-2,-2,-2] = 6
[-2,-2, 2] = 6
[-2, 2,-2] = 6
[-2, 2, 2] = 6
[ 2,-2,-2] = 6
[ 2,-2, 2] = 6
[ 2, 2,-2] = 6
[ 2, 2, 2] = 6
The distance from the origin is known for each point (a, b, c) to be sqrt(a*a + b*b + c*c). We can define this to be distance(a, b, c).†
For each point in your 3D array, you can insert it into a min heap using distance as your ordering criteria. To avoid recalculation, augment your point representation in the heap to include the cached calculation of distance for that point when it was inserted into the heap.
heap_element = (x, y, z, d)
heap_compare(heap_element a, heap_element b) = a.d < b.d
for each point (x,y,z) in 3D array
· heap.add(heap_element(x, y, z, distance(x, y, z)))
Now, you can just draw out each point from the top of the heap to get your ordering.
N = heap.size
for i in 0..N
· ordering[i] = heap.top
· heap.pop
† For the purposes of this algorithm, using the actual distance is not critical. For performance reasons, you can omit taking the sqrt, and just use a*a + b*b + c*c as the metric for the heap ordering criteria.
In ruby, I just get all points for each distance from the center in order.
def get_points(side_len)
side_len % 2 == 0 ? min_dist = 1 : min_dist = 0
if side_len % 2 == 0
min_dist = 1
max_1d_dist = side_len / 2
else
min_dist = 0
max_1d_dist = (side_len - 1) / 2
end
max_dist = 3 * max_1d_dist
min_dist.upto(max_dist) do |dist|
min_x_dist = [min_dist, dist - 2 * max_1d_dist].max
max_x_dist = [dist - 2 * min_dist, max_1d_dist].min
min_x_dist.upto(max_x_dist) do |x_dist|
min_y_dist = [min_dist, dist - x_dist - max_1d_dist].max
max_y_dist = [dist - x_dist - min_dist, max_1d_dist].min
min_y_dist.upto(max_y_dist) do |y_dist|
z_dist = dist - x_dist - y_dist
print_vals(x_dist, y_dist, z_dist)
end
end
end
end
def print_vals(x_dist, y_dist, z_dist)
x_signs = [1]
y_signs = [1]
z_signs = [1]
x_signs << -1 unless x_dist == 0
y_signs << -1 unless y_dist == 0
z_signs << -1 unless z_dist == 0
x_signs.each do |x_sign|
y_signs.each do |y_sign|
z_signs.each do |z_sign|
puts "[#{x_sign*x_dist}, #{y_sign*y_dist}, #{z_sign*z_dist}]"
end
end
end
end
Output is:
2.1.2 :277 > get_points(1)
[0, 0, 0]
2.1.2 :278 > get_points(2)
[1, 1, 1]
[1, 1, -1]
[1, -1, 1]
[1, -1, -1]
[-1, 1, 1]
[-1, 1, -1]
[-1, -1, 1]
[-1, -1, -1]
2.1.2 :279 > get_points(3)
[0, 0, 0]
[0, 0, 1]
[0, 0, -1]
[0, 1, 0]
[0, -1, 0]
[1, 0, 0]
[-1, 0, 0]
[0, 1, 1]
[0, 1, -1]
[0, -1, 1]
[0, -1, -1]
[1, 0, 1]
[1, 0, -1]
[-1, 0, 1]
[-1, 0, -1]
[1, 1, 0]
[1, -1, 0]
[-1, 1, 0]
[-1, -1, 0]
[1, 1, 1]
[1, 1, -1]
[1, -1, 1]
[1, -1, -1]
[-1, 1, 1]
[-1, 1, -1]
[-1, -1, 1]
[-1, -1, -1]
This is a simple and fast algorithm to transverse a 3D array using Manhattan distance.
The 3D array with size n in each dimension shall be represented by a coordinate system with the origin in the middle of your array. For the centre to be defined, we assume the size of the array in each dimension is a odd number. Every element has a triple of coordinates like this [x, y, z] and each coordinate can reach a maximum value of `(n/2)-1. (Info: The pictures added are in 2D for better understanding)
First we can simplify this by regarding just the positive octant (all coordinates are positive). All other elements can be generated by reflection.
In one octant all elements with the same distance to the centre are defined by a plane with the equation: x+y+z=distance. We achieve this by counting the distance from 0 to n-1 in single steps. For each distance we look for all element on the corresponding plane.
When reaching a distance>(n/2)-1 some of the points will be outside the array (one of the coord > (n/2)-1). So we have to exclude them from the results.
Each calculated element is representing up to 8 elements you get by reflection (see point 1). You can simply achieve this by alternately multiplying each coordinate with -1. [+/-x, +/-y, +/-z] (8 possible combinations if all coords != 0)
Here is a code schema for my algorithm:
//rise the distance by one each iteration
for(distance=0; distance<n-1; distance++) //loop distance from 0 to n-1
for(i=0; i<=distance; i++)
x=i; //x ∈ [0, distance]
for(j=0; j<=distance-x; j++)
y=j; //y ∈ [0, distance-x]
z=distance-(x+y); //because distance=x+y+z
//now we have to exclude all elements with one coord <= (n/2)-1
if(x<=(n/2)-1 && y<=(n/2)-1 && z<=(n/2)-1)
//[x,y,z] we found a valid element!
//let's generate the 7 corresponding elements (up to 7)
if(x!=0) //[-x,y,z]
if(y!=0) //[x,-y,z]
if(z!=0) //[x,y,-z]
if(x!=0 && y!=0) //[-x,-y,z]
if(x!=0 && z!=0) //[-x,y,-z]
if(y!=0 && z!=0) //[x,-y,-z]
if(y!=0 && y!=0 && z!=0) //[-x,-y,-z]
Here is the output for n=7:
Distance:0 [0,0,0]
Distance:1 [0,0,1] [0,0,-1] [0,1,0] [0,-1,0] [1,0,0] [-1,0,0]
Distance:2 [0,0,2] [0,0,-2] [0,1,1] [0,-1,1] [0,1,-1] [0,-1,-1] [0,2,0] [0,-2,0] [1,0,1] [-1,0,1] [1,0,-1] [-1,0,-1] [1,1,0] [-1,1,0] [1,-1,0] [-1,-1,0] [2,0,0] [-2,0,0]
Distance:3 [0,1,2] [0,-1,2] [0,1,-2] [0,-1,-2] [0,2,1] [0,-2,1] [0,2,-1] [0,-2,-1] [1,0,2] [-1,0,2] [1,0,-2] [-1,0,-2] [1,1,1] [-1,1,1] [1,-1,1] [1,1,-1] [-1,-1,1] [-1,1,-1] [1,-1,-1] [-1,-1,-1] [1,2,0] [-1,2,0] [1,-2,0] [-1,-2,0] [2,0,1] [-2,0,1] [2,0,-1] [-2,0,-1] [2,1,0] [-2,1,0] [2,-1,0] [-2,-1,0]
Distance:4 [0,2,2] [0,-2,2] [0,2,-2] [0,-2,-2] [1,1,2] [-1,1,2] [1,-1,2] [1,1,-2] [-1,-1,2] [-1,1,-2] [1,-1,-2] [-1,-1,-2] [1,2,1] [-1,2,1] [1,-2,1] [1,2,-1] [-1,-2,1] [-1,2,-1] [1,-2,-1] [-1,-2,-1] [2,0,2] [-2,0,2] [2,0,-2] [-2,0,-2] [2,1,1] [-2,1,1] [2,-1,1] [2,1,-1] [-2,-1,1] [-2,1,-1] [2,-1,-1] [-2,-1,-1] [2,2,0] [-2,2,0] [2,-2,0] [-2,-2,0]
Distance:5 [1,2,2] [-1,2,2] [1,-2,2] [1,2,-2] [-1,-2,2] [-1,2,-2] [1,-2,-2] [-1,-2,-2] [2,1,2] [-2,1,2] [2,-1,2] [2,1,-2] [-2,-1,2] [-2,1,-2] [2,-1,-2] [-2,-1,-2] [2,2,1] [-2,2,1] [2,-2,1] [2,2,-1] [-2,-2,1] [-2,2,-1] [2,-2,-1]
If you are using Euclidian Norm, you have to replace your distance with: sqrt(x*x+y*y+z*z) and you cannot rise the distance in steps of one. But beside that you could do it very similiar.
Waited until the bounty race was completed, as to not interfere. I would however like to point out that imho all answers that i can see atm are rather dubious. The question holds this statement:
"this algorithm is intended to perform on GPU"
It is indeed possible to do efficient numeric processing on the GPU, but all solutions here propose some kind of looping. This is not how the GPU works.
The GPU executes in parallel. Indeed one can loop in the GPU, but then it happens in an extremely isolated fashion. A loop requires a "global controller", for example a counter, but the point of the GPU execution is that it executes in no particular order, using multiple cores. It is impossible to "grab" the result of one cores' execution and use it as argument to another cores' calculation. This is simply not how the GPU works.
It is possible to do calculation of multidimensional arrays on the GPU, even on higher than 3-dimensional. That is just a matter of cell addressing, and it is trivial to address for example 4 dimensions using a 1-dimensional address space, and describe/access the corresponding data.
But it is not possible to step through cells (pixels, memory locations) one by one, in a specific order - there is no control for such. And it is not possible to increment, or have loops, or have inner loops. On a GPU, the last element may well be executed first. On a GPU, each element is totally autonomous. No calculation is aware of any other calculation, since there is no communication between the them. The environment for each calculation on the GPU is as if it was alone in the world.
The answers given in this thread all assume CPU logic. IF solving the problem on CPU, it is trivial. Taking a 2-dimensional example, here are the absolute x,y coordinate pairs for 5x5, adjusted for zero in the middle:
┌───┬───┬───┬───┬───┐
│2 2│2 1│2 0│2 1│2 2│
├───┼───┼───┼───┼───┤
│1 2│1 1│1 0│1 1│1 2│
├───┼───┼───┼───┼───┤
│0 2│0 1│0 0│0 1│0 2│
├───┼───┼───┼───┼───┤
│1 2│1 1│1 0│1 1│1 2│
├───┼───┼───┼───┼───┤
│2 2│2 1│2 0│2 1│2 2│
└───┴───┴───┴───┴───┘
Adding the pairs in each cell gives us the Manhattan distances:
4 3 2 3 4
3 2 1 2 3
2 1 0 1 2
3 2 1 2 3
4 3 2 3 4
At the same time, we can have an indexing system (this is however a weakness in the question, as it does no state adressing spaces):
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
21 22 23 24 25
We can flatten both the distances:
dist = 4 3 2 3 4 3 2 1 2 3 2 1 0 1 2 3 2 1 2 3 4 3 2 3 4
and the indexes
index = 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
The unique Manhattan distances for an N (or N * N or N * N * N) array are all numbers between 1 and N-1 preceeded by 0 if N is odd. For a 5*5 matrix:
0 1 2 3 4
Walk through each distance and see where that distance equals "dist". For 5*5:
0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 // dist = 0 at locations 13
0 0 0 0 0 0 0 1 0 0 0 1 0 1 0 0 0 1 0 0 0 0 0 0 0 // dist = 1 at locations 8 12 14 18
0 0 1 0 0 0 1 0 1 0 1 0 0 0 1 0 1 0 1 0 0 0 1 0 0 // dist = 2 at locations 3 7 9 11 15 17 19 23
0 1 0 1 0 1 0 0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 0 1 0 // dist = 3 at locations 2 4 6 10 16 20 22 24
1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 // dist = 4 at locations 1 5 21 25
and build up an array based on those locations. Ie. have an ampty array, append 13 to it as it's first element, then append 8 12 14 18, etc. The eventual result will be
13 8 12 14 18 3 7 9 11 15 17 19 23 2 4 6 10 16 20 22 24 1 5 21 25
That is the desired sord order. It is simple to re-arrange that into for example 2-dimensional address space, by using divide, min and residue.
However, this way to calculate is USELESS ON A GPU. It requires a specific execution order, and that we do not have on a GPU.
If solving the traversal order on a GPU, the solution should be
lookupcoordinates = fn(currentposition)
It may be possible to describe fn, but the question is way too incomplete - a whole lot more detail is required, than just saying "on GPU". How is the 3d array described? In what memory is it situated? What is the result's adressing space? Etc.
I'd be happy to hear more insight detail on this, as well as corrections to what i write above.
Addendum for discussion with user m69
One (minor) thought is doing the calculation in [number of dimensions] steps, where an accumulation from previous dimension would be used for next, up to for example 3. It may be that the numerical behaviour would be useful in such a case.
If looking closer at the basics, one would probably assume a linear target space, such that it is a simple indexing vector from 1 to [number of cells], where for example a 10*10*10 cube would have a linear 1D vector of 1000 indexes, from 1 to 1000. It is always possible to later on re-phrase these indexes into square or more-dimensional format.
In a 1-dimensional case, let's assume we have a 9-element data "cube" (same as 9*1*1, if expressed 3-dimensionally). Would be as below
x x x x x x x x x // Data
1 2 3 4 5 6 7 8 9 // Indexes
4 3 2 1 0 1 2 3 4 // Manhattan distances
5 4 6 3 7 2 8 1 9 // Pick order, starting from center
Hence we need a fn that maps as below
┌──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┐
│1 to 5│2 to 4│3 to 6│4 to 3│5 to 7│6 to 2│7 to 8│8 to 1│9 to 9│
└──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┘
Now, if for example looking at index 4 of the result, fn must be able to, independently of any other index, resolve a result = 3, ie. fn(4) = 3. It should be possible to describe fn. It should be possible to first conclude that "4" resides in layer 2 (if layer 0 is the innermost). After that, it should be possible to conclude how meny cells layer 2 has (all layers have 2 cells), and finally whether this cell is the first or second occurance/element of layer 2. That would resolve into 3, ie. for result[4] we pick data[3].
Now, if assuming a 2-dimensional "cube" of size 11*11(*1), we have this situation:
0 1 2 3 4 5 6 7 8 9 10 // Unique Manhattan distances
1 4 8 12 16 20 20 16 12 8 4 // How many cells in each distance-layer?
We note that the "how many" is rather symmetrical, and even more symmetrical for 10*10:
1 2 3 4 5 6 7 8 9 // Unique Manhattan distances
4 8 12 16 20 16 12 8 4 // How many cells in each distance-layer?
4 12 24 40 60 76 88 96 100 // Cumulative sum of "How many..."
Notice the "cumulative sum"! Using that, if we are atm solving for example index=55 (can happen at any time, before or after 54, remember!), we can conclude that we are currently aiming at layer 5, which holds 20 elements, those with index = 40...60.
This layer starts with 40, and we are now at 55. Difference is 15. Maybe it is possible to describe the offset (x,y)-coordinates from "layer 5-origin x,y) using that "15"? My guess is that we are just entering the 4th quadrant.
Same for 3D?

Resources