首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用蛮力解决问题

用蛮力解决问题
EN

Code Review用户
提问于 2014-12-30 15:53:19
回答 1查看 1.8K关注 0票数 1

我有一些代码可以强行解决以下问题:

给定一套x硬币和一笔目标金额,达到该目标所需的硬币数量最少是多少?

到目前为止,守则:

代码语言:javascript
复制
import java.util.ArrayList;
import java.util.Arrays;

public class coinsSum {
    public static int min = Integer.MAX_VALUE;
    public static int[] combination;
    public static final int TARGET = 59;

    public static void main(String[] args) {
        long start = System.nanoTime();

        int[] validCoins = new int[] {1, 2, 5, 10, 20};
        Arrays.sort(validCoins);
        int len = validCoins.length;

        ArrayList<Integer> maxList = new ArrayList<Integer>();
        for(int c : validCoins) {
            maxList.add(TARGET / c);
        }

        int[] max = new int[len];
        for(int i = 0; i < len; i++) {
            max[i] = maxList.get(i).intValue();
        }

        permutations(new int[len], max, validCoins, 0); // bread&butter

        if(min != Integer.MAX_VALUE) {
            System.out.println();
            System.out.println("The combination " + Arrays.toString(combination) + " uses " + min + " coins to make the target of: " + TARGET);
        } else {
            System.out.println("The target was not reachable using these coins");
        }

        System.out.println("TOOK: " + (System.nanoTime() - start) / 1000000 + "ms");
    }

    public static void permutations(int[] workspace, int[] choices, int[] coins, int pos) {
        if(pos == workspace.length) {
            int sum = 0, coinCount = 0;
            System.out.println("TRYING " + Arrays.toString(workspace));
            for(int a = 0; a < coins.length; a++) {
                sum += workspace[a] * coins[a];
                coinCount += workspace[a];
            }
            if(sum == TARGET) {
                // System.out.println(Arrays.toString(n)); //valid combinations
                if(coinCount < min) {
                    min = coinCount;
                    combination = workspace;
                    System.out.println(Arrays.toString(combination)+" uses " + min + " coins");
                }
            }
            return;
        }
        for(int i = 0; i <= choices[pos]; i++) {
            workspace[pos] = i;
            permutations(workspace, choices, coins, pos + 1);
        }
    }
}

此解决方案使用递归。在Java中有使用循环计算组合的方法吗?

该算法还可以做哪些其他改进?

EN

回答 1

Code Review用户

回答已采纳

发布于 2014-12-30 17:32:40

你的代码很复杂。让它变得如此复杂的是,您有递归调用与全局对象交互。这使得我们几乎不可能理解某一特定时刻的状态。

当您以递归方式编写算法时,您的目标应该是使(最小数量的)信息只通过方法参数(而不是任何全局状态)向一个方向传递,并使用返回值返回答案。

话虽如此,我们还是来看看如何使这件事奏效吧。

这个想法在这里非常简单:

如果我要用1,2,5,10,20达到59,我可以试着用0乘以20,看看如何用1,2,5,10达到59。我也可以尝试1乘20,看看如何用1,2,5,10达到剩下的39。我也可以试着用2乘20,看看我怎么能达到19,剩下的1,2,5,10。

我把解决这个问题简化为解决较小的问题(因为我们的硬币数量较少)。问题是“我什么时候才能停下来?”如果你已经达到目标,你可以停下来。如果你没有硬币可以尝试,你可以停止;

一旦用代码表示了这一点,就可以编写:

代码语言:javascript
复制
import java.util.ArrayList;
import java.util.Arrays;

public class coinsSum {
    public static final int TARGET = 59;

    public static void main(String[] args) {
        long start = System.nanoTime();

        int[] validCoins = new int[] {1, 2, 5, 10, 20};
        Arrays.sort(validCoins);

        int min = permutations(TARGET, validCoins);
        System.out.println("TOOK: " + (System.nanoTime() - start) / 1000000 + "ms to find " + min);
    }

    public static int permutations(int target, int[] coins) {
        return permutations(target, coins, coins.length);
    }

    public static int permutations(int target, int[] coins, int array_size) {
        System.out.println("TRYING " + target + " with " + array_size + " different coins");
        assert target >= 0;
        assert array_size >= 0;
        assert array_size <= coins.length;
        if (target==0) // target is reached (in 0 coin)
            return 0;
        if (array_size == 0) // no more coins to reach a positive target
            return -1;
        array_size--;
        int coin = coins[array_size]; // let's consider the biggest coin
        int nb = target/coin; // and check how many times it fits in the target
        int sol = -1; // we haven't found a solution so far (Integer.MAX_VALUE would be another way to do this)
        for (int i = 0; i <= nb; i++) // let's try all possible values
        {
            // to check if we can reach the target with the other coins
            int sol_cand = permutations(target - i * coin, coins, array_size);
            if (sol_cand >= 0) // if we did
            {
                if (sol == -1 || sol_cand + i < sol) // and the new solution is better
                    sol = sol_cand + i; // then we are happy
            }
        }
        return sol;
    }
}

这已经比你的例子快24倍了。

现在,您可以尝试添加优化。一个简单的优化是说,如果你能用最大的硬币达到当前的目标,就没有必要更进一步,因为如果你使用较小的硬币,你将需要更多的硬币。

代码语言:javascript
复制
    int coin = coins[array_size]; // let's consider the biggest coin
    int nb = target/coin; // and check how many times it fits in the target
    if (nb * coin == target) // if it fits an exact number of time, we won't beat this
        return nb;

这使得这个例子的代码速度快了70倍。(如果这样做,检查target == 0是否不再相关,因为我们将始终拥有target > 0)。

另外,不是一个特定的优化,但是如果您想像以前一样在Integer.MAX_VALUE中使用,可以重写循环:

代码语言:javascript
复制
    int sol = Integer.MAX_VALUE; // we haven't found a solution so far (Integer.MAX_VALUE would be another way to do this)
    for (int i = 0; i <= nb; i++) // let's try all possible values
    {
        sol = Math.min(sol, i + permutations(target - i * coin, coins, array_size));
    }

这件事就这么简单。然后,您可以编写单元测试,以检查您的代码是否以您想要的方式运行。最后,如果您愿意,可以考虑各种优化:使用缓存避免多次计算相同的事情,添加条件以更早地停止循环,等等。

票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/75245

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档