【问题标题】:Optimising algorithm for an operation inserting program一个操作插入程序的优化算法
【发布时间】:2016-09-29 02:06:17
【问题描述】:

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

有没有更快的方法得出类似的结论?

【问题讨论】:

  • 这里的代码太多了。尝试隔离您要陈述问题的部分。
  • 我觉得如果没有人实际运行和研究您的代码,可能无法真正弄清楚它在做什么(或试图做什么)。您能否更新问题以包含有关您在此处尝试实现的目标的描述?

标签: java algorithm optimization


【解决方案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 if 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[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])) )

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-10-14
    • 2011-06-14
    • 2019-06-25
    • 2019-01-13
    • 2021-09-14
    • 1970-01-01
    • 2010-10-26
    • 1970-01-01
    相关资源
    最近更新 更多