【问题标题】:Best performance to search a number in a column of a 2d array without cloning the array在不克隆数组的情况下在二维数组的列中搜索数字的最佳性能
【发布时间】:2021-05-30 17:28:28
【问题描述】:

我正在尝试在二维数组的特定列中搜索一个数字。我尝试了几种不同的方法,并希望在 Java 8 中使用流。但是,它似乎并不是最好的性能。想知道是否有人可以提供帮助?

boolean isInColumn(int col, int number) {
    return IntStream.range(0, board.length)
        .map(i -> board[i][col])
        .filter(num -> num == number )
        .findFirst()
        .isPresent();
}

也尝试在一个块中搜索。有什么提示吗?

public boolean isInBlock(int row, int col, int number) {
    int r = row - row % 3;
    int c = col - col % 3;

    for (int i = r; i < r + 3; i++) {
        for (int j = c; j < c + 3; j++) {
            if (board[i][j] == number)
                return true;
        }
    }
    return false;
}

输入数据为如下数组。

public static int[][] PUZZLE = {
    {9,0,0,1,0,0,0,0,5},
    {0,0,5,0,9,0,2,0,1},
    {8,0,0,0,4,0,0,0,0},
    {0,0,0,0,8,0,0,0,0},
    {0,0,0,7,0,0,0,0,0},
    {0,0,0,0,2,6,0,0,9},
    {2,0,0,3,0,0,0,0,6},
    {0,0,0,2,0,0,9,0,0},
    {0,0,1,9,0,4,5,7,0},
};

【问题讨论】:

  • 您的性能问题似乎是什么?我检查了流版本与一种天真的方式相比,差异只有几毫秒,这很可能是由于测量方式错误。然而,天真的方式总是更快。 static boolean isInColumn2(int col, int number) { for (int[] ints : PUZZLE) { if (ints[col] == number) return true; } return false; }
  • @SzaPe 如果数据量会大得多,并且您需要实时处理它怎么办?即使是毫秒的差异也可能是至关重要的(当然,如果它们不是时间测量错误的结果)。
  • 如果数据可能更大,您应该首先提到这一点。我用一个 30000x30000 的数组对其进行了测试,两种方法都运行了 1000 次。幼稚的方式大约快 3-4 倍
  • 如果你正在做非常大的数组,那么使用parallelStream() 可能会很有趣。但请放心:对于任何低于“数千行/列”数量级的内容,朴素的老派幼稚代码将击败流式解决方案。您必须了解“流”意味着建立复杂的对象层次结构。它们为您提供易于阅读和维护的代码,如果您知道自己在做什么,可以为您提供良好的性能。但它们并不是为 Mac 性能设计的灵丹妙药!

标签: java arrays matrix search multidimensional-array


【解决方案1】:

这个“流”版本似乎有点优化,但我认为在数组中搜索命中总是比老式方法更快,请参阅Java performance tutorial – How fast are the Java 8 streams?

boolean isInColumn(int col, int number) {
    return IntStream.range(0, board.length)
        .anyMatch(i -> (board[i][col] == number) );
}

我对并行流进行了短暂的尝试,但开销让它变得更糟。 如果动作不是简单的比较,我认为会有所不同......

如果它只是关于数独求解器/生成器的速度,也许你根本不应该循环,而是将 9 个条件写在一个返回语句中

return board[0,col] == number || board[1,col] == number ...

【讨论】:

  • 好吧,除非您可以使用并行流,否则您可能会在非常大的数据和手头有许多 CPU 的系统上获得更快的结果。但只有这样
  • @GhostCat :我同意,我总是不小心使用,但是关于大数据的评论不是作者本人,问题是关于 9x9 数组。
  • 当然,但同时询问性能。如果 9x9 天真的解决方案比这里的流解决方案快几个数量级,我不会感到惊讶。
  • @GhostCat :再次正确,但假设它是一个数独求解器(或生成器,我认为 RoToRA 是正确的)将会有很多 isInColumn 和 isInBlock 调用。
  • 这让事情变得……更糟?
【解决方案2】:

由于这似乎是数独,您可以做的就是冗余地存储数据。不要只将数字“正常”存储在二维数组中,还要有二维布尔数组,存储行/列/块是否包含数字。

class Sudoku {

  private final int[][] puzzle = new int[9][9];
  
  private final boolean[][] rows = new boolean[9][9];
  private final boolean[][] columns = new boolean[9][9];
  private final boolean[][] blocks = new boolean[9][9];

  public void setCell(int row, in column, int number) {
    puzzle[row][column] = number;
    
    rows[row][number - 1] = true;
    columns[column][number - 1] = true;
    blocks[calcBlockId(row, column)][number - 1] = true;
 }

  // returns a number (0 - 8) identifying a block 
  // 0 - 2 is first line, 3 - 5 second line, etc.
  private int calcBlockId(int row, int column) {
    // Left as an exercise to the reader
  }
 
  public boolean isInColumn(int col, int number) {
     return columns[col][number - 1];
  }
 
  public boolean isInBlock(int row, int column, int number) {
    return blocks[calcBlockId(row, column)][number - 1];
  }
}

【讨论】:

    【解决方案3】:

    此代码在二维数组中搜索元素并返回第一个匹配项的坐标,如果存在这样的元素,否则返回 null:

    public static int[] findElement(int[][] arr, int element) {
        return IntStream
                // iterate through the indexes
                // of the rows of the array
                .range(0, arr.length)
                // for each row
                .mapToObj(i -> {
                    // look for the element in this row
                    int j = IntStream
                            // iterate through the indexes
                            // of the elements of the row
                            .range(0, arr[i].length)
                            // filter a matching element
                            .filter(el -> arr[i][el] == element)
                            // take first match
                            .findFirst().orElse(-1);
                    // if element is present
                    if (j >= 0)
                        // return its coordinates
                        return new int[]{i, j};
                    else
                        // or null otherwise
                        return null;
                })
                // take first non-null coordinates, if they are present
                .filter(Objects::nonNull).findFirst()
                // or null otherwise
                .orElse(null);
    }
    
    // test
    public static void main(String[] args) {
        int[][] puzzle = {
                {9, 0, 0, 1, 0, 0, 0, 0, 5},
                {0, 0, 5, 0, 9, 0, 2, 0, 1},
                {8, 0, 0, 0, 4, 0, 0, 0, 0},
                {0, 0, 0, 0, 8, 0, 0, 0, 0},
                {0, 0, 0, 7, 0, 0, 0, 0, 0},
                {0, 0, 0, 0, 2, 6, 0, 0, 9},
                {2, 0, 0, 3, 0, 0, 0, 0, 6},
                {0, 0, 0, 2, 0, 0, 9, 0, 0},
                {0, 0, 1, 9, 0, 4, 5, 7, 0}};
    
        int[] coordinates = findElement(puzzle, 7);
    
        System.out.println(Arrays.toString(coordinates)); // [4, 3]
    }
    

    另见:
    Difference between anyMatch and findAny in java 8
    First unique character in a string using LinkedHashMap

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-08-08
      • 1970-01-01
      • 2019-05-09
      • 2013-10-31
      • 1970-01-01
      • 1970-01-01
      • 2021-06-22
      相关资源
      最近更新 更多