Frequency of non-increasing and non-decreasing subsequences - arrays

Having a sequence of numbers of length L, I need to count how many non-decreasing and non-increasing sub-sequences of exact length are there. For example, if I have a sequence of length 15
2, 4, 11, 13, 3, 5, 5, 6, 3, 3, 2, 4, 2, 14, 15
I see that non-increasing sub-sequences are
13, 3
6, 3, 3 , 2
4, 2
and non-decreasing sub-sequences are
2, 4, 11, 13
3, 5, 5, 6
2, 4
2, 14, 15
So here I have
2 non-increasing sub-sequences of length 2
1 non-increasing sub-sequence of length 4
2 non-decreasing sub-sequences of length 2
1 non-decreasing sub-sequences of length 3
2 non-decreasing sub-sequence of length 4
Since the maximum length of a non-decreasing (or non-increasing) sub-sequence can be 15 in this case, I thought about representing frequencies through vectors x for non-increasing and y for non-decreasing sub-sequences:
x = (0,2,0,1,0,0,0,0,0,0,0,0,0,0,0)
y = (0,1,1,2,0,0,0,0,0,0,0,0,0,0,0)
Expanding this to general case of sequence of length L, I wanted to go through the sequence and, using loops, count frequencies of subsequences of the exact lengths. How would I do that? I would create zero-vectors of length L and I would add 1 to the l-th element of zero matrix every time I meet a sub-sequence of length l.
Since my sequence will be of length of few thousands, I wouldn't ask Matlab to write them, but I would ask it to write me particular frequency.
Is this a good approach?
Is there some function in Matlab that is doing this?

How about that lovely one-line solution?
%// vector
A = [2, 4, 11, 13, 3, 5, 5, 6, 3, 3, 2, 4, 2, 14, 15]
%// number of digits in output
nout = 15;
seqFreq = #(vec,x) histc(accumarray(cumsum(~(-x*sign([x*1; diff(vec(:))]) + 1 )), ...
vec(:),[],#(x) numel(x)*~all(x == x(1)) ),1:nout).' %'
%// non-increasing sequences -> input +1
x = seqFreq(A,+1)
%// non-decreasing sequences -> input -1
y = seqFreq(A,-1)
x = 0 2 0 1 0 0 0 0 0 0 0 0 0 0 0
y = 0 1 1 2 0 0 0 0 0 0 0 0 0 0 0
Explanation
%// example for non-increasing
q = +1;
%// detect sequences: value = -1
seq = sign([q*1; diff(A(:))]);
%// find subs for accumarray
subs = cumsum(~(-q*seq + 1));
%// count number of elements and check if elements are equal, if not, set count to zero
counts = accumarray(subs,A(:),[],#(p) numel(p)*~all(p == p(1)) );
%// count number of sequences
x = histc(counts,1:nout);

For non-decreasing sequences:
x = [2, 4, 11,13,3,5,5,6,3,3,2,4,2,14,15]; %// data
y = [inf x -inf]; %// terminate data properly
starts = find(diff(y(1:end-1))<0 & diff(y(2:end))>=0);
ends = find(diff(y(1:end-1))>=0 & diff(y(2:end))<0);
result = histc(ends-starts+1, 1:numel(x));
For non-increasing sequences, just change inequalities and sign of infs:
y = [-inf x inf]; %// terminate data properly
starts = find(diff(y(1:end-1))>0 & diff(y(2:end))<=0);
ends = find(diff(y(1:end-1))<=0 & diff(y(2:end))>0);
result = histc(ends-starts+1, 1:numel(x));

Related

Algorithm for array permutation

We have an integer array A[] of size N (1 ≤ N ≤ 10^4), which originally is a sorted array with entries 1...N. For any permutation P of size N, the array is shuffled so that i-th entry from the left before the shuffle is at the Ai-th position after the shuffle. You would keep repeating this shuffle until the array is sorted again.
For example, for A[] = {1, 2, 3, 4}, if P = {1, 2, 3, 4}, it would only take one move for the array to be sorted (the entries would move to their original positions). If P = {4, 3, 1, 2}, then it would take 4 moves for the array to be sorted again:
Move 0 | [1, 2, 3, 4]
Move 1 | [3, 4, 2, 1]
Move 2 | [2, 1, 4, 3]
Move 3 | [4, 3, 1, 2]
Move 4 | [1, 2, 3, 4]
The problem is to find the sum of all positive integers J for which you can generate a permutation that requires J moves to get the array sorted again.
Example:
For A[] = {1, 2, 3, 4}, you can generate permutations that require 1, 2, 3, and 4 steps:
Requires 1 move: P = {1, 2, 3, 4}
Requires 2 moves: P = {1, 3, 2, 4}
Requires 3 moves: P = {1, 4, 2, 3}
Requires 4 moves: P = {4, 3, 1, 2}
So you would output 1 + 2 + 3 + 4 = 10.
Some observations I have made is that you can always generate a permutation that requires J moves for (1 ≤ J < N). This is because in the permutation, you would simply shift by 1 all the entries in the range of size J. However, for permutations that requires J moves where J ≥ N, you would need another algorithm.
The brute-force solution would be checking every permutation, or N! permutations which definitely wouldn't fit in run time. I'm looking for an algorithm with run time at most O(N^2).
EDIT 1: A permutation that requires N moves will always be guaranteed as well, as you can create a permutation where every entry is misplaced, and not just swapped with another entry. The question becomes how to find permutations where J > N.
EDIT 2: #ljeabmreosn made the observation that there exists a permutation that takes J steps if and only if there are natural numbers a_1 + ... + a_k = N and LCM(a_1, ..., a_k) = J. So using that observation, the problem comes down to finding all partitions of the array, or partitions of the integer N. However, this won't be a quadratic algorithm - how can I find them efficiently?
Sum of distinct orders of degree-n permutations.
https://oeis.org/A060179
This is the number you are looking for, with a formula, and some maple code.
As often when trying to compute an integer sequence, compute the first few values (here 1, 1, 3, 6, 10, 21) and look for it in the great "On-line Encyclopedia of Integer Sequences".
Here is some python code inspired by it, I think it fits your complexity goals.
def primes_upto(limit):
is_prime = [False] * 2 + [True] * (limit - 1)
for n in range(int(limit**0.5 + 1.5)):
if is_prime[n]:
for i in range(n*n, limit+1, n):
is_prime[i] = False
return [i for i, prime in enumerate(is_prime) if prime]
def sum_of_distinct_order_of_Sn(N):
primes = primes_upto(N)
res = [1]*(N+1)
for p in primes:
for n in range(N,p-1,-1):
pj = p
while pj <= n:
res[n] += res[n-pj] * pj
pj *= p
return res[N]
on my machine:
>%time sum_of_distinct_order_of_Sn(10000)
CPU times: user 2.2 s, sys: 7.54 ms, total: 2.21 s
Wall time: 2.21 s
51341741532026057701809813988399192987996798390239678614311608467285998981748581403905219380703280665170264840434783302693471342230109536512960230

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?

Filling missing data in a data set with constant values

I have a data set like the following:
x= [1, 4, 10]
y= [10, 20, 30]
(x and y are value pairs, i.e. (1,10), (4,20), (10,30))
I would like to fill the x values gaps and having constant values for y until the next known value pair comes.This should be done between each value pair, i.e. between (1,10) and (4,20) and then again between (4,20) and (10,30).
Input:
x=[1, 4, 10];
y=[10, 20, 30];
Output:
xi= [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
yi= [10,10, 10, 20, 20, 20, 20, 20, 20, 30];
How can Matlab solve this for me?
Assuming ascending order of elements in x, this could be one approach based on diff & cumsum -
%// Sample inputs
x=[1, 4, 10]
y=[-2, 5, -3]
xi = min(x):max(x)
yi = zeros(1,numel(xi))
yi(x) = diff([0 y])
yi = cumsum(yi)
Sample run -
x =
1 4 10
y =
-2 5 -3
xi =
1 2 3 4 5 6 7 8 9 10
yi =
-2 -2 -2 5 5 5 5 5 5 -3
Customary bsxfun solution to get yi -
lens = [diff(x) 1];
yi = nonzeros(bsxfun(#times,bsxfun(#ge,lens,[1:max(lens)]'),y)).'
Assuming that x always starts with a 1 and finishes with the final length of xi, this will work:
xi=1:x(end)
yi=y(arrayfun(#(xi)find(x<=xi,1,'last'),xi))

Find the length of the longest contiguous sub-array in a sorted array in which the difference between the end and start values is at most k

I have a sorted array, for example
[0, 0, 3, 6, 7, 8, 8, 8, 10, 11, 13]
Here, let's say k = 1 so the longest sub-array is [7, 8, 8, 8] with length = 4.
As another example, consider [0, 0, 0, 3, 6, 9, 12, 12, 12, 12] with k = 3. Here the longest sub-array is [9, 12, 12, 12, 12] with length = 5.
So far, I have used a binary search algorithm O(n log n) which iterates from index 0 .. n - 1 and tries to find the rightmost index that satisfies our condition.
Is there a linear time algorithm to do this?
Yes, there is a linear time algorithm. You can use two pointers technique. Here is a pseudo code:
R = 0
res = 0
for L = 0 .. N - 1:
while R < N and a[R] - a[L] <= k:
R += 1
res = max(res, R - L)
It has O(n) time complexity because L and R are strictly increasing and each of them can be incremented only n times.
Why is this algorithm correct? For a fixed L, R is the index of the first element of the array such that a[R] - a[L] > k. That's why R - 1 is the index of the last element that fits. The length of [L, R - 1] subarray is exactly R - L. The resulting subarray is obtained by iterating over all possible values of L, that is, all possibilities are checked. That's why it always finds correct answer.

Arrangement of elements to avoid duplicate on consecutive positions

I have an array with N elements and I have M numbers. U need to arrange the M(1, 2, 3, ..M) numbers in array, the numbers in M are repeated. Such that constitutive elements in the array are not same.
Ex : N=9 and M=3 [4, 4, 1] means 1 appears 4 times in the array, '2' appears 4 times and 3 appears only one time.
So the possible arrangement will be [1,2,1,2,1,2,3,1,2].
Ex : N=8 and M=2 [3, 5].
There is no possibility to arrange the elements such that two consecutive elements are not same.
I need to find weather the arrangement is possible or not.
Try something like this (Java syntax):
int max = M[0];
int sum = M[0];
for ( i = 1 ; i < M.length ; i++ ) {
if ( M[i] > max ) {
max = M[i];
}
sum = sum + M[i];
}
if ( 2*max <= s+1 ) {
System.out.println("possible");
} else {
System.out.println("NOT possible");
}
Time complexity: O(|M|)
The idea is that if you want to place those numbers into an array, you'll start with the longest sequence, then you choose the first longest sequence in order to avoid repeating.
E.g.:
----------- arr: []
1, 1, 1, 1
2, 2
3
----------- arr: [1]
1, 1, 1
2, 2
3
----------- arr: [1, 2]
1, 1, 1
2
3
----------- arr: [1, 2, 1]
1, 1
2
3
----------- arr: [1, 2, 1, 2]
1, 1
3
----------- arr: [1, 2, 1, 2, 1]
1
3
----------- arr: [1, 2, 1, 2, 1, 3]
1
----------- arr: [1, 2, 1, 2, 1, 3, 1]
So, if the maximum number from M is at most the sum of the others + 1, it's possible to obtain that array without repeats:
max <= sum_of_the_others + 1 | + max
which is equivalent to
max + max <= sum_of_all_numbers + 1
which is equivalent to
2*max <= sum_of_all_numbers + 1

Resources