【发布时间】:2017-11-21 12:45:26
【问题描述】:
上周我参加了面试,我被要求为通用 ATM 算法编写代码。
所以我必须实现 ATM 功能,它可以获取所有可用纸币及其限额和所需金额,并返回任何可能的方式,将给定纸币中的这些金额与其限额相结合。
ATM 函数实现的第一次迭代非常简单,因为限制看起来像
{
1000: 20,
500: 430,
100: 300,
50: 23,
10: 23
}
但在最后一次迭代的任务限制看起来像
{
1000: 20,
500: 430,
100: 300,
50: 23,
30: 23
}
我想,我成功地实现了这个算法的工作版本,但我对它的最终复杂性不满意,我觉得应该有更有效的东西。
我的决定如下:
- 为给定货币创建所有可能的子集,因此 [100, 50, 30] set 将被转换为 7 个子集:[[100], [50], [30], [100, 50], [100, 30], [50, 30], [100, 50, 30]]。
-
尝试在每个子集中形成所需的金额,所以如果我必须处理 有 180 的数量,我会尝试形成这个数量
- 仅使用 100 张钞票,此尝试失败
- 仅使用 50 张纸币,此尝试失败
- 仅使用 30 张钞票,此尝试成功,结果类似于 {30:6}
- 使用 50 和 30 张纸币,此尝试成功,结果类似于 {50:3, 30:1}
- 对于 100 和 50 纸币,此尝试失败
- 对于 100 和 30 纸币,此尝试失败
- 对于 100、50 和 30 张纸币,此尝试成功,结果类似于 {100:1,50:1,30:1}
从成功的变体中选择任何我喜欢的并作为结果返回。
我在 JavaScript 中的决定的工作代码如下:
function getAllSubsetsInSet(set) {
const result = [];
let mask = 1;
do {
let maskString = mask.toString(2).padStart(set.length, '0');
result.push(set.filter((item, index) => maskString[index] === '1'));
mask++;
}while (mask < (2 ** set.length));
return result;
}
function getMoney(currencies, limits, amount) {
const sorted = currencies.sort((a, b) => b - a);
let workingLimits = {
...limits
};
let workingAmount = amount;
let result = {};
for (let i = 0; i < sorted.length; i++) {
let currentCurrency = sorted[i];
let desiredBanknotes = Math.floor(workingAmount / currentCurrency);
let availableBanknotes = workingLimits[currentCurrency];
let banknotesToBeUsed = (availableBanknotes < desiredBanknotes) ? availableBanknotes : desiredBanknotes;
workingAmount = (workingAmount - (banknotesToBeUsed * currentCurrency));
workingLimits[currentCurrency] = availableBanknotes - banknotesToBeUsed;
result[currentCurrency] = banknotesToBeUsed;
}
if (workingAmount > 0) {
return {
result: {},
limits,
error: true
}
}
return {
result: result,
limits: workingLimits,
error: false
}
}
function ATM(limits, amount) {
let currencies = Object.keys(limits).map(item => Number(item));
let allCurrencyCombinations = getAllSubsetsInSet(currencies);
let resultsForEachCombination = allCurrencyCombinations.map(combination => {
return getMoney(combination, limits, amount);
});
const succeedResults = resultsForEachCombination.filter(variant => !variant.error);
if (succeedResults.length) {
return succeedResults;
}
return {
result: 'No possible ways',
limits
}
}
console.log(ATM(
{
1000: 20,
500: 430,
100: 300,
50: 23,
30: 90
},
180
));
但是请,谁能帮助我正确有效的方式来实现这个逻辑?任何编程语言和伪代码都可以。
谢谢!
【问题讨论】:
-
这两个列表的限制不是完全一样吗?另外,您必须返回所有种可能的组合,还是只返回一个?
-
@tobias_k 不,10 面额更改为 30 面额,不再划分其他面额。因此,它将打破天真的贪心算法。
-
自从您发布了这个问题以来,您似乎错过了一个关键的搜索词。尝试“总和到目标值”。这个问题已经在网上用多种语言解决了很多次了。
-
@Prune,谢谢你的搜索词,我什至不知道这样的词组合。我会尽快学习,如果这解决了我的问题,我会回答。
标签: algorithm performance