一个操作插入程序的优化算法



我的程序目前接受N个数字,然后是一个目标。它在数字之间插入"+"或"*"来尝试达到目标。如果它能达到目标,它将打印出正确的操作。然而,它找到答案的方式是通过蛮力,这对于N个数字的大集合是不够的。我当前的代码如下:

public class Arithmetic4{
  private static ArrayList<String> input = new ArrayList<String>();
  private static ArrayList<String> second_line = new ArrayList<String>();
  private static ArrayList<Integer> numbers = new ArrayList<Integer>();
  private static ArrayList<String> operations = new ArrayList<String>();
  private static ArrayList<Integer> temp_array = new ArrayList<Integer>();
  public static void main(String [] args){
    Scanner sc = new Scanner(System.in);
    while(sc.hasNextLine()){
      readInput(sc);
    }
  }
  public static void readInput(Scanner sc){
    String line = sc.nextLine();
    input.add(line);
    line = sc.nextLine();
    second_line.add(line);
    dealInput();
  }
  public static void dealInput(){
    String numberS = input.get(0);
    String[] stringNumbers = numberS.split("\s+");
    for(int i = 0; i < stringNumbers.length; i++){
      String numberAsString = stringNumbers[i];
      numbers.add(Integer.parseInt(numberAsString));
    }
    String orderString = second_line.get(0);
    String[] stringWhatWay = orderString.split("\s+");
    int target = Integer.parseInt(stringWhatWay[0]);
    char whatway = stringWhatWay[1].charAt(0);
    long startTime = System.currentTimeMillis();
    whatEquation(numbers, target, whatway);
    long elapsedTime = System.currentTimeMillis() - startTime; 
    long elapsedMSeconds = elapsedTime / 1;
    System.out.println(elapsedMSeconds);
    numbers.clear();
    input.clear();
    second_line.clear();
  }
  public static void whatEquation(ArrayList<Integer> numbers, int target, char whatway){
    if(whatway != 'L' && whatway != 'N'){
      System.out.println("Not an option");
    }
    if(whatway == 'N'){
      ArrayList<Integer> tempo_array = new ArrayList<Integer>(numbers);
      int count = 0;
      for (int y: numbers) {
        count++;
      }
      count--;
      int q = count;
      calculateN(numbers, target, tempo_array, q);
    }
    if (whatway == 'L'){
      if(numbers.size() == 1){
        System.out.println("L " + numbers.get(0));
      }
      ArrayList<Integer> temp_array = new ArrayList<Integer>(numbers);
      calculateL(numbers, target, temp_array);
    }
  }     
  public static void calculateN(ArrayList<Integer> numbers, int target, ArrayList<Integer> tempo_numbers, int q){
    int sum = 0;
    int value_inc = 0;
    int value_add;
    boolean firstRun = true;
    ArrayList<Character> ops = new ArrayList<Character>();
    ops.add('+');
    ops.add('*');
    for(int i = 0; i < Math.pow(2, q); i++){
      String bin = Integer.toBinaryString(i);
      while(bin.length() < q)
        bin = "0" + bin;
      char[] chars = bin.toCharArray();
      List<Character> oList = new ArrayList<Character> ();
      for(char c: chars){
        oList.add(c);
      }
      ArrayList<Character> op_array = new ArrayList<Character>();
      ArrayList<Character> temp_op_array = new ArrayList<Character>();
      for (int j = 0; j < oList.size(); j++) {
        if (oList.get(j) == '0') {
          op_array.add(j, ops.get(0));
          temp_op_array.add(j, ops.get(0));
        } else if (oList.get(j) == '1') {
          op_array.add(j, ops.get(1));
          temp_op_array.add(j, ops.get(1));              
        }
      }
      sum = 0;
      for(int p = 0; p < op_array.size(); p++){
        if(op_array.get(p) == '*'){
          int multiSum = numbers.get(p) * numbers.get(p+1);
          numbers.remove(p);
          numbers.remove(p);
          numbers.add(p, multiSum);
          op_array.remove(p);
          p -= 1;
        }
      }
      for(Integer n: numbers){
        sum += n;
      }
      if(sum != target){
        numbers.clear();
        for (int t = 0; t < tempo_numbers.size(); t++) {
          numbers.add(t, tempo_numbers.get(t));
        }
      }
      if (sum == target){
        int count_print_symbol = 0;
        System.out.print("N ");
        for(int g = 0; g < tempo_numbers.size(); g++){
          System.out.print(tempo_numbers.get(g) + " ");
          if(count_print_symbol == q){
            break;
          }
          System.out.print(temp_op_array.get(count_print_symbol) + " ");
          count_print_symbol++;
        }
        System.out.print("n");
        return;
      }          
    }
    System.out.println("N is Impossible");
  }     
  public static void calculateL(ArrayList<Integer> numbers, int target, ArrayList<Integer> temp_array){
    int op_count = 0;
    int sum = 0;
    int n = (numbers.size() -1);
    boolean firstRun = true;
    for (int i = 0; i < Math.pow(2, n); i++) {  
      String bin = Integer.toBinaryString(i);
      while (bin.length() < n)
        bin = "0" + bin;
      char[] chars = bin.toCharArray();
      char[] charArray = new char[n];          
      for (int j = 0; j < chars.length; j++) {
        charArray[j] = chars[j] == '0' ? '+' : '*';
      }
      //System.out.println(charArray);
      for(char c : charArray){
        op_count++;
        if(firstRun == true){
          sum = numbers.get(0);
          numbers.remove(0);
          // System.out.println(sum);
        }
        if (!numbers.isEmpty()){
          if (c == '+') {
            sum += numbers.get(0);
          } else if (c == '*') {
            sum *= numbers.get(0);
          }
          numbers.remove(0);
        }
        firstRun = false;
        //System.out.println(sum);
        if(sum == target && op_count == n){
          int count_print_op = 0;
          System.out.print("L ");
          for(int r = 0; r < temp_array.size(); r++){
            System.out.print(temp_array.get(r) + " ");
            if(count_print_op == n){
              break;
            }
            System.out.print(charArray[count_print_op] + " ");
            count_print_op++;
          }
          System.out.print("n");
          return;
        }
        if(op_count == n && sum != target){
          firstRun = true;
          sum = 0;
          op_count = 0;
          for(int e = 0; e < temp_array.size(); e++){
            numbers.add(e, temp_array.get(e));
          }
        }
      }          
    }
    System.out.println("L is impossible");
  }
}

是否有更快的方法来得出类似的结论?

这个问题可以使用动态规划范式在0 (NK²)中解决,其中K是目标目标的最大可能值。这不是很好,也许有一个更快的算法,但它仍然比O(2^N)蛮力解决方案好得多。

首先让我们定义一个递归式来解决这个问题:设G为目标值,f(i,j,k)为一个函数,返回:

  • 1,如果只使用索引i及以后的元素可以达到G-j-k值
  • 否则
  • 0

我们将使用j作为保存当前总和的累加器,使用k作为保存当前乘法链的总乘积的累加器,你很快就会明白的。

递归式的基本情况是:

  • f(N,x,y) = 1如果x+y = G(我们已经使用了每个元素并达到了我们的目标)
  • f(N,x,y) = 0否则
  • f(i,x,y) = 0 i != N and x+y>= G(我们在使用每个元素之前已经超过了目标)

对于其他i值,我们可以将递归定义为:

  • f (i, j, k) = max (f (i + 1 j + k v[我]),f (i + 1 j k * v[我]))

max()中的第一个函数调用意味着我们将在当前索引前面加一个"+"号,所以我们当前的乘法链被打破了,我们必须将其总积加到当前的和上,所以第二个参数是j+k,因为我们现在开始一个新的乘法链,它的总积正好是v[i]。

max()内部的第二个函数调用意味着我们将在当前索引之前放一个"*"号,所以我们当前的乘法链仍然在继续,所以第二个参数仍然是j,第三个参数将变成k * v[i]。

我们想要的是f(0,0,0)的值(我们没有使用任何元素,我们当前的累加和等于0),f(0,0,0)等于1当且仅当问题有解,所以问题解决了。现在让我们回到递归式并修正一个细节:当我们运行f(0,0,0)时,无论v[i]的值是多少,k*v[i]的值都将是0,所以我们必须在计算i = 0的答案时添加一个特殊的检查,最终的递归式看起来像这样:

  • f (i, j, k) = max (f (i + 1 j + k v[我]),f (i + 1 j(我= = 0 ? v[我]:k * v[我])))

最后,我们应用记忆/动态规划范式来优化递归式的计算。在算法执行期间,我们将跟踪每个计算的状态,因此当该状态被另一个递归调用再次调用时,我们只需返回存储的值,而不是再次计算其整个递归树。不要忘记这样做,否则由于子问题的重新计算,您的解决方案将与暴力解决方案一样慢(甚至更糟)。如果需要DP上的一些资源,可以从这里开始:https://en.wikipedia.org/wiki/Dynamic_programming

最新更新