如何计算最优解?

时间:2015-05-07 19:00:22

标签: java performance algorithm assembly

我有一些想象中的硬件。它只有2个内存地址和2个寄存器。它只有两个指令可用 - 从寄存器中减去一个存储器地址,并将寄存器的值移入存储器。它需要执行将值从一个存储器地址移动到另一个存储器地址的操作。存储器和寄存器中的四个值中的每一个都具有未知值。所有这些都是这样的:

存储

Memory #1 (m1) -> a
Memory #2 (m2) -> b
Register #1 (r1) -> c
Register #2 (r2) -> d

说明:

mov m r //Copies value at r into m
sub r m //Subtracts the value in m from r

可能的组合:

mov m1 r1
mov m1 r2
mov m2 r1
mov m2 r2
sub r1 m1
sub r1 m2
sub r2 m1
sub r2 m2

目标:

Set the value in m2 to equal a (which is the value initially stored in m1).

可能的解决方案(扰流警报!):

//Comments show respective value for m1, m2, r1, r2
//a b c d
mov m2 r1  //a c c d
sub r1 m2  //a c 0 d
mov m2 r1  //a 0 0 d
sub r1 m1  //a 0 -a d
mov m1 r1  //-a 0 -a d
sub r1 m1  //-a 0 0 d
sub r1 m1  //-a 0 a d
mov m2 r1  //-a a a d

这个解决方案需要8个动作。我试图找到一个更好的解决方案,通过编写一个程序来暴力破解所有解决方案(最多10个动作)。我用Java写的。这就是我想出的:

import java.util.ArrayList;
import java.util.Random;

public class AssemblyChallenge {

    //This is the main logic.
    public static void main(String[] args) {
        Memory memory1 = new Memory();
        Memory memory2 = new Memory();
        while (memory1.value == memory2.value) {
            memory2 = new Memory();
        }
        Register register1 = new Register();
        Register register2 = new Register();
        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 8; j++) {
                for (int k = 0; k < 8; k++) {
                    for (int l = 0; l < 8; l++) {
                        for (int m = 0; m < 8; m++) {
                            for (int n = 0; n < 8; n++) {
                                for (int o = 0; o < 8; o++) {
                                    for (int p = 0; p < 8; p++) {
                                        for (int q = 0; q < 8; q++) {
                                            for (int r = 0; r < 8; r++) {
                                                ArrayList<Integer> instructions = new ArrayList<>();
                                                instructions.add(i);
                                                instructions.add(j);
                                                instructions.add(k);
                                                instructions.add(l);
                                                instructions.add(m);
                                                instructions.add(n);
                                                instructions.add(o);
                                                instructions.add(p);
                                                instructions.add(q);
                                                instructions.add(r);
                                                runInstructions(instructions, memory1, memory2, register1, register2);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    //This runs an set of instruction sets
    public static void runInstructions(ArrayList<Integer> instructions, Memory memory1, Memory memory2, Register register1, Register register2) {
        int memoryValue1 = memory1.value;
        for (int instruction : instructions) {
            switch (instruction) {
                case 0:
                    moveRegisterIntoMemory(memory1, register1);
                    break;
                case 1:
                    moveRegisterIntoMemory(memory1, register2);
                    break;
                case 2:
                    moveRegisterIntoMemory(memory2, register1);
                    break;
                case 3:
                    moveRegisterIntoMemory(memory2, register2);
                    break;
                case 4:
                    subtractMemoryFromRegister(register1, memory1);
                    break;
                case 5:
                    subtractMemoryFromRegister(register1, memory2);
                    break;
                case 6:
                    subtractMemoryFromRegister(register2, memory1);
                    break;
                case 7:
                    subtractMemoryFromRegister(register2, memory2);
                    break;
            }
            if (memoryValue1 == memory2.value) {
//                System.out.println(instructions + " succeeded in 10 moves or less!");
            }
            System.out.println(instructions.size());
        }
    }

    //This takes the value in the register and copies it into the value at the memory
    public static void moveRegisterIntoMemory(Memory memory, Register register) {
        memory.value = register.value;
    }

    //This takes the value in the memory and subtracts it from the value in the register
    public static void subtractMemoryFromRegister(Register register, Memory memory) {
        register.value -= memory.value;
    }

    //This is the Memory class which stores a random value
    static class Memory {
        public int value;

        public Memory() {
            Random random = new Random();
            value = random.nextInt(20000) - 10000;
        }
    }

    //This is the Register class which stores a random value
    static class Register {
        public int value;

        public Register() {
            Random random = new Random();
            value = random.nextInt(20000) - 10000;
        }
    }
}

这个问题是它运行速度太慢,而且它似乎不会在实际时间内计算1,073,741,824种可能性。如何修复程序以便找到解决此问题的最佳方案?围绕着蛮力的方法吗?

2 个答案:

答案 0 :(得分:0)

感谢CptBartender和Harold,我有一个解决方案!它在大约1秒内打印出少于8条指令的所有解决方案。

$('.div').css("width",100-skill) // How to do 100-skill in %?

我只搜索少于8次移动的解决方案,输出为:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;

public class AssemblyChallenge {
    //Possible solutions to be confirmed.
    private static ArrayList<int[]> solutions = new ArrayList<>();

    //This is the main logic.
    public static void main(String[] args) {
        Memory memory1 = new Memory();
        Memory memory2 = new Memory();
        while (memory1.value == memory2.value) {
            memory2 = new Memory();
        }
        Register register1 = new Register();
        Register register2 = new Register();
        int startingMemoryValue1 = memory1.value;
        int startingMemoryValue2 = memory2.value;
        int startingRegisterValue1 = register1.value;
        int startingRegisterValue2 = register2.value;
        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 8; j++) {
                for (int k = 0; k < 8; k++) {
                    for (int l = 0; l < 8; l++) {
                        for (int m = 0; m < 8; m++) {
                            for (int n = 0; n < 8; n++) {
                                for (int o = 0; o < 8; o++) {
                                    for (int p = 0; p < 8; p++) {
                                        memory1.value = startingMemoryValue1;
                                        memory2.value = startingMemoryValue2;
                                        register1.value = startingRegisterValue1;
                                        register2.value = startingRegisterValue2;
                                        int[] instructions = new int[8];
                                        instructions[0] = i;
                                        instructions[1] = j;
                                        instructions[2] = k;
                                        instructions[3] = l;
                                        instructions[4] = m;
                                        instructions[5] = n;
                                        instructions[6] = o;
                                        instructions[7] = p;
                                        runInstructions(instructions, memory1, memory2, register1, register2);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        confirmSolutions();
    }

    //This runs an set of instruction sets
    public static boolean runInstructions(int[] instructions, Memory memory1, Memory memory2, Register register1, Register register2) {
        int memoryValue1 = memory1.value;
        int numMoves = 0;
        for (int instruction : instructions) {
            numMoves++;
            switch (instruction) {
                case 0:
                    moveRegisterIntoMemory(memory1, register1);
                    break;
                case 1:
                    moveRegisterIntoMemory(memory1, register2);
                    break;
                case 2:
                    moveRegisterIntoMemory(memory2, register1);
                    break;
                case 3:
                    moveRegisterIntoMemory(memory2, register2);
                    break;
                case 4:
                    subtractMemoryFromRegister(register1, memory1);
                    break;
                case 5:
                    subtractMemoryFromRegister(register1, memory2);
                    break;
                case 6:
                    subtractMemoryFromRegister(register2, memory1);
                    break;
                case 7:
                    subtractMemoryFromRegister(register2, memory2);
                    break;
            }
            if (memoryValue1 == memory2.value) {
                int[] solution = Arrays.copyOfRange(instructions, 0, numMoves);
                if (!isSolutionAlreadyFound(solution)) {
                    solutions.add(solution);
                }
                return true;
            }
        }
        return false;
    }

    private static boolean isSolutionAlreadyFound(int[] solution) {
        for (int[] foundSolution : solutions) {
            if (Arrays.equals(solution, foundSolution)) {
                return true;
            }
        }
        return false;
    }

    private static void confirmSolutions() {
        Memory memory1 = new Memory();
        Memory memory2 = new Memory();
        while (memory1.value == memory2.value) {
            memory2 = new Memory();
        }
        Register register1 = new Register();
        Register register2 = new Register();
        int startingMemoryValue1 = memory1.value;
        int startingMemoryValue2 = memory2.value;
        int startingRegisterValue1 = register1.value;
        int startingRegisterValue2 = register2.value;
        for (int i = 0; i < solutions.size(); i++) {
            memory1.value = startingMemoryValue1;
            memory2.value = startingMemoryValue2;
            register1.value = startingRegisterValue1;
            register2.value = startingRegisterValue2;
            boolean success = runInstructions(solutions.get(i), memory1, memory2, register1, register2);
            if (success && solutions.get(i).length < 8) {
                System.out.println(Arrays.toString(solutions.get(i)) + " was successful in " + solutions.get(i).length + " moves!");
            }
        }
    }

    //This takes the value in the register and copies it into the value at the memory
    public static void moveRegisterIntoMemory(Memory memory, Register register) {
        memory.value = register.value;
    }

    //This takes the value in the memory and subtracts it from the value in the register
    public static void subtractMemoryFromRegister(Register register, Memory memory) {
        register.value -= memory.value;
    }

    //This is the Memory class which stores a random value
    static class Memory {
        public int value;

        public Memory() {
            Random random = new Random();
            value = random.nextInt(20000) - 10000;
        }
    }

    //This is the Register class which stores a random value
    static class Register {
        public int value;

        public Register() {
            Random random = new Random();
            value = random.nextInt(20000) - 10000;
        }
    }
}

我必须克服的一次打嗝是偶然机会的正确解决方案。我的解决方案只是用不同的数字再次运行检查。当然,这确实有很小的机会产生双重误报。

可能的说明最少为7。

答案 1 :(得分:0)

要将m1移动到m2,仅使用r1的这7个指令序列看起来相当简单:

             m1 m2 r1 r2
              a  b  c  d
mov r1 m2        c   
sub m2 r1           0
sub m1 r1          -a
mov r1 m2       -a
sub m2 r1           0
sub m2 r1           a
mov r1 m2        a

由于没有寄存器来注册指令,因此不包含r2会引起误解,因为它不需要。