计算所有可能的排列/组合,然后检查结果是否等于某个值

时间:2014-04-20 23:16:39

标签: c# combinations permutation

最好的解释方法就是使用一个例子:

您正在以2000美元的价格参观一家商店,您的目标是在旅行结束时获得0美元。 你不知道有多少物品可用,也不知道它们的价格是多少。

假设目前有3件商品价格为1000美元,750美元,500美元。 (重点是计算所有可能的解决方案,而不是最有效的解决方案。)

你可以花2000美元,这意味着:

You can buy the $1000 item 0, 1 or 2 times.
You can buy the $750 item 0, 1 or 2 times.
You can buy the $500 item 0, 1, 2, 3 or 4 times.

最后我需要能够拥有所有解决方案,在这种情况下它将是

2*$1000
1*$1000 and 2*$500
2*$750 and 1*$500
4*$500

旁注:你不能有一个重复的解决方案(像这样)

1*$1000 and 2*$500
2*$500 and 1*$1000

这就是我的尝试:

首先使用

调用此函数
 goalmoney = convert.ToInt32(goalMoneyTextBox.Text);
 totalmoney = Convert.ToInt32(totalMoneyTextBox.Text);
 int[] list = new int[usingListBox.Items.Count];
 Calculate(0, currentmoney, list);

功能:

    public void Calculate(int level, int money, int[] list)
    {
        string item = usingListBox.Items[level].ToString();
        int cost = ItemDict[item];
        for (int i = 0; i <= (totalmoney / cost); i++)
        {
            int[] templist = list;
            int tempmoney = money - (cost * i);
            templist[level] = i;
            if (tempmoney == goalmoney)
            {
                resultsFound++;
            }
            if (level < usingListBox.Items.Count - 1 && tempmoney != goalmoney) Calculate(level + 1, tempmoney, templist);
        }
    }

2 个答案:

答案 0 :(得分:2)

您的问题可以简化为一个众所周知的标记为Frobenius equation的数学问题,该问题与众所周知的Coin problem密切相关。假设您有N项,其中第i项费用为c[i],您需要花费S $。所以你需要找到方程

的所有非负整数解(或决定是否没有解)
c[1]*n[1] + c[2]*n[2] + ... + c[N]*n[N] = S

其中所有n[i]都是未知变量,每个n[i]是第i种类型的已购买项目数。

该等式可以以各种方式解决。以下函数allSolutions(我想它可以另外简化)找到给定方程的所有解:

public static List<int[]> allSolutions(int[] system, int total) {
    ArrayList<int[]> all = new ArrayList<>();
    int[] solution = new int[system.length];//initialized by zeros
    int pointer = system.length - 1, temp;
    out:
    while (true) {
        do { //the following loop can be optimized by calculation of remainder
            ++solution[pointer];
        } while ((temp = total(system, solution)) < total);

        if (temp == total && pointer != 0)
            all.add(solution.clone());
        do {
            if (pointer == 0) {
                if (temp == total) //not lose the last solution!
                    all.add(solution.clone());
                break out;
            }
            for (int i = pointer; i < system.length; ++i)
                solution[i] = 0;
            ++solution[--pointer];
        } while ((temp = total(system, solution)) > total);
        pointer = system.length - 1;
        if (temp == total)
            all.add(solution.clone());
    }
    return all;
}

public static int total(int[] system, int[] solution) {
    int total = 0;
    for (int i = 0; i < system.length; ++i)
        total += system[i] * solution[i];
    return total;
}

在上面的代码system中,系数c[i]total的数组是S。有一个明显的限制:system应该没有任何零元素(这导致无限数量的解决方案)。稍微修改上述代码可避免此限制。

答案 1 :(得分:0)

假设您有一个类Product,它公开了一个名为Price的属性,这是一种方法:

public List<List<Product>> GetAffordableCombinations(double availableMoney, List<Product> availableProducts)
{
    List<Product> sortedProducts = availableProducts.OrderByDescending(p => p.Price).ToList();

    //we have to cycle through the list multiple times while keeping track of the current
    //position in each subsequent cycle. we're using a list of integers to save these positions
    List<int> layerPointer = new List<int>();
    layerPointer.Add(0);

    int currentLayer = 0;

    List<List<Product>> affordableCombinations = new List<List<Product>>();
    List<Product> tempList = new List<Product>();

    //when we went through all product on the top layer, we're done
    while (layerPointer[0] < sortedProducts.Count)
    {
        //take the product in the current position on the current layer
        var currentProduct = sortedProducts[layerPointer[currentLayer]];
        var currentSum = tempList.Sum(p => p.Price);

        if ((currentSum + currentProduct.Price) <= availableMoney)
        {
            //if the sum doesn't exeed our maximum we add that prod to a temp list
            tempList.Add(currentProduct);
            //then we advance to the next layer
            currentLayer++;
            //if it doesn't exist, we create it and set the 'start product' on that layer
            //to the current product of the current layer
            if (currentLayer >= layerPointer.Count)
                layerPointer.Add(layerPointer[currentLayer - 1]);
        }
        else
        {
            //if the sum would exeed our maximum we move to the next prod on the current layer
            layerPointer[currentLayer]++;

            if (layerPointer[currentLayer] >= sortedProducts.Count)
            {
                //if we've reached the end of the list on the current layer,
                //there are no more cheaper products to add, and this cycle is complete
                //so we add the list we have so far to the possible combinations
                affordableCombinations.Add(tempList);
                tempList = new List<Product>();

                //move to the next product on the top layer
                layerPointer[0]++;
                currentLayer = 0;
                //set the current products on each subsequent layer to the current of the top layer
                for (int i = 1; i < layerPointer.Count; i++)
                {
                    layerPointer[i] = layerPointer[0];
                }
            }
        }
    }

    return affordableCombinations;
}