2
votes

I don't want this :

    for (int k=0; k<X; k++) {
        for (int j=0; j<X; j++) {
            for (int i=0; i<X; i++) {
                // do something
            }
        }
    }

Edit : this code snipet above will send the program to the following points : (000) (001) (002) (003) (004) then (for X = 5 ) (010) (011) (012) (013) (014) then (020) (021) ...

This is a waste of time for my program because I believe I will get results sooner by testing in this order : (0,0,0) (1,0,0) (1,1,0) (0,1,0) (0,1,1) (0,0,1) (1,0,1) etc... A way to see it is to increment through the matrix with trying to get the smallest distance to (0,0,0) increment possible at each step.

I hope it is more clear.

Do you know a clever way to code that ?

Edit : It totally matters for this problem solving to test the matrix points in this order.

6
It's not clear what you mean by "one angle and incrementing along the first diagonal" - texasflood
Take a 5*5*5 cube, make it stand on one corner, and imagine your are going through all the points from that corner (0,0,0) to the opposite corner (5,5,5) following a spiraling upward trail. - Poutrathor
You should concider if it really matters for the outcome. If the code you posted does the trick, why change it. I know see 2 nested for loops and i have no idea where you want to go with this. - WonderWorld
I don't understand the question; please provide a more detailed example with more indices. - Codor

6 Answers

2
votes

You need to get elements in order of increasing distance. "distance" can be evaluated as d=sqrt(x*x+y*y+z*z) or, more effectively as d=x*x+y*y+z*z (because square root is monotonic)

static int sumsq(int[] a){
    return IntStream.of(a).map(i -> i * i).sum();
}

All you need is to list all indexes combinations and sort then in increasing sumsq order. In java 8:

int n = 5;
int n3 = n * n * n;
int[][] r = IntStream.range(0, n3)  //0,1,2...124
        .mapToObj(i -> new int[]{i%n, i/n%n, i/n/n%n}) //(0,0,0),(1,0,0),(2,0,0)...
        .sorted((a1, a2) -> sumsq(a1) - sumsq(a2)) //(0,0,0),(1,0,0),(0,1,0)...
        .toArray(i -> new int[n3][]);

You can rewrite this in a longer way using previous java versions. Iteration through 3d matrix would be:

for (int i = 0; i < n3; ++i) {
    System.out.format("(%d,%d,%d)\n", r[i][0], r[i][1], r[i][2]);
}

5x5x5 is relatively small 3d array so such optimization can be needed if you either have to iterate many times or every iteration is heavy operation. Then listing all indexes combinations and sorting them should be at negligible cost comparing with other job. Array r should be calculated only once; it takes less than second for n=5.

1
votes

I have no complete solution but a few ideas what you can try. Basically, what you want is a 3D flood fill algorithm where you select the next point based on the distance to the starting point (smaller distance is better).

Start with a 2D version of the algorithm to see how it works and then add a third dimension. For the test, you'll need a matrix which can just hold true/false for each coordinate plus a fast distance algorithm. If sqrt() turns out to be too slow, try Manhattan Distance which is sometimes enough if all coordinates are integers (without decimal places).

1
votes

You could use an Iterator.

public class Point3D {

    // NOT the way to do it but done like this for sumplicity.
    int x, y, z;

    public Point3D(int x, int y, int z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    public Point3D(Point3D p) {
        this(p.x, p.y, p.z);
    }

    public Point3D add(Point3D p) {
        return new Point3D(x + p.x, y + p.y, z + p.z);
    }

    public String toString() {
        return "{" + x + "," + y + "," + z + "}";
    }
}

public class Point3DIterator implements Iterator<Point3D> {

    final Point3D start;
    final List<Point3D> steps;
    int i = 0;
    Point3D next;

    public Point3DIterator(Point3D start, List<Point3D> steps) {
        this.start = start;
        this.steps = steps;
        this.next = start;
    }

    @Override
    public boolean hasNext() {
        if (next == null) {
            if (i < steps.size() - 1) {
                next = start.add(steps.get(++i));
            }
        }
        return next != null;
    }

    @Override
    public Point3D next() {
        Point3D n = next;
        next = null;
        return n;
    }

}

public void test() {
    // The traversal order.
    List<Point3D> steps = Arrays.asList(new Point3D(0, 0, 0), new Point3D(1, 0, 0), new Point3D(1, 1, 0), new Point3D(0, 1, 0), new Point3D(0, 1, 1), new Point3D(0, 0, 1), new Point3D(1, 0, 1));
    // The iterator.
    Iterator<Point3D> i = new Point3DIterator(new Point3D(10, 10, 10), steps);
    while (i.hasNext()) {
        System.out.println(i.next());
    }
}

You can also add an Iterable

public class Around implements Iterable<Point3D> {

    final List<Point3D> steps;
    final Point3D center;

    public Around(Point3D center, int d) {
        this.center = center;
        steps = new ArrayList<>(24);
        for (int x = -d; x <= d; x++) {
            for (int y = -d; y <= d; y++) {
                for (int z = -d; z <= d; z++) {
                    if (x != 0 || y != 0 || z != 0) {
                        steps.add(new Point3D(x, y, z));
                    }
                }
            }
        }
    }

    @Override
    public Iterator<Point3D> iterator() {
        return new Point3DIterator(center, steps);
    }

}

public void test() {
    for (Point3D p : new Around(new Point3D(10, 10, 10), 2)) {
        System.out.println(p);
    }
}
1
votes

I'm not sure this approach is the best for you, but you could try this:

// let X be your matrix length in every direction
// we iterate through the sum of the indexes in the matrix
for (int k = 0; k <= 3 * (X - 1); k++) {
    // now we distribute this value k through the 3 indexes in every way
    // possible (taking care not to surpass the matrix length)
    int limitOne = k < X ? k : X - 1; // limit for the first index
    for (int j = 0; j <= limitOne; j++) {
        int limitTwo = (k - j) < X ? k - j : X - 1;// limit for the
                                                    // second index
        for (int i = 0; i <= limitTwo; i++) {
            // the third index is calculated by extracting the other 2
            // indexes so we need to make sure it isn't too big
            if (k - (j + i) < X) { // limit for the third index

                // do something with yourMatrix[j][i][k-(j+i)]
                // System.out.println("Index: " + j + " " + i + " "
                // + (k - (j + i)));
            }
        }
    }
}

And for different indexes:

int[][][] yourMatrix = new int[2][3][3];
// note that we need the length to be constant through the indexes
int x = yourMatrix.length;
int y = yourMatrix[0].length;
int z = yourMatrix[0][0].length;
// we iterate through the sum of the indexes in the matrix
for (int k = 0; k <= 3 * (x + y + z - 3); k++) {
    // now we distribute this value k through the 3 indexes in every way
    // possible (taking care not to surpass the matrix length)
    int limitOne = k < x ? k : x - 1; // limit for the first index
    for (int j = 0; j <= limitOne; j++) {
        int limitTwo = (k - j) < y ? k - j : y - 1; // limit for the
                                                    // second index
        for (int i = 0; i <= limitTwo; i++) {
            if (k - (j + i) < z) { // limit for the third index

                // do something with yourMatrix[j][i][k-(j+i)]
                System.out.println("Index: " + j + " " + i + " "
                        + (k - (j + i)));

            }
        }
    }
}

Example output of the indexes:

Index: 0 0 0
Index: 0 0 1
Index: 0 1 0
Index: 1 0 0
Index: 0 0 2
Index: 0 1 1
Index: 0 2 0
Index: 1 0 1
Index: 1 1 0
Index: 0 1 2
Index: 0 2 1
Index: 1 0 2
Index: 1 1 1
Index: 1 2 0
Index: 0 2 2
Index: 1 1 2
Index: 1 2 1
Index: 1 2 2

If you look for the "diagonal" distance then you could use something similar but changing the limits for something more appropriate. Although you might even need to add additional loops...

1
votes

Spiral order

Imagine the cube standing on its corner. It can be cut by planes. Each plane intersection with cube is a triangle or a hexagon. Sum of the coordinates at the intersection is the same for every point of this intersection. Let's call it a level.

Cube levels

Level 0 is trivial - it's just a vertex.

Level 1 contains three vertexes. It's spiral path contains two lines: one step to the right and one step to the bottom.

Level 1

Level 2 contains six vertexes. Spiral path: two steps to the right, two steps to the bottom, one step to the top.

Level 2

Level 3: three steps to the right, three steps to the bottom, two steps to the top, one step to the right.

Level 3

In the cube 5x5x5 we have 1 corner vertex, 5 triangles then 4 hexagons and then 5 triangles again ending by the last corner vertex.

Here is the algorithm that prints coordinates of the first triangles in spiral order. The algorithm for hexagons can be written in the same manner.

Each level coordinates sum equals to the level.
Level 0: ( 0, 0, 0 )
Level 1: ( 1, 0, 0 ) - decrement value at 0 and increment value at 1 to get next vertex coordinates
         ( 0, 1, 0 ) - decrement 1 and increment 2 to get next coordinates
         ( 0, 0, 1 )
Level 2: ( 2, 0, 0 ) - dec 0, inc 1 to get next line
         ( 1, 1, 0 ) - dec 0, inc 1
         ( 0, 2, 0 ) - dec 1, inc 2
         ( 0, 1, 1 ) - dec 1, inc 2
         ( 0, 0, 2 ) - dec 2, inc 0
         ( 1, 0, 1 )
Level 3: ( 3, 0, 0 ) - dec 0, inc 1
         ( 2, 1, 0 ) - dec 0, inc 1
         ( 1, 2, 0 ) - dec 0, inc 1
         ( 0, 3, 0 ) - dec 1, inc 2
         ( 0, 2, 1 ) - dec 1, inc 2
         ( 0, 1, 2 ) - dec 1, inc 2
         ( 0, 0, 3 ) - dec 2, inc 0
         ( 1, 0, 2 ) - dec 2, inc 0
         ( 2, 0, 1 ) - dec 0, inc 1
         ( 1, 1, 1 )

Can you see the pattern?

Each level's pattern repeats several times.

Level 1: pattern 1 then pattern 2
Level 2: pattern 1 twice then pattern 2 twice, then pattern 3
Level 3: pattern 1 thrice then pattern 2 thrice, then pattern 3 twice, then pattern 1 again

So we get:

Level 1: 1, 1
Level 2: 2, 2, 1
Level 3: 3, 3, 2, 1
Level 4: 4, 4, 3, 2, 1
Level 5: 5, 5, 4, 3, 2, 1

Applying these patterns gives the correct spiral order.

import java.util.Arrays;
import java.util.Iterator;

public class CoordsPrinter {
  public static final int COORDS_CNT = 3;

  public static class CoordsIterator implements Iterator< int[] > {
    private final int maxLevel;
    private final int coords[];
    private int currentLevel;
    private int currentTurn;
    private int currentStep;
    private int currentEdge;

    public CoordsIterator( int max ) {
      this.maxLevel = max;
      coords = new int[ COORDS_CNT ];
    }

    @Override
    public boolean hasNext() {
      return currentLevel <= maxLevel;
    }

    @Override
    public int[] next() {
      int ret[] = coords.clone();
      int stepsQuantity = currentTurn == 0 ? currentLevel : currentTurn == currentLevel ? 2 : currentLevel - currentTurn + 1;
      int nextEdge = currentEdge + 1;
      if ( nextEdge == COORDS_CNT )
        nextEdge = 0;
      coords[ currentEdge ]--;
      coords[ nextEdge ]++;
      currentStep++;
      if ( currentStep >= stepsQuantity ) {
        currentTurn++;
        currentStep = 0;
        if ( currentTurn > currentLevel ) {
          currentLevel++;
          currentTurn = 0;
          currentEdge = 0;
          Arrays.fill( coords, 0 );
          coords[ 0 ] = currentLevel;
        } else
          currentEdge = nextEdge;
      }
      return ret;
    }
  }

  public static class CoordsIterable implements Iterable< int[] > {
    private final int maxLevel;

    public CoordsIterable( int max ) {
      this.maxLevel = max;
    }

    @Override
    public Iterator< int[] > iterator() {
      return new CoordsIterator( maxLevel );
    }
  }

  public static void main( String args[] ) {
    for ( int coords[] : new CoordsIterable( 5 ) )
      System.out.println( Arrays.toString( coords ) );
  }
}

currentLevel determines the level of the triangle. currentTurn determines how many times we have changed the direction of spiral. currentStep determines how many steps we have passed at the current direction. currentEdge and nextEdge show the direction (we move from currentEdge to nextEdge).

0
votes

The code I have used :

public Space getNextFreeSpace() {

    double distanceToOrigin = 0;
    double smallestDistanceYet=100;
    Space nearestFreeSpace = new Space(); // = originSpace
    for (int k = 0; k<5; k++) {
        for (int l = 0; l<5; l++) {
            for (int m = 0; m<5; m++) {

                if (workingMatrix[k][l][m] == 0) { // That space is free, let's check the 6 directions around it
                    distanceToOrigin = Math.sqrt(k*k+l*l+m*m);
                    if (distanceToOrigin<smallestDistanceYet) {
                        smallestDistanceYet = distanceToOrigin;
                        nearestFreeSpace = new Space(k, l, m, isPair(k, l, m));
                    }
                }
            }
        }
    }
    return nearestFreeSpace;
}