用于确定组合的值的组合的函数小于阈值

时间:2011-12-05 18:18:22

标签: c# optimization recursion

我正在尝试开发一个优化函数,该函数将确定加在一起时双精度列表中的哪些元素将小于指定的阈值。这些元素可以多次使用。

例如,如果我的元素列表是

{1,3,7,10}  

我的门槛是20,我希望我的结果是

1
3
7
10
10, 10
10, 7
10, 7, 3
10,7,1
10,7,1,1
10,7,1,1,1
7,7
7,7,3
7,7,1
7,7,1,1
7,7,1,1,1
...

我希望这个问题的答案可能是一个递归调用,可能可以在教科书中找到,但我不知道如何正确地说出问题以找到答案。欢迎来自这一组专家的帮助。

3 个答案:

答案 0 :(得分:1)

这个程序有效,似乎是最简单的解决方案。所有结果都按升序排序。

  private static final HashSet<ArrayList<Double>> lists = 
       new HashSet<ArrayList<Double>>(); // all of the combinations generated

  private static final double[] elements = {10, 7, 3, 1};

  public static void main(String[] args) {
    combine(20, new ArrayList<Double>());
    for (ArrayList<Double> set : lists) {
      System.out.println(set);
    }
  }

  private static void combine(final double limit, ArrayList<Double> stack) {
    // iterates through the elements that fit in the threshold
    for (double item : elements) {
      if (item < limit) {
        final ArrayList<Double> nextStack = new ArrayList<Double>(stack);
        nextStack.add(item);
        // a sort is necessary to let the HashSet de-dup properly
        Collections.sort(nextStack);
        lists.add(nextStack);
        combine(limit - item, nextStack);
      }
    }
  }

但是,这种类型的组合问题会产生许多结果。如果您更关心性能而不是代码可读性和简单性,我可以进一步优化。

C#:

static void Main(string[] args)
    {
        Run();
    }

    static public void Run()
    {
        Combine(20, new List<Double>());
        foreach (List<Double> set in lists)
        {

            Debug.Print(set.ToString());
        }
    }
    private static HashSet<List<Double>> lists =
        new HashSet<List<Double>>(); // all of the combinations generated

    private static double[] elements = { 10, 7, 3, 1 };

    private static void Combine(double limit, List<Double> stack)
    {
        // iterates through the elements that fit in the threshold
        foreach (double item in elements)
        {
            if (item < limit)
            {
                List<Double> nextStack = new List<Double>(stack);
                nextStack.Add(item);
                // a sort is necessary to let the HashSet de-dup properly
                nextStack.Sort();
                lists.Add(nextStack);
                Combine(limit - item, nextStack);
            }
        }
    }

答案 1 :(得分:0)

我不确定是否需要Sort()来检测正确重复的条目,但此代码应该有效:

    private List<int[]> CombinedElementsInArrayLessThanValue(int[] foo, int value)
    {
        List<int[]> list = new List<int[]>();

        for (int i = 0; i < foo.Length; i++)
        {
            List<int> start = new List<int>();
            start.Add(foo[i]);
            start.Sort();
            int[] clone = start.ToArray();
            if (start.Sum() < value && !list.Contains(clone))
            {
                list.Add(clone);
                CombinedElementsInArrayLessThanValue(foo, value, start, list);
            }
        }
        return list;
    }

    private void CombinedElementsInArrayLessThanValue(int[] foo, int value, List<int> partial, List<int[]> accumulate_result)
    {
        for (int i = 0; i < foo.Length; i++)
        {
            List<int> clone = new List<int>(partial);
            clone.Add(foo[i]);
            clone.Sort();
            int[] array = clone.ToArray();
            if (clone.Sum() < value && !accumulate_result.Contains(array))
            {
                accumulate_result.Add(array);
                CombinedElementsInArrayLessThanValue(foo, value, clone, accumulate_result);
            }
        }
    }

答案 2 :(得分:0)

一次处理列表中的一个项目,让递归完全处理一个项目,以缩短递归的“深度”。

public static List<int[]> Combine(int[] elements, int maxValue)
{
    LinkedList<int[]> result = new LinkedList<int[]>();
    List<int> listElements = new List<int>(elements);
    listElements.Sort();
    Combine(listElements.ToArray(), maxValue, new int[0], result);
    return result.ToList();
}

private static void Combine(int[] elements, int maxValue, int[] stack, LinkedList<int[]> result)
{
    if(elements.Length > 0 && maxValue >= elements[0])
    {               
        var newElements = elements.Skip(1).ToArray();
        for (int i = maxValue / elements[0]; i > 0; i--)
        {
            result.AddLast(stack.Concat(Enumerable.Repeat(elements[0], i)).ToArray());  
            Combine(newElements, maxValue - i*elements[0], result.Last(), result);
        }
        Combine(newElements, maxValue, stack, result);
    }
}