2

私の目標は、プログラムにいくつかの項目 (文字列)、範囲、および目標パーセントを与え、各項目の可能なすべてのパーセントを与えることです。たとえば、食料品店に行ってリンゴとナシのバスケットを持っていて、すべてのアイテムを使用して得られるすべてのパーセンテージを知りたいと想像してください (完全な解決策ではありません。私はこれを手作業で行っています)。 {Apple:50, Pears:50}, {Apple:75, Pears:25}, {Apple:90, Pears:10},etc.

20〜50の範囲で同じことを行うと(単一のアイテムが持つことができる最大値は50%で、最小値は20%であることを意味します)、唯一の結果は次のとおりです:( {Apple:50, Pears:50}アイテムは2つしかなく、50を超えることはできないため) % 重さ)

アイテムに関連付けられた値/重みがないため、いくつかの大きな違いがあるナップザックの問題と同様の特性があると思いました(ただし、アイテムをナップザックに収めようとするナップザックの問題のように、target_percent、100 内に値を収めようとしています) %)。また、問題を分解する方法がわからないため、一般的な動的プログラミングのアイデアを適用するのにも問題があります(典型的なナップザックの問題は結果を構築し、結果を「キャッシュ」して再利用しますが、Xのリストがある場合アイテム、範囲内で使用するすべての X アイテムが必要です)。

私は力ずくでこれを行うことができますが、すべてを試すだけなので、効率的であるとは感じませんPear が 25% を超える必要がある理由はありません..境界はリスト、範囲、および target_percent のサイズです..5 ~ 20 の範囲の 20 ~ 30 個のリスト項目、または 1 ~ 5 の範囲の 50 個の項目がある可能性があります。問題の解決方法を理解したら設定できるので、target_percent の部分は質問に示していませんが、基本的にはすべて例では最大 100% を想定していますが、バスケットにすでに 20% のオレンジがあり、リンゴ/ナシを使用して残りの 80% を埋める方法を確認することもできます)。

私の質問は、これにどのようにアプローチできますか(使用するアイデアロジック、例、またはルックアップできるプロキシ問題)? 動的プログラミングはこの問題に適していますか、それともこれをより小さなチャックに分割できないという事実が問題なのですか (常にリスト内のすべての項目が含まれているため、構築されていないことを思い出してください)? 誰かが私を正しい方向に向けることができれば、役立つかもしれないトピックを喜んで研究します (これを理解するために 2 日間費やした後、動的プログラミングのルートが正しいかどうかはわかりません)。また、このタイプの問題の名前はありますか?

これが私の(壊れた)ブルートフォースアプローチです(実際には期待どおりに機能していませんが、ブルートフォースメソッドのアイデアが得られるかもしれません):

import java.util.ArrayList;
import java.util.Arrays;


public class brute_force_percent_returner {
    static String[] data = new String[]{"Apple", "Pears"};
    static int[] coeff = new int[data.length];
    static ArrayList<int[]> queue = new ArrayList<int[]>();

    public static void main(String[] args) {
        System.out.println("Starting");
        recursion(0,data);
        for (int[] item : queue) {
            for (int item2 = 0; item2<data.length; item2++) {
                System.out.print(data[item2] + " = " + item[item2] + " ");
            }
            System.out.println();
        }
    }

    private static void recursion(int k, String[] data2) {
        // this is not exactly working
        for (String item: data2) {
            for (int x = 0; x<5;x++) {
                int[] coeff_temp = Arrays.copyOf(coeff, coeff.length);
                coeff_temp[k] = x;
                queue.add(coeff_temp);
            }
        }
        if (k == data.length-1) {
            return;
        } else {
            recursion(k+1, data2);
        }
    }
}

それが役立つ場合、私が作成しようとしていたソリューションは、これに多少基づいていました(ナップザックの問題ですが、多数の変数に対して非常に高速であるように見えますが、このケアでは、その処理はリスト内のアイテムですが、私の場合はリストは単なる文字列です):

public class TurboAdder {
    private static final int[] data = new int[] { 5, 10, 20, 25, 40, 50 };

    private static class Node {
        public final int index;
        public final int count;
        public final Node prevInList;
        public final int prevSum;
        public Node(int index, int count, Node prevInList, int prevSum) {
            this.index = index;
            this.count = count;
            this.prevInList = prevInList;
            this.prevSum = prevSum;
        }
    }

    private static int target = 100;
    private static Node sums[] = new Node[target+1];

    // Only for use by printString.
    private static boolean forbiddenValues[] = new boolean[data.length];

    public static void printString(String prev, Node n) {
        if (n == null) {
            System.out.println(prev);
        } else {
            while (n != null) {
                int idx = n.index;
                // We prevent recursion on a value already seen.
                if (!forbiddenValues[idx]) {
                    forbiddenValues[idx] = true;
                    printString((prev == null ? "" : (prev+" + "))+data[idx]+"*"+n.count, sums[n.prevSum]);
                    forbiddenValues[idx] = false;
                }
                n = n.prevInList;
            }
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < data.length; i++) {
            int value = data[i];
            for (int count = 1, sum = value; count <= 100 && sum <= target; count++, sum += value) {
                for (int newsum = sum+1; newsum <= target; newsum++) {
                    if (sums[newsum - sum] != null) {
                        sums[newsum] = new Node(i, count, sums[newsum], newsum - sum);
                    }
                }
            }
            for (int count = 1, sum = value; count <= 100 && sum <= target; count++, sum += value) {
                sums[sum] = new Node(i, count, sums[sum], 0);
            }
        }
        printString(null, sums[target]);

    }
}
4

2 に答える 2

1

これは宿題のように聞こえるので、私はあなたをあまり助けたくありませんが、ここにアプローチがあります.

範囲を定義するには、いくつかのハッシュ マップを作成します。

lower bounds = {apples  => 20, pears => 40,  oranges => 0}
upper bounds = {apples  => 50, pears => 100, oranges => 30}

考えてみると、すべての最終的な (有効な) 組み合わせには、少なくとも、下限マップによって定義された内容が含まれます。それをベースコンビネーションと呼びます。

次に、ベースの組み合わせに追加できる可能性のある各タイプの理論上の最大値を計算します。これは単なる別のマップです

{apples  => 30, pears => 60,  oranges => 30}

ベース マップに追加できるアイテムの合計数を計算します。これは 100 です。この例では、すべての下限値の合計で、40 です。

ここで、組み合わせを生成する必要があります。おそらく、再帰が最も簡単な方法であることがわかるでしょう。残りのアルゴリズムは、明確にするために疑似コードとハードコーディングされたものを使用して示していますが、一般的な再帰バージョンを作成する必要があります。

totalItemsToAdd = 40 //as calculated via baseCombo.sumOfEntries()


for (i=0; i<maxApples; i++) {
    combo = clone the base combination
    combo.apples += i;
    remainingItemsToAdd = totalItemsToAdd - i;
    if (remainingItemsToAdd > 0) {
        for (j=0; j<maxPears; j++) {
            combo.pears += j;
            // and so on, recursively
        }
    }

    results.append(combo)
}

組み合わせごとに可能なアイテムの数を追跡することにより、有効な組み合わせのみを生成する方法に注意してください。したがって、これは力ずくではなく、組み合わせのセットを生成するために必要な最小限の作業を実際に実行します。

于 2012-05-24T22:06:38.290 に答える
1

ブルートフォースアプローチが最善の方法であると確信しています-少なくとも、それが私が行う方法です(決して同じではありません...)。

これは、私が作業している再帰的アプローチを使用する試みです (ただし、 の高い値でテストしていませんweightsNo。これは、重みの順列ではなく、重みの組み合わせに関心があることに基づいて機能します)。 - ただし、切り替えは比較的簡単です。

public static Set<int[]> getPossiblePercentageWeights(int weightsNo, int min, int max){
  return recusiveFixWeight(weightsNo, 100, min, max);
}

private static Set<int[]> recusiveFixWeight(int weightsNo, int sum, int min, int max){
  Set<int[]> weightsSet = new LinkedHashSet<int[]>();
  if (weightsNo>2){
    for (int iWeight=min; iWeight<=max; iWeight++){
      Set<int[]> subSet = recusiveFixWeight(weightsNo-1, sum-iWeight, min, iWeight);
      for (int[] subWeights : subSet){
        int[] weights = new int[weightsNo];
        weights[0] = iWeight;
        System.arraycopy(subWeights, 0, weights, 1, subWeights.length);
        weightsSet.add(weights);
      }
    }
  } else {
    int iMax = Math.min(max, sum/weightsNo);
    for (int iWeight=min; iWeight<=iMax; iWeight++){
      int jWeight = sum-iWeight;
      if (jWeight>=min && jWeight<=max){
        weightsSet.add(new int[]{iWeight,jWeight});
      }
    }
  }
  return weightsSet;
}

weightsNoとはいえ、結果を見ると、与えられた、 、minおよびの weightSet の数を決定するアルゴリズムが必要であるように見えます。そこmaxから、可能な値でそれらを埋めるのはかなり簡単なはずです。とは言え、現時点ではよくわかりません。(または、実際、ブルートフォースアプローチよりも速いかどうか...)

于 2012-05-24T21:07:48.743 に答える