【问题标题】:Optimizing an algorithm java优化算法java
【发布时间】:2015-04-07 07:54:23
【问题描述】:

您好,我有以下方法。它的作用是找到从 N x M 矩阵的左上角到右下角的所有可能路径。我想知道优化速度的最佳方法是什么,因为它现在有点慢。然后将生成的路径存储在一个集合中。

编辑我忘了澄清你只能向下或向右移动到相邻的位置,不能从你当前的位置对角线

For example
ABC
DEF
GHI

从左上角到右下角的路径是 ADEFI

static public void printPaths (String tempString, int i, int j, int m, int n, char [][] arr, HashSet<String> palindrome) {
    String newString = tempString + arr[i][j];
    if (i == m -1 && j == n-1) {
        palindrome.add(newString);
        return;
    }
    //right
    if (j+1 < n) {
        printPaths (newString, i, j+1, m, n, arr, palindrome);
    }
    //down
    if (i+1 < m) {
        printPaths (newString, i+1, j, m, n, arr, palindrome);          
    }
}

编辑这是完整的代码

public class palpath {

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("palpath.in"));
        PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter("palpath.out")));
        StringTokenizer st = new StringTokenizer(br.readLine());

        int d = Integer.parseInt(st.nextToken());


        char[][] grid = new char [d][d];

        String index = null;

        for(int i = 0; i < d; i++)
        {
            String temp = br.readLine();
            index = index + temp;
            for(int j = 0; j < d; j++)
            {
                grid[i][j] = temp.charAt(j);
            }
        }

        br.close();

        int counter = 0;

        HashSet<String> set = new HashSet<String>();
        printPaths ("", 0, 0, grid.length, grid[0].length, grid, set);

        Iterator<String> it = set.iterator();
        while(it.hasNext()){
            String temp =  it.next();
            StringBuilder sb = new StringBuilder(temp).reverse();
            if(temp.equals(sb.toString()))  {
                counter++;
            }

        }




        pw.println(counter);
        pw.close();

    }


        static public void printPaths (String tempString, int i, int j, int m, int n, char [][] arr, HashSet<String> palindrome) {
            String newString = tempString + arr[i][j];
            if (i == m -1 && j == n-1) {
                palindrome.add(newString);
                return;
            }
            //right
            if (j+1 < n) {
                printPaths (newString, i, j+1, m, n, arr, palindrome);
            }
            //down
            if (i+1 < m) {
                printPaths (newString, i+1, j, m, n, arr, palindrome);          
            }
        }

【问题讨论】:

  • 真的是所有路径吗?因为在我看来,这包括循环和弯路。
  • 这还不够,代码通常也不是问题,给我们展示所有代码。
  • 我忘了说明你只能向下或向右移动到相邻的位置,不能从你当前的位置对角线
  • 从您的代码看来,您只对向右或向下的路径感兴趣。代码或问题是错误的。请澄清!
  • 据我了解,您正在尝试对矩阵进行排序。为什么不尝试 heapsort,因为它的复杂性最低。

标签: java algorithm matrix


【解决方案1】:

给定一个长度为 M x N 的图,从 (0,0) 到 (M-1, N-1) 的所有仅涉及向右和向下移动的路径都保证恰好包含 M-1 个向右移动和 N- 1 向下移动。

这为我们提供了一个有趣的属性:我们可以将从 (0,0) 到 (M-1, N-1) 的路径表示为二进制字符串(0 表示向右移动,1 表示向下移动)。

那么,问题就变成了:我们能以多快的速度打印出该位串的排列列表?

相当快。

public static void printPaths(char[][] arr) {
    /* Get Smallest Bitstring (e.g. 0000...111) */
    long current = 0;
    for (int i = 0; i < arr.length - 1; i++) {
        current <<= 1;
        current |= 1;
    }

    /* Get Largest Bitstring (e.g. 111...0000) */
    long last = current;
    for (int i = 0; i < arr[0].length - 1; i++) {
        last <<= 1;
    }

    while (current <= last) {
        /* Print Path */
        int x = 0, y = 0;
        long tmp = current;
        StringBuilder sb = new StringBuilder(arr.length + arr[0].length);
        while (x < arr.length && y < arr[0].length) {
            sb.append(arr[x][y]);
            if ((tmp & 1) == 1) {
                x++;
            } else {
                y++;
            }
            tmp >>= 1;
        }
        System.out.println(sb.toString());

        /* Get Next Permutation */
        tmp = (current | (current - 1)) + 1;
        current = tmp | ((((tmp & -tmp) / (current & -current)) >> 1) - 1);
    }
}

【讨论】:

    【解决方案2】:

    您在字符串内存管理上花费了大量时间。
    Java中的字符串是可变的吗?如果您可以更改字符串中的字符,则将字符串的长度设置为 n+m,并使用这个唯一的字符串,在每次迭代时设置第 (i+j) 个字符。如果它们不可变,请使用 char 数组或类似的数组,并在最后将其转换为字符串

    【讨论】:

    • Java 中的字符串确实是不可变的。您可以使用 char[] 或 StringBuilder 来减少字符串分配的数量,因为每个字符串连接都会有效地构造一个或多个新对象。也就是说,我还没有真正研究过你的算法足够长的时间来推断字符串处理是否真的是你的问题。
    【解决方案3】:

    对于给定大小 N×M 的数组所有,您的路径有 N+M+1 项(N+M 步),因此优化的第一步是摆脱递归,分配一个数组并在显式堆栈上使用while 运行递归。

    每个部分路径都可以通过一或两步进行扩展:向右或向下。因此,您可以轻松地创建一个显式堆栈,其中包含访问过的位置以及在每个位置上采取的步骤。将位置 (0,0) 放入堆栈,相位(已采取的步骤)'none',然后:

    while stack not empty {
        if stack is full /* reached lower-right corner, path complete */ {
            print the path;
            pop;
        }
        else if stack.top.phase == none {
            stack.top.phase = right;
            try push right-neighbor with phase none;
        }
        else if stack.top.phase == right {
            stack.top.phase = down;
            try push down-neighbor with phase none;
        }
        else /* stack.top.phase == down */ {
            pop;
        }
    }
    

    【讨论】:

    • 我不太确定你的意思。你能再解释一下吗?
    【解决方案4】:

    如果您对自己的需求进行一些观察,您可以大幅优化。

    1. 正好有 (r-1)+(c-1) 个步骤(其中 r = 行,c = 列)。
    2. 向右正好有 (c-1) 步,向下正好有 (r-1) 步。

    因此,您可以使用数字,其中 0 位可以(任意)表示向下步进,而 1 位则跨越。然后,我们可以仅迭代所有数量的 (r-1)+(c-1) 位,其中仅包含 (c-1) 位集。斯坦福 BitTwiddling 网站 Compute the lexicographically next bit permutation 有一个很好的算法。

    首先是我之前用过的BitPatternIterator。如果您愿意,可以提取hasNext 中的代码。

    /**
     * Iterates all bit patterns containing the specified number of bits.
     *
     * See "Compute the lexicographically next bit permutation" http://graphics.stanford.edu/~seander/bithacks.html#NextBitPermutation
     *
     * @author OldCurmudgeon
     */
    public static class BitPattern implements Iterable<BigInteger> {
    
        // Useful stuff.
        private static final BigInteger ONE = BigInteger.ONE;
        private static final BigInteger TWO = ONE.add(ONE);
        // How many bits to work with.
        private final int bits;
        // Value to stop at. 2^max_bits.
        private final BigInteger stop;
    
        // All patterns of that many bits up to the specified number of bits.
        public BitPattern(int bits, int max) {
            this.bits = bits;
            this.stop = TWO.pow(max);
        }
    
        @Override
        public Iterator<BigInteger> iterator() {
            return new BitPatternIterator();
        }
    
        /*
         * From the link:
         *
         * Suppose we have a pattern of N bits set to 1 in an integer and
         * we want the next permutation of N 1 bits in a lexicographical sense.
         *
         * For example, if N is 3 and the bit pattern is 00010011, the next patterns would be
         * 00010101, 00010110, 00011001,
         * 00011010, 00011100, 00100011,
         * and so forth.
         *
         * The following is a fast way to compute the next permutation.
         */
        private class BitPatternIterator implements Iterator<BigInteger> {
    
            // Next to deliver - initially 2^n - 1 - i.e. first n bits set to 1.
            BigInteger next = TWO.pow(bits).subtract(ONE);
            // The last one we delivered.
            BigInteger last;
    
            @Override
            public boolean hasNext() {
                if (next == null) {
                    // Next one!
                    // t gets v's least significant 0 bits set to 1
                    // unsigned int t = v | (v - 1);
                    BigInteger t = last.or(last.subtract(BigInteger.ONE));
                    // Silly optimisation.
                    BigInteger notT = t.not();
                    // Next set to 1 the most significant bit to change,
                    // set to 0 the least significant ones, and add the necessary 1 bits.
                    // w = (t + 1) | (((~t & -~t) - 1) >> (__builtin_ctz(v) + 1));
                    // The __builtin_ctz(v) GNU C compiler intrinsic for x86 CPUs returns the number of trailing zeros.
                    next = t.add(ONE).or(notT.and(notT.negate()).subtract(ONE).shiftRight(last.getLowestSetBit() + 1));
                    if (next.compareTo(stop) >= 0) {
                        // Dont go there.
                        next = null;
                    }
                }
                return next != null;
            }
    
            @Override
            public BigInteger next() {
                last = hasNext() ? next : null;
                next = null;
                return last;
            }
    
            @Override
            public void remove() {
                throw new UnsupportedOperationException("Not supported.");
            }
    
            @Override
            public String toString() {
                return next != null ? next.toString(2) : last != null ? last.toString(2) : "";
            }
    
        }
    
    }
    

    使用它来迭代您的解决方案:

    public void allRoutes(char[][] grid) {
        int rows = grid.length;
        int cols = grid[0].length;
        BitPattern p = new BitPattern(rows - 1, cols + rows - 2);
        for (BigInteger b : p) {
            //System.out.println(b.toString(2));
            /**
             * Walk all bits, taking a step right/down depending on it's set/clear.
             */
            int x = 0;
            int y = 0;
            StringBuilder s = new StringBuilder(rows + cols);
            for (int i = 0; i < rows + cols - 2; i++) {
                s.append(grid[y][x]);
                if (b.testBit(i)) {
                    y += 1;
                } else {
                    x += 1;
                }
            }
            s.append(grid[y][x]);
            // That's a solution.
            System.out.println("\t" + s);
        }
    }
    
    public void test() {
        char[][] grid = {{'A', 'B', 'C'}, {'D', 'E', 'F'}, {'G', 'H', 'I'}};
        allRoutes(grid);
        char[][] grid2 = {{'A', 'B', 'C'}, {'D', 'E', 'F'}, {'G', 'H', 'I'}, {'J', 'K', 'L'}};
        allRoutes(grid2);
    }
    

    打印

    ADGHI
    ADEHI
    ABEHI
    ADEFI
    ABEFI
    ABCFI
    ADGJKL
    ADGHKL
    ADEHKL
    ABEHKL
    ADGHIL
    ADEHIL
    ABEHIL
    ADEFIL
    ABEFIL
    ABCFIL
    

    这 - 在我看来 - 看起来不错。

    【讨论】:

      猜你喜欢
      • 2020-02-09
      • 1970-01-01
      • 2012-08-11
      • 1970-01-01
      • 1970-01-01
      • 2016-07-30
      • 2023-02-03
      • 1970-01-01
      相关资源
      最近更新 更多