创建所有可能的项目百分比列表?

时间:2012-05-24 18:46:36

标签: java algorithm

我的目标是为程序提供一些项目(字符串),范围和目标百分比,并让它给出每个项目的所有可能百分比。例如,想象一下,你去杂货店买了一篮苹果&梨你想知道使用所有项目你可以拥有的所有百分比(不是一个完整的解决方案,我手动这样做): {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的列表items,我需要在一个范围内使用所有X项。)

我可以通过暴力来做到这一点,但我不觉得它有效,因为它只是尝试一切所以我正在使用的界限根本没有被用来使它高效(例如,如果苹果是75 %然后梨没有理由超过25%..边界是列表,范围和target_percent的大小..我可能有20-30个列表项,范围为5-20或50项,范围从1-5 ..或介于两者之间的任何事情我想尽可能快地完成我可以得到多少完整的结果。我没有在问题中显示target_percent部分,因为我可以设置它,一旦我理解了如何解决问题,但基本上所有的例子都假设100%最大,但有时候你的篮子里可能已经有20%的橘子,看看你如何使用苹果/梨来填补剩余的80%)。

我的问题是,我如何处理这个问题(任何想法逻辑使用,示例或代理问题我可以查找)?动态编程是否适合这个问题,或者我不能将这个问题分解成一个小问题(请记住,因为它总是包含列表中的所有项目,它没有构建)?如果有人可以指出我正确的方向,我愿意研究任何可能有用的主题(花了两天时间试图解决这个问题,我只是不确定动态编程路线是否正确)。还有一个这类问题的名称(我查找了背包问题,整数分区,组合,但它们似乎都不合适)?

这是我的(破碎的)蛮力方法(它实际上并没有按预期工作,但可能让你了解蛮力方法):

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]);

    }
}

2 个答案:

答案 0 :(得分: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;
}

也就是说,看了一下结果后,看起来应该有一个算法来确定给出weightsNominmax的权重数量,并从那里开始应该相当简单,以填补可能的价值观。那就是说,我现在还不能理解。 (或者确实,它是否比蛮力方法更快......)

答案 1 :(得分: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)
}

注意它如何仅通过跟踪每个组合可能有多少项来生成有效组合。所以,这不是蛮力,它实际上会做生成组合所需的最小工作。