zoukankan      html  css  js  c++  java
  • [算法]一个关于组合的算法

    【问题描述】(专业领域问题,保密起见此处省略……)

    【模型】

    data[][]={
    {0, 100, 200, 300},
    {0, 50, 100},
    {0, 150, 300},
    {0, 100, 200, 300, 400,}
    }
    //2维数组data,M行,每一行代表一组数据,每组数据内有序

    //要求:从每一组数据中,取出且仅仅取出一个数,然后使这些数据的和满足[MIN, MAX]范围。

    这是最近同学遇到的一个问题,开始先用非递归方法实现的,后来为了比较递归和非递归的区别,又用递归方法实现了。

    先说说非递归方法,我的思路是:肯定要遍历各组数据之间的所有组合,即任意Xi属于组Si(i=0,1,2,....,M-1),{X1,X2,...,X(M-1)}的所有组合。当然,因为各组数据之内有序,可以在遍历过程中增加判断,如果当前的和已经大于M,则可以结束当前循环,以及后面的判断。采用数组INDEX[0---M-1]来记录每一行当前循环的index,如果后面的i行的index已经达到该行的最大index,则i-1行加一。及类似数字进位原理。(后来听同学说,这样也可以解决多层循环的问题)。 

      1package swing;
      2
      3import java.util.ArrayList;
      4
      5
      6public class HHH {
      7  private final int[] INDEX; //记录当前各层下一步的Index
      8  private final int[] LENGTH; //记录各层的长度
      9  private double[][] data; //原始数组
     10  private double[][] result; //记录组合
     11  private final double MIN; //要组合的数的和的下限
     12  private final double MAX; //要组合的数的和的上限
     13  private final int DD; //二维数组的行数
     14  public HHH(double[][] data, double min, double max) {
     15    this.data = data;
     16    this.MIN = min;
     17    this.MAX = max;
     18
     19    if (this.data == null{
     20      DD = 0;
     21    }

     22    else {
     23      DD = data.length;
     24    }

     25    INDEX = new int[DD];
     26    LENGTH = new int[DD];
     27    for (int i = 0; i < DD; i++{
     28      if (data[i] != null{
     29        LENGTH[i] = data[i].length;
     30      }

     31      else {
     32        LENGTH[i] = 0;
     33      }

     34    }

     35    //filter first
     36    for (int i = 0; i < DD; i++{
     37      for (int j = 0; j < LENGTH[i]; j++{
     38        if (data[i][j] > MAX) {
     39          LENGTH[i] = j;
     40          }
     41        break;

     42      }

     43    }

     44
     45    //greatly filter
     46    ArrayList a = new ArrayList();
     47    double[] temp = new double[DD];
     48    double sum = 0;
     49    boolean over = false;
     50    while (true{
     51      sum = 0;
     52      for (int i = 0; i < DD; i++{
     53        temp[i] = data[i][INDEX[i]];
     54        sum += temp[i];
     55        if (sum > MAX) //此处判断,可大量提高速度
     56          for (int j = i + 1; j < DD; j++{
     57            INDEX[j] = LENGTH[j] - 1;
     58            break;
     59          }

     60        }

     61      }

     62      if (sum >= MIN && sum <= MAX) {
     63        a.add(temp);
     64       temp = new double[DD];
     65      }

     66      INDEX[DD - 1]++;
     67      int div = INDEX[DD - 1/ LENGTH[DD - 1];
     68      INDEX[DD - 1%= LENGTH[DD - 1];
     69      for (int i = DD - 2; i >= 0; i--{
     70        int ttt = INDEX[i] + div;
     71        if (ttt == LENGTH[0]) {
     72          over = true;
     73          break;
     74        }

     75        INDEX[i] = ttt % LENGTH[i];
     76        div = (ttt) / LENGTH[i];
     77      }

     78      if (over) {
     79        break;
     80      }

     81    }

     82    //format
     83    int size = a.size();
     84    result = new double[size][];
     85    for (int i = 0; i < size; i++{
     86      result[i] = (double[]) a.get(i);
     87    }

     88  }

     89
     90  public double[][] getResult() {
     91    return result;
     92  }

     93
     94  public void printResult() {
     95    if (result != null{
     96      for (int i = 0; i < result.length; i++{
     97        for (int j = 0; j < result[i].length; j++{
     98          System.out.print(result[i][j] + " ");
     99        }

    100        System.out.println();
    101      }

    102    }

    103  }

    104
    105  public static void main(String[] args) {
    106    double[][] capacity = {
    107        {
    108        020030040050060070080010005500551055155520,
    109        55606000}

    110        , {
    111        03006009001200150018002000221122222233}

    112        , {
    113        03006009001200}

    114        , {
    115        0300600}

    116        , {
    117        0135150230460}

    118        , {
    119        03006004000}

    120        , {
    121        0600120018002400}

    122        , {
    123        0300600}

    124        , {
    125        03006001000}

    126    }
    ;
    127    long begin = System.currentTimeMillis();
    128    HHH h = new HHH(capacity, 70008000);
    129    begin = System.currentTimeMillis() - begin;
    130    System.out.println("time used: " + (begin / 1000.0+ "s");
    131    System.out.println("" + h.getResult().length);
    132    //输出太多,省略
    133    //h.printResult();
    134  }

    135
    136}

    后来又用递归实现了,发现用递归实现反而思路更加简单,而且也好理解。就是从第一行到最后一行,每行取一个数,直到最后一行取了或者前面之和已经大于MAX为止。但是回退时要注意减掉上一次加的那个数。

      1package swing;
      2
      3import java.util.ArrayList;
      4
      5
      6
      7public class HHH1 {
      8  private double[][] data;
      9  private final int DD;
     10  private final double MAX;
     11  private final double MIN;
     12  private double[][] result;
     13  private ArrayList res = new ArrayList();
     14  private double[] tmp;
     15  private double sum = 0;
     16  public HHH1(double[][] data, double min, double max) {
     17    this.data = data;
     18    if (data != null{
     19      DD = data.length;
     20    }

     21    else {
     22      DD = 0;
     23    }

     24    MAX = max;
     25    MIN = min;
     26    tmp = new double[DD];
     27  }

     28
     29  public void process(int n, int index) {
     30    if (n == index) {
     31      if (sum >= MIN && sum <= MAX) {
     32        res.add(tmp.clone());
     33      }

     34    }

     35    else {
     36      int i;
     37      for (i = 0; i < data[index].length; i++{
     38        tmp[index] = data[index][i];
     39        sum += tmp[index];
     40        if (i > 0{
     41          sum -= data[index][i - 1];
     42        }

     43        if (sum > MAX) {
     44          sum -= tmp[index];
     45          break;
     46        }

     47        process(n, index + 1);
     48      }

     49      if (i == data[index].length) {
     50        sum -= data[index][i - 1];
     51      }

     52    }

     53  }

     54
     55  private void format() {
     56    int size = res.size();
     57    result = new double[size][];
     58    for (int i = 0; i < size; i++{
     59      result[i] = (double[]) res.get(i);
     60    }

     61  }

     62
     63  public double[][] getResult() {
     64    if (result == null{
     65      format();
     66    }

     67    return result;
     68  }

     69
     70  public static void main(String[] args) {
     71    double[][] capacity = {
     72        {
     73        020030040050060070080010005500551055155520,
     74        55606000}

     75        , {
     76        03006009001200150018002000221122222233}

     77        , {
     78        03006009001200}

     79        , {
     80        0300600}

     81        , {
     82        0135150230460}

     83        , {
     84        03006004000}

     85        , {
     86        0600120018002400}

     87        , {
     88        0300600}

     89        , {
     90        03006001000}

     91    }
    ;
     92    long begin = System.currentTimeMillis();
     93    HHH1 h = new HHH1(capacity, 70008000);
     94    h.process(capacity.length, 0);
     95    double[][] res = h.getResult();
     96    begin = System.currentTimeMillis() - begin;
     97    System.out.println("time used: " + (begin / 1000.0+ "s");
     98    System.out.println(res.length);
     99//    for(int i=0;i<h.res.size();i++){
    100//      double[] d=(double[])h.res.get(i);
    101//      for(int j=0;j<d.length;j++){
    102//        System.out.print(""+d[j]+" ");
    103//      }
    104//      System.out.println();
    105//    }
    106  }

    107}

    经过用两种方法对相同的数据进行测试,发现递归的程序明显比非递归的程序快。原因初步分析如下:虽说递归涉及到多次的函数调用,即进栈出栈的操作,但是因为函数调用使用栈,而非在堆中,因此速度比在非递归时使用的在堆中分配的数据要快。当然,这也许和我写的非递归算法有关。

    关于递归与非递归的使用以及转换和效率问题,最近抽空研究下。

    ====================================================================================================

    added on 2009-11-17

    经测试,非递归算法之所以这么慢,是因为我在程序中处理“进位”时,使用了除法和取余操作,而这两种操作相对加法和比较来说,是比较费时的。经改进算法,程序已经达到和递归差不多的速度。看来以后写程序得注意了,如果能有替代方案,尽量少用乘除操作。虽然计算少的时候效果看不出来,

    最近对递归和非递归看了下,感觉递归思想确实易理解,而且程序简练,但是也存在许多问题,比如多次函数调用导致耗资源,不便于控制处理过程等。所以以后还是能不用递归就不用了吧。

  • 相关阅读:
    MFC中文件的查找、创建、打开、读写等
    使用DOS比较两个txt文件的差异
    HDU
    LIS(两种方法求最长上升子序列)
    7-17 奥运排行榜 (25 分)
    区间DP
    HDU-1864&&HDU-2602(01背包问题)
    HDU-5968异或密码
    Maximum Value(unique函数,lower_bound()函数,upper_bound()函数的使用)
    博弈结论记录
  • 原文地址:https://www.cnblogs.com/gaojing/p/1593559.html
Copyright © 2011-2022 走看看