【问题标题】:Loop through 3d matrix with increment in each directions successively?循环遍历 3d 矩阵并在每个方向上连续递增?
【发布时间】:2015-02-16 10:44:05
【问题描述】:

我不想要这个:

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

编辑:上面的代码片段会将程序发送到以下几点: (000) (001) (002) (003) (004) 然后(对于 X = 5 ) (010) (011) (012) (013) (014) 然后 (020) (021) ...

这对我的程序来说是浪费时间,因为我相信通过按以下顺序测试我会更快得到结果: (0,0,0) (1,0,0) (1,1,0) (0,1,0) (0,1,1) (0,0,1) (1,0,1) 等... 查看它的一种方法是通过矩阵递增,并尝试在每一步中获得到 (0,0,0) 增量的最小距离。

我希望它更清楚。

你知道一个聪明的编码方法吗?

编辑:按此顺序测试矩阵点对于解决此问题非常重要。

【问题讨论】:

  • 不清楚您所说的“一个角度并沿第一个对角线递增”是什么意思
  • 取一个 5*5*5 的立方体,让它站在一个角落,想象你正在穿过从那个角落 (0,0,0) 到对角 (5, 5,5) 沿着螺旋上升的轨迹。
  • 你应该考虑它是否真的对结果很重要。如果您发布的代码可以解决问题,为什么要更改它。我知道看到 2 个嵌套的 for 循环,我不知道你想用这个去哪里。
  • 我不明白这个问题;请提供更多索引的更详细示例。

标签: java loops matrix


【解决方案1】:

您需要按距离增加的顺序获取元素。 “距离”可以评估为d=sqrt(x*x+y*y+z*z),或者更有效的是d=x*x+y*y+z*z(因为平方根是单调的)

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

您只需要列出所有索引组合,然后按递增的sumsq 顺序排序。在 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][]);

您可以使用以前的 java 版本以更长的方式重写它。遍历 3d 矩阵将是:

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

5x5x5 是相对较小的 3d 数组,因此如果您必须进行多次迭代或每次迭代都是繁重的操作,则可能需要进行此类优化。然后列出所有索引组合并对其进行排序与​​其他工作相比应该可以忽略不计。数组r 应该只计算一次; n=5 只需不到一秒。

【讨论】:

    【解决方案2】:

    我没有完整的解决方案,但您可以尝试一些想法。基本上,您想要的是一个 3D flood fill 算法,您可以根据到起点的距离选择下一个点(距离越小越好)。

    从算法的 2D 版本开始,看看它是如何工作的,然后添加第三个维度。对于测试,您需要一个矩阵,它可以为每个坐标保持真/假以及快速距离算法。如果sqrt() 太慢,请尝试Manhattan Distance,如果所有坐标都是整数(没有小数位),这有时就足够了。

    【讨论】:

    • 是的,我也将其视为从起点开始的最小距离增量,但我认为详细说明步骤会更容易解释问题。吃完我看看你的链接,brb。
    【解决方案3】:

    您可以使用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());
        }
    }
    

    您也可以添加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);
        }
    }
    

    【讨论】:

    • 只是一个简单的问题:说您的解决方案是对我想要遵循的路径进行很好的硬编码是否正确?在这种情况下,这对我来说是可行的,因为我确实在 5 5 5 矩阵上工作,但对于任何大小的矩阵都失败了,对吗?
    • @Poutrathor - 使用带有预建访问点列表的Iterator 确实会对订单进行硬编码。如果您像我刚刚添加的那样使用Iterable,那么您可以构建访问点一次并在增强的for循环中使用它 - 这一切都非常好。它适用于任何大小的矩阵 - 它只需要定义的步骤。
    • @Poutrathor - 我已经调整了 Iterable 以按照您的需要进行 5X5。
    【解决方案4】:

    我不确定这种方法是否最适合你,但你可以试试这个:

    // 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)));
                }
            }
        }
    }
    

    对于不同的索引:

    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)));
    
                }
            }
        }
    }
    

    索引的示例输出:

    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
    

    如果您寻找“对角线”距离,那么您可以使用类似的东西,但将限制更改为更合适的东西。虽然您甚至可能需要添加额外的循环...

    【讨论】:

    • 我很困惑。我会考虑更多,但这确实是一个不错的小技巧。
    • @Poutrathor 我添加了一些 cmets 和代码的变体,它考虑了棱镜(每个方向的长度不同)而不仅仅是立方体。输出显示它从长度为 [2][3][3] 的矩阵迭代的索引
    【解决方案5】:

    螺旋顺序

    想象立方体站在它的角落。它可以被飞机切割。每个与立方体相交的平面都是三角形或六边形。对于该交点的每个点,交点处的坐标总和都相同。让我们称之为一个级别。

    0 级是微不足道的 - 它只是一个顶点。

    级别 1 包含三个顶点。它的螺旋路径包含两条线:向右一步,向下一步。

    级别 2 包含六个顶点。螺旋路径:向右两步,向下两步,向上一步。

    第 3 级:向右三步,向下三步,向上两步,向右一步。

    在 5x5x5 立方体中,我们有 1 个角顶点,5 个三角形,然后是 4 个六边形,然后是 5 个三角形,再次以最后一个角顶点结束。

    这是以螺旋顺序打印第一个三角形坐标的算法。六边形的算法也可以这样写。

    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 )
    

    你能看到图案吗?

    每个级别的模式重复几次。

    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
    

    所以我们得到:

    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
    

    应用这些模式可以得到正确的螺旋顺序。

    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 确定三角形的水平。 currentTurn 决定了我们改变螺旋方向的次数。 currentStep 决定了我们在当前方向经过了多少步。 currentEdgenextEdge 显示方向(我们从 currentEdge 移动到 nextEdge)。

    【讨论】:

      【解决方案6】:

      我用过的代码:

      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;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-10-29
        • 1970-01-01
        • 1970-01-01
        • 2021-04-13
        • 1970-01-01
        • 2019-01-20
        • 2020-05-30
        相关资源
        最近更新 更多