【问题标题】:Coin exchange algorithm硬币交换算法
【发布时间】:2014-12-27 18:21:33
【问题描述】:

我正在开发一款扑克游戏。在我的应用程序中,我有一个课程ChipSetChipSet 基本上是一个由 5 个整数组成的数组(每个颜色扑克筹码一个)。

public class ChipSet {

    public static final int WHITE_VALUE = 1;
    public static final int RED_VALUE = 2;
    public static final int GREEN_VALUE = 5;
    public static final int BLUE_VALUE = 10;
    public static final int BLACK_VALUE = 20;

    public static final int[] VALUES = new int[]{WHITE_VALUE, RED_VALUE, GREEN_VALUE, BLUE_VALUE, BLACK_VALUE};

    protected int[] chips;

    public ChipSet(int[] chips) {
        if (chips == null || chips.length != 5)
            throw new IllegalArgumentException("ChipSets should contain exactly 5 integers!");

        // store a copy of passed array
        this.chips = new int[5];
        for (int i = 0; i < this.chips.length; i++) {
            this.chips[i] = chips[i];
        }
    }

    public int getSum() {
        return chips[0] * WHITE_VALUE +
                chips[1] * RED_VALUE +
                chips[2] * GREEN_VALUE +
                chips[3] * BLUE_VALUE +
                chips[4] * BLACK_VALUE;
    }
}

现在,如果我有一个 ChipSet 和数组 [0,0,2,1,0] (5+5+10 = 20),我想用我的 ChipSet 支付 16 个单位的费用。我必须交换筹码才能做到这一点。

我需要一种算法,尽可能高效地交换(交换尽可能少的筹码)以使这样的支付成为可能。付款只会从数组中减去筹码的数量,因此在付款后剩余的筹码仍将在ChipSet 中。我该怎么做?

我需要的是这种方法:

public boolean exchangeToMakeAvailable(int goal) {
    // algorithm to exchange chips as efficient as possible

    // store the new values in the array, making sure the sum is still the same

    // return whether it has succeeded (false when the sum is less than the requested amount)
}

如果您能帮我解决这个问题,将不胜感激。

例如:

在算法之前,我有一个带有数组[0,0,2,1,0] 的芯片组,其总和为 20。我想支付 16 个单位的费用。使用算法后,我会尽可能地进行最有效的交换,在这种情况下,算法会将数组更改为[1,2,1,1,0],它的总和也是20,但现在我可以支付16。支付后ChipSet 将拥有数组 [0,2,0,0,0]

我希望我的问题很清楚,如果不是,请发表评论,我会进一步解释。

【问题讨论】:

  • 你能给我们一些例子吗?如果chips 在输入时是[0,0,2,1,0] 并且goal 是16,那么chips 之后会是什么,函数结果会是什么?可能还有其他类似的例子。抱歉,问题陈述对我来说不是很清楚。
  • 好问题!这是一个很棒的算法练习!
  • 我添加了一个带有前置条件和后置条件的示例。
  • 对我来说不清楚的部分是“尽可能高效(交换尽可能少的筹码)” - 从字面上看,我相信你总是可以通过交换你最有价值的筹码来解决它。那么,您的成本函数到底是什么?给予和接受的筹码数量?交易后剩余筹码数?...
  • 我认为您应该始终使用“总”。所以:你有 20 个并且想从你的总数中打折 -16。你“呈现”筹码总数的方式无关紧要,只要它是正确的!我会实现一个函数来“标准化”芯片数组中的总数,总是使用尽可能少的芯片!

标签: java algorithm


【解决方案1】:

这是一个编辑,我完全修改了我的答案。

**从游戏角度来看问题** 玩家有 2 个绿色筹码(5 分)和 1 个蓝色筹码(10 分)。这总分是 20 分。现在玩家想要购买一个花费 16 点的游戏图标。玩家有足够的钱购买该物品。所以玩家支付了 16 点,但是他会给商店多少点才能正确支付。

现在我已经编写了一个工作示例,所有工作都已完成。

代码

程序.java

import java.util.Arrays;

public class Program {

    public static void main(String[] args) {
        // Setting up test environment
        Player player = new Player("Borrie", new int[]{0,0,0,0, 230});
        int itemCost = 16626;
        // Pay for item
        System.out.printf("First we check if the player can pay with it's current ChipSet");
        if (!player.canPayWithChipSet(player.getChips(), 5)) {
            if (player.exchangeChips(5)) {
                System.out.printf("\n\nThe players ChipSet:" + Arrays.toString(player.getChips().chips));
                System.out.printf("\nThe players ChipSet has been succesfully exchanged.");
            } else {
                System.out.printf("\n\nThe players ChipSet:" + Arrays.toString(player.getChips().chips));
                System.out.printf("\nThe players ChipSet was not able to be exchanged.\n");
            }
        } else {
            System.out.printf("\n\nThe player can pay exact with it's original ChipSet. No need to exchange.");
        }

    }
}

Player.java

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

public class Player {

    private String name;
    private ChipSet chips;
    private int points = 0;

    public Player(String name, int[] chips) {
        this.name = name;
        this.chips = new ChipSet(chips);
        this.points = this.chips.getSum();
    }

    public boolean exchangeChips(int cost) {
        ChipSet newChipSet = exchangePlayerChipSet(this.chips.getChips(), cost);
        if (newChipSet == null) {
            return false;
        } else {
            this.chips = newChipSet;
            return true;
        }
    }

    public ChipSet exchangePlayerChipSet(int[] originalChipValues, int cost) {
        ChipSet newChipSet = null;
        // Create possible combinations to compare
        ArrayList<ChipSet> chipSetCombos = createCombinations(this.chips.getChips());
        // Filter the chipset based on if it's able to pay without changing chips
        System.out.printf("\n\n---- Filter which of these combinations are able to be payed with without changing chips ----");
        ArrayList<ChipSet> filteredCombos = filterCombinations(chipSetCombos, cost);
        // Compare the filtered chipsets to determine which one has changed the least
        if (!filteredCombos.isEmpty()) {
            newChipSet = compareChipSets(originalChipValues, filteredCombos);
        }
        return newChipSet;
    }

    private ArrayList<ChipSet> createCombinations(int[] array) {
        ArrayList<ChipSet> combos = new ArrayList<>();
        int[] startCombo = array;
        System.out.printf("Player has " + getTotalPoints(startCombo) + " points in chips.");
        System.out.printf("\nPlayer has these chips (WHITE,RED,GREEN,BLUE,BLACK): " + Arrays.toString(startCombo));

        while (startCombo[4] != 0) {
            startCombo = lowerBlack(startCombo);
            if (getTotalPoints(startCombo) == points) {
                combos.add(new ChipSet(startCombo));
            }
        }
        while (startCombo[3] != 0) {
            startCombo = lowerBlue(startCombo);
            if (getTotalPoints(startCombo) == points) {
                combos.add(new ChipSet(startCombo));
            }
        }
        while (startCombo[2] != 0) {
            startCombo = lowerGreen(startCombo);
            if (getTotalPoints(startCombo) == points) {
                combos.add(new ChipSet(startCombo));
            }
        }
        while (startCombo[1] != 0) {
            startCombo = lowerRed(startCombo);
            if (getTotalPoints(startCombo) == points) {
                combos.add(new ChipSet(startCombo));
            }
        }
        System.out.printf("\n\n---- Creating variations on the players chips ----");
        System.out.printf("\nVariation (all worth " + getTotalPoints(startCombo) + " points):\n");
        int teller = 1;
        for (ChipSet a : combos) {
            System.out.printf("\nCombo " + teller + ": " + Arrays.toString(a.getChips()));
            teller++;
        }
        return combos;
    }

    private ArrayList<ChipSet> filterCombinations(ArrayList<ChipSet> combinations, int cost) {
        ArrayList<ChipSet> filteredChipSet = new ArrayList<>();
        combinations.stream().filter((cs) -> (canPayWithChipSet(cs, cost))).forEach((cs) -> {
            filteredChipSet.add(cs);
        });
        return filteredChipSet;
    }

    // This method has be worked out
    public boolean canPayWithChipSet(ChipSet cs, int cost) {
        ChipSet csOrig = new ChipSet(cs.chips);
        ChipSet csCopy = new ChipSet(cs.chips);
        int counterWhite = 0;
        int counterRed = 0;
        int counterGreen = 0;
        int counterBlue = 0;
        int counterBlack = 0;

        while (20 <= cost && cost > 0 && csOrig.getChips()[4] != 0) {
            csOrig.getChips()[4] -= 1;
            cost -= 20;
            counterBlack++;
        }
        while (10 <= cost && cost > 0 && csOrig.getChips()[3] != 0) {
            csOrig.getChips()[3] -= 1;
            cost -= 10;
            counterBlue++;
        }
        while (5 <= cost && cost > 0 && csOrig.getChips()[2] != 0) {
            csOrig.getChips()[2] -= 1;
            cost -= 5;
            counterGreen++;
        }
        while (2 <= cost && cost > 0 && csOrig.getChips()[1] != 0) {
            csOrig.getChips()[1] -= 1;
            cost -= 2;
            counterRed++;
        }
        while (1 <= cost && cost > 0 && csOrig.getChips()[0] != 0) {
            csOrig.getChips()[0] -= 1;
            cost -= 1;
            counterWhite++;
        }
        if (cost == 0){
            System.out.printf("\nCombo: %s can pay exact. With %d white, %d red, %d green, %d blue an %d black chips", Arrays.toString(csCopy.chips),counterWhite,counterRed,counterGreen,counterBlue,counterBlack);
            return true;
        } else {
            System.out.printf("\nCombo: %s cannot pay exact.\n\n\n", Arrays.toString(csCopy.chips));
            return false;
        }    
    }

    private ChipSet compareChipSets(int[] originalChipValues, ArrayList<ChipSet> chipSetCombos) {
        ChipSet newChipSet;
        int[] chipSetWaardes = originalChipValues; // originele chipset aantal van kleur
        int[] chipSetCombosDifferenceValues = new int[chipSetCombos.size()];
        int teller = 1;

        System.out.printf("\n\n---- Calculate differences between players stack and it's variations ----");
        for (ChipSet cs : chipSetCombos) {
            int aantalWhite = cs.getChips()[0];
            int aantalRed = cs.getChips()[1];
            int aantalGreen = cs.getChips()[2];
            int aantalBlue = cs.getChips()[3];
            int aantalBlack = cs.getChips()[4];
            int differenceWhite = Math.abs(chipSetWaardes[0] - aantalWhite);
            int differenceRed = Math.abs(chipSetWaardes[1] - aantalRed);
            int differenceGreen = Math.abs(chipSetWaardes[2] - aantalGreen);
            int differenceBlue = Math.abs(chipSetWaardes[3] - aantalBlue);
            int differenceBlack = Math.abs(chipSetWaardes[4] - aantalBlack);
            int totalDifference = differenceWhite + differenceRed + differenceGreen + differenceBlue + differenceBlack;
            chipSetCombosDifferenceValues[teller - 1] = totalDifference;
            System.out.printf("\nCombo " + teller + ": " + Arrays.toString(cs.getChips()) + " = " + totalDifference);
            teller++;
        }
        newChipSet = chipSetCombos.get(smallestValueOfArrayIndex(chipSetCombosDifferenceValues));
        System.out.printf("\n\nThe least different ChipSet is: " + Arrays.toString(newChipSet.getChips()) + " ");
        return newChipSet;
    }

    private int smallestValueOfArrayIndex(int[] array) {
        int huidigeWaarde = array[0];
        int smallestIndex = 0;
        for (int j = 1; j < array.length; j++) {
            if (array[j] < huidigeWaarde) {
                huidigeWaarde = array[j];
                smallestIndex = j;
            }
        }
        return smallestIndex;
    }

    private int[] lowerBlack(int[] array) {
        return new int[]{array[0], array[1], array[2], array[3] + 2, array[4] - 1};
    }

    private int[] lowerBlue(int[] array) {
        return new int[]{array[0], array[1], array[2] + 2, array[3] - 1, array[4]};
    }

    private int[] lowerGreen(int[] array) {
        return new int[]{array[0] + 1, array[1] + 2, array[2] - 1, array[3], array[4]};
    }

    private int[] lowerRed(int[] array) {
        return new int[]{array[0] + 2, array[1] - 1, array[2], array[3], array[4]};
    }

    private int getTotalPoints(int[] array) {
        return ((array[0] * 1) + (array[1] * 2) + (array[2] * 5) + (array[3] * 10) + (array[4] * 20));
    }

    public String getName() {
        return this.name;
    }

    public int getPoints() {
        return this.points;
    }

    public ChipSet getChips() {
        return chips;
    }

}

ChipSet.java

public class ChipSet {

    public static final int WHITE_VALUE = 1;
    public static final int RED_VALUE = 2;
    public static final int GREEN_VALUE = 5;
    public static final int BLUE_VALUE = 10;
    public static final int BLACK_VALUE = 20;

    public static final int[] VALUES = new int[]{WHITE_VALUE, RED_VALUE, GREEN_VALUE, BLUE_VALUE, BLACK_VALUE};

    protected int[] chips;

    public ChipSet(int[] chips) {
        if (chips == null || chips.length != 5) {
            throw new IllegalArgumentException("ChipSets should contain exactly 5 integers!");
        }

        // store a copy of passed array
        this.chips = new int[5];
        for (int i = 0; i < this.chips.length; i++) {
            this.chips[i] = chips[i];
        }
    }

    public int getSum() {
        return chips[0] * WHITE_VALUE
                + chips[1] * RED_VALUE
                + chips[2] * GREEN_VALUE
                + chips[3] * BLUE_VALUE
                + chips[4] * BLACK_VALUE;
    }

    public int[] getChips() {
        return this.chips;
    }
}

一些解释:

  • 创建组合

我们创建了一些子方法,将筹码换成较低的筹码。所以 例如黑色 = 2 个蓝色。然后我们按顺序创建 5 个循环。这 第一个检查是否还有黑色筹码,如果有,减少 1 个黑色 添加2个蓝调。将此新组合保存在列表中,如果 新 ChipSet 中的筹码等于原始 ChipSets 值。环形 一直持续到没有黑人为止。然后它检查是否存在 是蓝色并重复相同的过程,直到没有红色 了。现在我们列出了 X 值的所有可能变化 筹码。

  • 过滤组合

您根据以下条件过滤 ChipSet 如果您可以在不交换的情况下与他们支付 X 积分。我们遍历所有 上一部分中创建的芯片组的可能组合。如果你 可以使用 ChipSet 支付而无需交换将其添加到过滤列表 芯片组。结果是一个仅包含有效芯片组的归档列表。

  • 计算差异

对于每个 ChipSet,我们计算一个中所有颜色的芯片数量 ChipSet 并用它减去原始芯片组的芯片数。 你把它的绝对值加起来 原始颜色和组合颜色的差异。现在我们有一个 代表与原始差异的数字。现在我们都 所要做的就是比较所有芯片组的“差异数”。唯一的那个 我们用来分配给播放器的差异最小。

简单吧

【讨论】:

  • 花了我一段时间,但制作它很有趣:) 确切的实现取决于你,但算法就在那里。
  • 关于最佳交换,您希望尽可能少的移动。因此,尽可能提供最大的筹码。更少的筹码 = 更少的移动。
  • 您的算法无法保持总和!它返回一个ChipSet,总和为pointsToExchange
  • 等一下。我刚醒过来。保持总和是什么意思?如果你能用荷兰语解释。
  • 算法后,getSum()的结果应该和算法前一样。
【解决方案2】:

您可以尝试装箱。对筹码进行分类并选择最合适的。然后计算它还剩下多少,然后换一个筹码或冲洗并重复。

【讨论】:

  • 这是一种算法,它试图解决寻找物品的最佳数量/大小/位置的问题,就好像这些物品被打包到一个箱子里一样。
  • 你能解释得详细一点吗?
  • @MarkBuikema:Binpacking 非常快,当你有太多点时更好。你也可以尝试对组合进行排序,只找到附近的组合:stackoverflow.com/questions/25011551/…
猜你喜欢
  • 2021-12-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多