0

皆さん、最近私のアルゴリズムの問​​題について投稿しました。

最小量の廃棄物を与える集合から数を見つける

コードを少し修正したので、ある程度バックトラックするようになりましたが、出力にはまだ欠陥があります。私はこれをかなりデバッグして、すべての変数値をチェックしましたが、問題を見つけられないようです。

ここでも、完全な解決策ではなく、アドバイスが非常に役立ちます。私のコードにはいくつかの問題があるだけだと思いますが、どこにあるのかわかりません。

//前回の投稿より:

基本的に、セットは以下のこのメソッドに渡され、バーの長さも渡されます。ソリューションは、セットからの特定の数値がバーの長さから削除された場合に最小量の無駄を与えるセットからの数値を出力する必要があります。したがって、バーの長さは 10、セットには 6、1、4 が含まれているため、解は 6 と 4 であり、無駄は 0 です。セットをバックトラックする条件に問題があります。また、無駄な「グローバル」変数を使用してバックトラッキングの側面を支援しようとしましたが、役に立ちませんでした。

SetInt は手動で作成されたセットの実装であり、追加、削除、セットが空かどうかのチェック、およびセットから最小値を返すことができます。

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package recursivebacktracking;

/**
 *
 * @author User
 */
public class RecBack {

        int WASTAGE = 10;
        int BESTWASTAGE;
        int BARLENGTH = 10;

    public void work()
    {


         int[] nums = {6,1,2,5};
        //Order Numbers
        SetInt ORDERS = new SetInt(nums.length);
        SetInt BESTSET = new SetInt(nums.length);
        SetInt SOLUTION = new SetInt(nums.length);

        //Set Declarration


        for (int item : nums)ORDERS.add(item);
        //Populate Set

        SetInt result = tryCutting(ORDERS, SOLUTION, BARLENGTH, WASTAGE);
        result.printNumbers();


    }
    public SetInt tryCutting(SetInt possibleOrders, SetInt solution, int lengthleft, int waste)
      {


        for (int i = 0; i < possibleOrders.numberInSet(); i++) // the repeat
          {


            int a = possibleOrders.min(); //select next candidate
              System.out.println(a);

            if (a <= lengthleft) //if accecptable
              { 
                solution.add(a); //record candidate
                lengthleft -= a;
                WASTAGE = lengthleft;
                possibleOrders.remove(a); //remove from original set



                if (!possibleOrders.isEmpty()) //solution not complete
                  {
                      System.out.println("this time");
                    tryCutting(possibleOrders, solution, lengthleft, waste);//try recursive call
                    BESTWASTAGE = WASTAGE;
                    if ( BESTWASTAGE <= WASTAGE )//if not successfull
                      {
                        lengthleft += a;
                        solution.remove(a);

                          System.out.println("never happens");
                      }

                  } //solution not complete
              }

          } //for loop

        return solution;

      }




}
4

1 に答える 1

1

バックトラッキングを使用する代わりに、代わりにビットマスクアルゴリズムを使用することを検討しましたか?アルゴリズムがもっと簡単になると思います。

これを行う方法の概要は次のとおりです。

Nをセット内の要素の数とします。したがって、セットが{6,1,2,5}の場合、Nは4になります。max_wasteを排除できる最大の無駄とします(この例では10)。

int best = 0;  // the best result so far

for (int mask = 1; mask <= (1<<N)-1; ++mask) {

    // loop over each bit in the mask to see if it's set and add to the sum
    int sm = 0;
    for (int j = 0; j < N; ++j) {
        if ( ((1<<j)&mask) != 0) {
            // the bit is set, add this amount to the total
            sm += your_set[j];

            // possible optimization: if sm is greater than max waste, then break
            // out of loop since there's no need to continue
        }
    }

    // if sm <= max_waste, then see if this result produces a better one
    // that our current best, and store accordingly
    if (sm <= max_waste) {
        best = max(max_waste - sm);
    }
}

このアルゴリズムはバックトラッキングと非常によく似ており、同様の複雑さを持っています。再帰を使用しないだけです。

ビットマスクは基本的にバイナリ表現であり、1はセット内のアイテムを使用することを示し、0は使用しないことを示します。1からにループしているため(1<<N)-1、指定されたアイテムのすべての可能なサブセットを検討しています。

このアルゴリズムの実行時間は、Nが大きくなるにつれて非常に急速に増加しますが、N<=約20であれば問題ないはずです。ちなみに、同じ制限がバックトラッキングにも当てはまります。より高速なパフォーマンスが必要な場合は、動的計画法などの別の手法を検討する必要があります。

バックトラッキングの場合は、セット内のどの要素を使用しているかを追跡する必要があり、その要素を使用しようとするか、使用しないかのどちらかです。使用する場合は合計に追加し、使用しない場合は合計を増やさずに次の再帰呼び出しに進みます。次に、合計をデクリメントします(インクリメントした場合)。これがバックトラックの出番です。

これは上記のビットマスクアプローチと非常によく似ており、バックトラッキングアルゴリズムがどのように機能するかをよりよく理解できるようにビットマスクソリューションを提供しました。

編集 OK、再帰を使用する必要があることに気づきませんでした。

ヒント 1まず、単一の再帰関数を使用し、その関数にロジックを配置するだけで、コードを大幅に簡略化できると思います。事前にすべてのセットをビルドしてから処理する必要はありません(それがあなたが行っていることかどうかは完全にはわかりませんが、コードからはそのように見えます)。セットを作成して、セット内のどこにいるかを追跡できます。セットの最後に到達したら、結果がより良いかどうかを確認します。

ヒント 2さらにヒントが必要な場合は、バックトラッキング機能で何をすべきかを考えてみてください。終了条件は何ですか?終了条件に達したとき、何を記録する必要がありますか(たとえば、新しい最良の結果が得られたかなど)?

ヒント3 スポイラーアラート 以下は、いくつかのアイデアを提供するためのC ++実装です。したがって、自分でさらに作業したい場合は、ここを読むのをやめてください。

int bestDiff = 999999999;
int N;
vector< int > cur_items;
int cur_tot = 0;
int items[] = {6,1,2,5};
vector< int > best_items;
int max_waste;

void go(int at) {
  if (cur_tot > max_waste)
    // we've exceeded max_waste, so no need to continue
    return;
  if (at == N) {
    // we're at the end of the input, see if we got a better result and
    // if so, record it
    if (max_waste - cur_tot < bestDiff) {
      bestDiff = max_waste - cur_tot;
      best_items = cur_items;
    }
    return;
  }

  // use this item  
  cur_items.push_back(items[at]);
  cur_tot += items[at];
  go(at+1);

  // here's the backtracking part
  cur_tot -= items[at];
  cur_items.pop_back();

  // don't use this item
  go(at+1);
}

int main() {
  // 4 items in the set, so N is 4
  N=4;

  // maximum waste we can eliminiate is 10
  max_waste = 10;

  // call the backtracking algo
  go(0);

  // output the results
  cout<<"bestDiff = "<<bestDiff<<endl;
  cout<<"The items are:"<<endl;
  for (int i = 0; i < best_items.size(); ++i) {
    cout<<best_items[i]<<" ";
  }
  return 0;
}
于 2011-04-21T01:18:09.140 に答える