操作插入程序的优化算法

时间:2016-09-29 02:06:18

标签: java algorithm optimization

我目前的程序是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");
  }
}

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

1 个答案:

答案 0 :(得分:1)

这个问题可以使用动态编程范例在O(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和x + y&gt; = G(我们在使用每个元素之前已超出目标)

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

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

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 [i]),f(i + 1,j,(i == 0?v [i]:k * v [i])))

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