优化算法java

时间:2015-04-07 07:54:23

标签: java algorithm matrix

您好我有以下方法。它的作用是找到N×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);          
            }
        }

4 个答案:

答案 0 :(得分:3)

给定长度为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);
    }
}

答案 1 :(得分:0)

你花了很多时间在字符串内存管理上 Java中的字符串是否可变?如果你可以在字符串中更改字符,那么将字符串的长度设置为n + m,并使用这个唯一的字符串,在每次迭代时设置(i + j)个字符。如果它们不可变,请使用char或类似的数组,并在结尾处将其转换为字符串

答案 2 :(得分:0)

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

每个部分路径可以通过一个或两个步骤扩展:向右或向下。因此,您可以轻松地对访问过的位置以及每个位置采取的步骤进行显式堆栈。将位置(0,0)放入堆栈(阶段采取)'无',然后:

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

答案 3 :(得分:0)

如果您对自己的要求做了一些观察,可以大幅优化。

  1. 将会有(r-1)+(c-1)步骤(其中r =行和c =列)。
  2. 右边会有(c-1)步骤,而且(r-1)会向下走。
  3. 因此,您可以使用数字,其中零位可以(任意)指示向下步,而1位跨越。然后,我们可以仅迭代所有数量的仅包含(c-1)位的(r-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
    

    在我看来 - 看起来是正确的。