【问题标题】:Is there a "faster" way to iterate through a two-dimensional array than using nested for loops?与使用嵌套 for 循环相比,是否有一种“更快”的方式来遍历二维数组?
【发布时间】:2018-03-22 13:11:39
【问题描述】:

我编写了一个程序来加密和解密使用四方密码读取的文件。目前,我将文件存储到一个字符数组中,将其传递给一个将其分解为二元组的函数,并使用另一个函数中的嵌套 for 循环对其进行加密。在这一点上,我基本上是在尝试优化运行时间。是否有另一种方法来遍历我正在使用的二维数组,它不使用两个 for 循环,或者最多只使用一个 for 循环?下面是相关代码:

FileHandler.java

   import java.io.*;
    import java.util.*;

    public class FileHandler {
    private List<String> characters = new ArrayList<String>();
    public char fileToChar[];

    public void preEncryptionFile(String fileText) throws IOException {
        String line;

        FileInputStream fileReader = new FileInputStream(fileText);
        DataInputStream dataInputStream = new DataInputStream(fileReader);
        BufferedReader bufferedReader = 
               new BufferedReader(new InputStreamReader(dataInputStream));

        while ((line = bufferedReader.readLine()) != null) {
            characters.add(line);
        }

        String charsToString = characters.toString();

        charsToString = charsToString.replaceAll("[^a-zA-Z]", "").toUpperCase();

        fileToChar = charsToString.toCharArray();

        bufferedReader.close();
    }
}

FourSquareCipher.java

    import java.util.*;

    public class FourSquareCipher {
        List<Character> encryptionList = new ArrayList<Character>();
        List<Character> decryptionList = new ArrayList<Character>();

        private char[][] matrix = { 
                { 'A', 'B', 'C', 'D', 'E', 'Z', 'G', 'P', 'T', 'F' },
                { 'F', 'G', 'H', 'I', 'K', 'O', 'I', 'H', 'M', 'U' }, 
                { 'L', 'M', 'N', 'O', 'P', 'W', 'D', 'R', 'C', 'N' },
                { 'Q', 'R', 'S', 'T', 'U', 'Y', 'K', 'E', 'Q', 'A' }, 
                { 'V', 'W', 'X', 'Y', 'Z', 'X', 'V', 'S', 'B', 'L' },
                { 'M', 'F', 'N', 'B', 'D', 'A', 'B', 'C', 'D', 'E' }, 
                { 'C', 'R', 'H', 'S', 'A', 'F', 'G', 'H', 'I', 'K' },
                { 'X', 'Y', 'O', 'G', 'V', 'L', 'M', 'N', 'O', 'P' }, 
                { 'I', 'T', 'U', 'E', 'W', 'Q', 'R', 'S', 'T', 'U' },
                { 'L', 'Q', 'Z', 'K', 'P', 'V', 'W', 'X', 'Y', 'Z' } };

        public void encryptionBigram(char[] fileToText) {
            int i;
            char x, y;

            for (i = 0; i < fileToText.length - 1; i += 2) {
                x = fileToText[i];
                y = fileToText[i + 1];

                encryption(x, y);
            }
        }

        private void encryption(char x, char y) {
            int i, j;
            int a, b, c, d;

            a = b = c = d = 0;

            for (i = 0; i < 5; i++) {
                for (j = 0; j < 5; j++) {
                    if (x == matrix[i][j]) {
                        a = i;
                        b = j;
                    }
                }
            }

            for (i = 5; i < 10; i++) {
                for (j = 5; j < 10; j++) {
                    if (y == matrix[i][j]) {
                        c = i;
                        d = j;
                    }
                }
            }

            encryptionList.add(matrix[a][d]);
            encryptionList.add(matrix[c][b]);
        }
}

【问题讨论】:

  • @DanielGale 我阅读它的方式 OP 正在使用该正则表达式删除任何非 alpha 内容。
  • List.toString()不是将字符串列表加入一个字符串的正确方法。考虑使用例如StringUtils.join() 来自 Apache Commons Lang。
  • 你正在做 3 个循环。对于您的 encryptionBigram 中的流,这可能是一个很好的用例。这样,流 api 可以在多个线程上执行加密。此外,您可能希望将数组展平为一维,以便您可以使用一个 for 循环进行迭代。 softwareengineering.stackexchange.com/questions/212808/…
  • 您应该在设置值之后添加一个break,一旦您遇到该值,您就可以停止循环。 (基本上减少了过多的评估)
  • Dont' ;) 这是一个容易犯的错误。也有人会说我错了。有些案例可能是“基于上下文”的,所以请拭目以待,看看是否有更多分享我的想法

标签: java arrays for-loop


【解决方案1】:

使用break; 将允许您在匹配值后立即停止循环,而不是在找到并设置值后继续循环。当然,如果您的值在两个循环的末尾,这可能需要更长的时间。如果您的值在两个循环的前面,那么您基本上会删除所有额外的计算。您可以通过将它们放在循环中较早找到的位置来更改映射以考虑更高频率的字母。

    for (i = 5; i < 10; i++) {
        for (j = 5; j < 10; j++) {
            if (y == matrix[i][j]) {
                c = i;
                d = j;
                break; // Using break here allows you to do the minimum comparisons.
            }
        }
    }

【讨论】:

    【解决方案2】:

    Asymptotically,没什么可做的,因为您的程序仍然具有 O(n) 时间复杂度 - 它读取每个字符并在每个字符的两个嵌套 for 循环中执行固定 50 次迭代,以及其他一些恒定时间工作.由于您必须阅读所有输入,因此您无法渐进地变得更好。

    但是,如果您想稍微加快程序速度,那么嵌套的 for 循环确实是开始的地方 - 在每个循环中,您实际上是在执行查找 - 遍历 25 个位置并寻找匹配项,然后返回行和列。这可以通过创建将每个字母映射到其位置数据的Map 来改进,并且get() 具有恒定的渐近时间复杂度。

    【讨论】:

      【解决方案3】:

      使用单个循环迭代二维数组。

      更具体地说,此代码根据当前索引和子数组的宽度计算行和列。

      这仅在子数组具有固定长度/宽度时才有效。

      我没有对此进行基准测试,所以我不能确定它是否比你目前拥有的更有效。检查第二个条件和增加第二个变量的开销可能与计算行和列相同(甚至更少)。

      永不过时:

      char[][] letters = {
          {'A', 'B', 'C'},
          {'D', 'E', 'F'},
          {'G', 'H', 'I'}
      };
      
      int width = 3;
      int maxIndex = letters.length * width;
      for(int i = 0; i < maxIndex; i++) {
          int row = i / width; // determines row
          int column = i % width; // determines column
      
          System.out.println("Value["+letters[row][column]+"] Row["+row+"] Column["+column+"]");
      }
      

      行由索引(由i 表示)除以宽度来确定。由于上例中子数组的宽度为3

      • 如果索引为6,则该行为2。
      • 如果索引为9,则该行为3。
      • 如果索引为11,则该行为3.66(仍为3)

      该列由模运算确定。由于上例中的每个子数组的宽度都是3

      • 如果索引为6,则列为0。
      • 如果索引为9,则列为0。
      • 如果索引为11,则列为2。

      【讨论】:

      • 谢谢。我试试看。
      • @Hollow 请让我知道结果,以便我可以将它们包含在帖子中。我通过手机解决了这个问题,无法进行基准测试
      • 差不多。可能会非常稍微快一点。
      猜你喜欢
      • 2016-01-20
      • 1970-01-01
      • 2015-05-07
      • 2010-11-03
      • 2016-10-08
      • 2021-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多