Java大数运算最短路径

时间:2017-02-11 15:58:18

标签: java algorithm performance recursion logic

一周前,我的教授向我提出了一个挑战,要求制作一个程序,对大数字进行三次操作,并给出字符串。我只能通过十个测试用例中的五个,并且无论如何都得到了A,但我仍然想知道你们会为这个问题做些什么,就编程技术或我没有想到的方法而言...... / p>

您将获得一个长度最多为309位的数字的字符串表示形式。您可以执行三项操作:
1)在数字上加1 2)减去1
3)除以2

程序的目的是找到可以对此数字执行的最短路径或最小操作量,以便结果为1.
例如:给出" 11"
1 - > 2 - > 3 - > 6 - > 12 - > 11个
结果:5步骤
我有两种方法不能100%工作:
1:从一个或数字本身开始,递归地逐步执行每个可能的答案,直到达到最大步数(例如11,20)的数字。
2:借助具有所有可能的permutaions的2-d布尔数组来定义所有可能的答案,然后逐个逐步浏览可能的移动集。此数组在概念上用作地图。

这两种方法都取得了有限的成功,我遇到了堆栈溢出错误,或者我的大型阵列内存不足。这迫使我限制步骤数,以便代码可以在某种程度上成功运行。你的方法是什么?

编辑下午1:30
尝试1(对不起,它已被严格编辑,因此之前没有显示...):

import java.math.BigInteger;
import java.util.Scanner;

public class Answer {

public static void main(String[] args) {
    // TODO Auto-generated method stub
    String str;
    Scanner sc = new Scanner(System.in);
    while (true) {
        str = sc.nextLine();
        System.out.println(answer(str));
    }
    // System.out.println(answer("15"));
}

private static BigInteger minNumOfJumps;
private static BigInteger big2 = BigInteger.valueOf(2);
/** smallest Number of jumps reached so far */
private static BigInteger smallestAmountOfJumps;

public static int answer(String string) {
    // TODO Auto-generated method stub
    BigInteger src = new BigInteger(string);
    // BigInteger currentJump = BigInteger.ZERO; //used to initialize the
    // nodes
    // minNumOfJumps = src.divide(big2).add(BigInteger.ONE); //this must
    // execute...
    minNumOfJumps = new BigInteger("14"); // this must execute...
    smallestAmountOfJumps = new BigInteger(minNumOfJumps.toString());
    // System.out.println(minNumOfJumps);
    Node n = new Node(src); // ...before this

    return Integer.parseInt(getSmallestAmountOfJumps().toString());
    // System.out.println(n.getSmallestAmountOfJumps().toString());
}

public static BigInteger getBig2() {
    return big2;
}

public static void setBig2(BigInteger big2) {
    Answer.big2 = big2;
}

public static BigInteger getMinNumOfJumps() {
    return minNumOfJumps;
}

public static BigInteger getSmallestAmountOfJumps() {
    return smallestAmountOfJumps;
}

public static void setSmallestAmountOfJumps(String smallestAmountOfJumps) {
    Answer.smallestAmountOfJumps = new BigInteger(smallestAmountOfJumps);
}
}

/*
* I have never made a shortest path algorithm before, so i hope this is toyour
* satisfaction
*/
class Node {

/** number of nodes per creation */
private static final int NUMBER_OF_NODES_PER_NODE = 3;
/** if this number is exceeded, no more jumps are necessary. */ // SAVE THAT
                                                                // THINKING
                                                                // JUICE!
// private static BigInteger POSSIBLE_MINIMUM_NUMBER_OF_JUMPS;

private static boolean lastTransformWasRemoveOne;
private static boolean lastTransformWasAddOne;

// if one is the given value(src)
// private boolean isOneReached;
/** if the current path isn't valid */
// private boolean threadIsBroken;
// value passed during creation
private BigInteger src;
// current jump
private BigInteger currentJump;

// all possible transformations during next jump
// private Node[] path;

private Node(BigInteger src, BigInteger jump) {
    currentJump = jump;
    this.src = src;
    // POSSIBLE_MINIMUM_NUMBER_OF_JUMPS = Answer.getMinNumOfJumps();
    // this.path = new Node[NUMBER_OF_NODES_PER_NODE];

    // 0 = remove | 1 = add | 2 = divide
    for (int i = 0; i < NUMBER_OF_NODES_PER_NODE; i++) {
        // System.out.println("i: " + i);
        // System.out.println("src: " + src);
        // System.out.println("compare: " +
        // currentJump.compareTo(smallestAmountOfJumps));
        // System.out.println("currentJump: " + currentJump);
        // System.out.println("smallestAmountOfJumps: " +
        // smallestAmountOfJumps);
        if (src.compareTo(BigInteger.ONE) == 0) {
            if (currentJump.subtract(Answer.getSmallestAmountOfJumps()).compareTo(BigInteger.ZERO) == -1) {
                Answer.setSmallestAmountOfJumps(currentJump.toString());
                // this below may break the code, but i think it fits with
                // the logic
                break;
            }
        } else if (i == 0) { // remove 1
            // System.out.println(lastTransformWasAddOne);
            // System.out.println("compare: " +
            // currentJump.compareTo(smallestAmountOfJumps));
            // System.out.println("currentJump: " + currentJump);
            // System.out.println("smallestAmountOfJumps: " +
            // smallestAmountOfJumps);
            if (!lastTransformWasAddOne && currentJump.compareTo(Answer.getSmallestAmountOfJumps()) < 0) {
                lastTransformWasRemoveOne = true;
                Node n = new Node(transform(i), currentJump.add(BigInteger.ONE));
            }
        } else if (i == 1 && !lastTransformWasRemoveOne
                && currentJump.compareTo(Answer.getSmallestAmountOfJumps()) < 0) { // add
                                                                                    // 1
            lastTransformWasAddOne = true;
            Node n = new Node(transform(i), currentJump.add(BigInteger.ONE));
        } else if (src.mod(Answer.getBig2()) == BigInteger.ZERO
                && currentJump.compareTo(Answer.getSmallestAmountOfJumps()) < 0) { // divide
                                                                                    // by
                                                                                    // 2
            lastTransformWasRemoveOne = false;
            lastTransformWasAddOne = false;
            Node n = new Node(transform(i), currentJump.add(BigInteger.ONE));
        } else if (currentJump.compareTo(Answer.getSmallestAmountOfJumps()) == 0)
            break;
    }
}

private BigInteger transform(int i) {
    // TODO Auto-generated method stub
    return (i == 0) ? src.subtract(BigInteger.ONE)
            : (i == 1) ? src.add(BigInteger.ONE) : (i == 2) ? src.divide(Answer.getBig2()) : BigInteger.ZERO;
}

/**
 * To be called once and only once.
 */
public Node(BigInteger src) {
    this(src, BigInteger.ZERO);
}

}`

这是另一次尝试:

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;

public class AnswerLessProficient {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        String str;
        Scanner sc = new Scanner(System.in);
        while (true) {
            str = sc.nextLine();
            System.out.println(answer(str));
        }
        // System.out.println(answer("15"));
    }

    private static boolean notFirstCall;
    private static boolean pathIsSet;
    private static boolean[][] boolArray;
    private static final String ZERO = "0";
    private static final BigInteger TWO = BigInteger.ONE.add(BigInteger.ONE);
    private static int maximumSteps;
    private static int maximumPermutations;
    private static ArrayList<byte[]> listOfPaths;
    private static Set<byte[]> setOfPaths;
    // private static final int maximumPermutations = halfMaximumPermutations *
    // 2;
    // private static byte[][] path;
    private static BigInteger src;
    private static int steps;
    private static BigInteger tempSrc;
    private static byte[] tempPath;
    // private static boolean followThePathsWithAlternateRoutesWasCalledOnce;

    public static int answer(String s) {
        // listOfPaths = new ArrayList<>();
        src = new BigInteger(s);
        tempSrc = new BigInteger(s);
        maximumSteps = 9;
        steps = maximumSteps;
        maximumPermutations = (int) Math.pow(2, maximumSteps);
        if (!notFirstCall) {
            tempPath = new byte[maximumSteps];
            setOfPaths = new HashSet<>();
            int mercyVar = (int) Math.pow(2, maximumSteps);
            // path = new byte[maximumPermutations][maximumSteps];
            // boolArray = new boolean[maximumPermutations][maximumSteps];
            for (int i = 0; i < mercyVar; i++) {
                listOfPaths = new ArrayList<>();
                String bin = (Integer.toBinaryString(i));
                while (bin.length() < maximumSteps)
                    bin = (ZERO + bin);
                char[] chars = bin.toString().toCharArray();
                byte[] tempPath = new byte[maximumSteps];
                for (int j = 0; j < maximumSteps; j++) {
                    // if (!pathIsSet)
                    // path[j] = -1;
                    if (chars[j] == '0') {
                        tempPath[j] = 2;
                        // path[i][j] = 2;
                        // boolArray[i][j] = true;
                    } else {
                        tempPath[j] = -1;
                        // path[i][j] = -1;
                    }
                }
                 //System.out.println(Arrays.toString(tempPath));
                listOfPaths.add(tempPath);
                setOfPaths.add(tempPath);
                findAltRoute(listOfPaths.size() - 1, maximumSteps - 1);
            }
            /*
             * for (int i = mercyVar, j = 0; i < maximumPermutations; i++, j++)
             * { for (int k = 0; k < maximumSteps; k++) { if (path[j][k] == -1)
             * { path[i][k] = 1; } else { path[i][k] = 2; } } }
             */

            // for (byte[] bs : setOfPaths) {
            // System.out.println(Arrays.toString(bs));
            // }
            /*
             * for (int i = maximumSteps - 1, k = 0; i >= 0 &&
             * tempSrc.compareTo(BigInteger.ZERO) > 0; i--, k++) { if
             * (tempSrc.compareTo(BigInteger.ONE) <= 0) if (k < steps) { steps =
             * k; maximumSteps = steps; System.out.println(Arrays.toString(bs));
             * break; } else break; if (bs[i] == 2 && tempSrc.mod(TWO) !=
             * BigInteger.ZERO) break; tempSrc = transform(tempSrc, bs[i]); }
             * tempSrc = src.add(BigInteger.ZERO);
             */
            // }

            // System.out.println(bin);
            /*
             * for (int j = 0; j < maximumSteps && i >= halfMaximumPermutations;
             * j++) { // if (!pathIsSet) // path[j] = -1; if (chars[j + 1] ==
             * '0') { path[i][j] = 2; // boolArray[i][j] = true; } else {
             * path[i][j] = 1; } }
             */
            // System.out.println(bin);
            // System.out.println(Arrays.toString(path[i]));
            // pathIsSet = true;
            notFirstCall = true;
        }
        justFollowThePath();
        // System.out.println(Arrays.toString(path[0]));
        // System.out.println
        // (Arrays.toString(path[(int) (maximumPermutations/2)-1]));
        // System.out.println(Arrays.toString(path[maximumPermutations-1]));

        /**
         * 561-508-2204 george rubio debt forgiveness; 305-709-8255
         */
        // for (int i = 0; i < maximumPermutations; i++) {
        // followThePathsWithAlternateRoutes(path[i], maximumSteps - 1);
        // }

        // followThePathsWithAlternateRoutesWasCalledOnce = false;

        /*
         * for (int i = 0; i < maximumPermutations; i++) { for (int k = 0; k <
         * maximumSteps; k++) {
         * 
         * }
         * 
         * for (int k = maximumSteps - 1; k > 0; k--) {
         * 
         * } }
         */

        // for (boolean[] bs : boolArray) {
        // System.out.println(Arrays.toString(bs));
        // }

        // System.out.println(Arrays.toString(boolArray[maximumPermutations -
        // 1]));
        // System.out.println(Arrays.toString(path));

        return steps;
    }

    private static void findAltRoute(int listIndex, int startingSearchIndex) {
        if (listOfPaths.get(listIndex)[startingSearchIndex] == -1) {
            // followThePathsWithAlternateRoutesWasCalledOnce = true;
            // recurAlt(tempPath, maximumSteps - 1, maximumSteps - 1, (byte) 1,
            // maximumSteps - 1);
            for (int i = startingSearchIndex - 1; i >= 0; i--) {
                if (listOfPaths.get(listIndex)[i] == 2) {
                    returnAltRoute(listIndex, i + 1, startingSearchIndex, (byte) 1, i);
                    findAltRoute(listIndex + 1, i);
                    return;
                }

                else if (i == 0) {
                    returnAltRoute(listIndex, i, startingSearchIndex, (byte) 1);
                    return;
                }
            }
        }
        for (int i = startingSearchIndex - 1; i >= 0; i--) {
            if (listOfPaths.get(listIndex)[i] == -1 && listOfPaths.get(listIndex)[i + 1] == 2) {
                if (i != 0) {
                    for (int k = i - 1; k >= 0; k--) {
                        if (listOfPaths.get(listIndex)[k] == 2 && listOfPaths.get(listIndex)[k + 1] == -1) {
                            // recurAlt(tempPath, i, k + 1, (byte) 1, k);
                            returnAltRoute(listIndex, k + 1, i, (byte) 1, k);
                            findAltRoute(listIndex, i);
                        }
                        // returnAltRoute(listIndex, 0, i, (byte)1);
                        // return;
                    }
                } else {
                    returnAltRoute(listIndex, 0, i, (byte) 1);
                    return;
                }
            }
        }
    }

    private static void returnAltRoute(int listIndex, int tempStart, int tempEnd, byte adjust, int returnSearchInt) {
        byte[] tempPath = new byte[listOfPaths.get(listIndex).length];
        for (int i = maximumSteps - 1; i >= 0; i--) {
            if (i >= tempStart && i <= tempEnd) {
                tempPath[i] = adjust;
            } else {
                tempPath[i] = listOfPaths.get(listIndex)[i];
            }
        }
         System.out.println(Arrays.toString(tempPath));
        setOfPaths.add(tempPath);
        listOfPaths.add(tempPath);
        maximumPermutations = setOfPaths.size();
        findAltRoute(listIndex, returnSearchInt);
    }

    private static void returnAltRoute(int listIndex, int tempStart, int tempEnd, byte adjust) {
        byte[] tempPath = new byte[listOfPaths.get(listIndex).length];
        for (int i = maximumSteps - 1; i >= 0; i--) {
            if (i >= tempStart && i <= tempEnd) {
                tempPath[i] = adjust;
            } else {
                tempPath[i] = listOfPaths.get(listIndex)[i];
            }
        }
         System.out.println(Arrays.toString(tempPath));
        setOfPaths.add(tempPath);
        listOfPaths.add(tempPath);
        maximumPermutations = setOfPaths.size();
    }

    private static void justFollowThePath() {
        for (byte[] bs : setOfPaths) {
            //System.out.println(tempSrc.toString());
            for (int i = 0; i < maximumSteps && tempSrc.compareTo(BigInteger.ZERO) > 0; i++) {
                if (tempSrc.compareTo(BigInteger.ONE) == 0)
                    if (i < steps) {
                        steps = i;
                        maximumSteps = steps;
                        //System.out.println(i);
                        // System.out.println(Arrays.toString(tempPath));
                        break;
                    } else
                        break;
                if (bs[i] == 2 && tempSrc.mod(TWO) != BigInteger.ZERO)
                    break;
                tempSrc = transform(tempSrc, bs[i]);
            }
            tempSrc = src.add(BigInteger.ZERO);

        }
    }

    private static void followThePathsWithAlternateRoutes(byte[] tempPath, int startingSearchIndex) {
        if (tempPath[maximumSteps - 1] == -1) {
            // followThePathsWithAlternateRoutesWasCalledOnce = true;
            recurAlt(tempPath, maximumSteps - 1, maximumSteps - 1, (byte) 1, maximumSteps - 1);
        }
        for (int i = startingSearchIndex - 1; i >= 0; i--) {
            if (tempPath[i] == -1 && tempPath[i + 1] == 2) {
                for (int k = i - 1; k > 0; k--) {
                    if (tempPath[k] == 2) {
                        recurAlt(tempPath, i, k + 1, (byte) 1, k);
                    }
                }
            }
        }

        System.out.println();
        for (int i = maximumSteps - 1, k = 0; i >= 0 && tempSrc.compareTo(BigInteger.ZERO) > 0; i--, k++) {
            if (tempSrc.compareTo(BigInteger.ONE) <= 0)
                if (k < steps) {
                    steps = k;
                    maximumSteps = steps;
                    System.out.println(Arrays.toString(tempPath));
                    break;
                } else
                    break;
            if (tempPath[i] == 2 && tempSrc.mod(TWO) != BigInteger.ZERO)
                break;
            tempSrc = transform(tempSrc, tempPath[i]);
        }
        tempSrc = src.add(BigInteger.ZERO);
    }

    private static BigInteger transform(BigInteger temp, byte i) {
        // TODO Auto-generated method stub
        return (i == -1) ? tempSrc.subtract(BigInteger.ONE)
                : (i == 1) ? tempSrc.add(BigInteger.ONE) : (i == 2) ? tempSrc.divide(TWO) : null;
    }

    private static void recurAlt(byte[] tempPath, int tempStart, int tempEnd, byte adjust, int returnSearchInt) {
        // TODO Auto-generated method stub
        byte[] temp = new byte[tempPath.length];
        for (int i = 0; i < temp.length; i++) {
            if (i >= tempStart && i <= tempEnd)
                temp[i] = adjust;
            else
                temp[i] = tempPath[i];
        }
        followThePathsWithAlternateRoutes(temp, returnSearchInt);
    }

}  

我还尝试过其他一些事情,但你可以看到我要去的地方。有什么指针吗?

2 个答案:

答案 0 :(得分:3)

如果数字是偶数,除以2.如果数字是3 mod 4,除非数字实际是3,否则加一。否则,减去一。重复,直到达到1。

这是一个证明。

首先请注意,如果数字是偶数,则除以2才有意义。因为如果你执行一些数字(比如2k)的+1或-1,那么除以2表示相同的除以2,然后加上或减去k 1&s。因此,首先除以2,即可保存操作。

所以唯一的问题是当数字是奇数时是加1还是减1。您可以通过归纳证明给定的策略是正确的。

奇数n是4x + 1或4x + 3。请注意,任何操作序列(当我们在可能的情况下除以2时)将在某个时刻达到x或x + 1.

我们将依次考虑这些中的每一个,并计算到x和x + 1的最短路径。 (省略了我正确识别的最短路径)。

在第一种情况下(4x + 1),通过先减去一个,我们可以在3个步骤中获得x(4x + 1-> 4x-> 2x-> x)和4个步骤中的x + 1( 4X + 1→4x-&GT; 2x-&GT; X-&GT; X + 1)。通过首先添加一个,我们可以在4个步骤(4x + 1-> 4x + 2-> 2x + 1-> 2x-> x)和x + 1(4个步骤)中获得x(4x + 1- &GT; 4X + 2→2×+ 1→2倍+ 2→X + 1)。所以我们总是先减去1。

在第二种情况(4x + 3)中,通过先减去一个,我们可以在4个步骤中得到x(4x + 3-> 4x + 2-> 2x + 1-> 2x-> x)和4 +步骤中的x + 1(4x + 3-> 4x + 2-> 2x + 1-> 2x + 2-> x + 1)。通过首先添加一个,我们可以在3个步骤(4x + 3-> 4x + 4-> 2x + 2-> x + 1)中获得x + 1,并且在4个步骤中获得x(4x + 3-&gt; ; 4X + 4-&GT; 2X + 2→X + 1→x)的。所以我们不妨在这种情况下首先添加1个。

在第二种情况下,如果x = 0(即n = 4 * 0 + 3 = 3),那么我们的推理并不是很有效,因为我们实际上并没有达到x 。在这种情况下,我们应该减1并除以2得到1。

问题标记为java,但是这里有一些伪代码(实际上是Python)来生成最佳链:

def chain(n):
    while n:
        print n,
        if n % 2 == 0:
            n //= 2
        elif n % 4 == 3 and n != 3:
            n += 1
        else:
            n -= 1

chain(11)

答案 1 :(得分:0)

我的第一种方法是使用BFS

只有2或3个状态转换(我假设我们不能分割奇数)。 但这种方法对于少数人来说只够快。

第二种方法是使用编号最小的A*作为启发式函数。例如,当我以“12”开头时,我可以去: “11”,“13”,“6”。我将为下一个州选择“6”,因为它更接近“1”。 然而,这种方法仍然不够快。

我的第三种方法是从“1”到“100”生成答案,然后寻找模式/公式。

编辑:删除错误的公式。