我正在使用Codewars开发的this kata。任务是:
给定一个数字,您的数字可以得到3的多少倍?
假设您有数字362。可以从中生成的数字是:
362 ----> 3, 6, 2, 36, 63, 62, 26, 32, 23, 236, 263, 326, 362, 623, 632
我编写了以下递归函数来计算所有可能性:
const findMult_3 = (num) => {
const powerset = (set) => {
const combinations = []
const combine = (prefix, chars) => {
for (let i = 0; i < chars.length; i++) {
const newPrefix = parseInt(prefix + chars[i])
if (!combinations.includes(newPrefix)) {
combinations.push(newPrefix)
} else {
console.log('encountered duplicate')
}
combine(newPrefix, chars.filter((x, ind) => ind !== i))
}
}
combine('', set)
return combinations.sort((a, b) => a - b)
}
const allCombinations = powerset(num.toString().split(''))
const factorsOfThree = allCombinations.filter(x => x % 3 === 0).filter(x => x !== 0)
return [factorsOfThree.length, factorsOfThree.pop()]
}
findMult_3(43522283000229)
我很早就注意到我遇到了很多重复的案例,因此console.log('encountered duplicate')
标志出现了。
对于大数(例如43522283000229
),执行此算法要花费非常长的时间。
如何提高此代码的性能,还是应该将其完全废弃?
答案 0 :(得分:1)
对于大多数编码选项,算法的选择要比实现细节更为重要,但是在开始之前,让我指出您实现中最明显的缺陷:
if (!combinations.includes(newPrefix)) {
combinations.push(newPrefix)
} else {
console.log('encountered duplicate')
}
combine(newPrefix, chars.filter((x, ind) => ind !== i))
combinations
是一个数组,includes
的工作原理是遍历数组并检查每个元素。也就是说,要检查某个元素是否为重复项,请将其与每个先前遇到的组合进行比较。由于其中的指数成倍增加,所以这将非常缓慢。如果您使用字典对象或Map
,则您的代码会更快。
此外,即使组合是重复的,您是否注意到您仍在继续生成组合?那是多余的。
因此便宜的改进是:
const combinations = {};
if (combinations[prefix]) {
// duplicate, do nothing
} else {
combinations[prefix] = true;
combine(...);
}
然而,真正的改进是选择了一种更好的算法。如果利用问题的数学结构,则可能无需遍历所有解决方案即可找到解决方案的数量。
以下见解可能会有所帮助:
答案 1 :(得分:0)
一个(第一个)优化是仅检查或生成数字的数字之和可被3整除的数字,因为只有那些数字可被3整除。
因此,在您的示例(362
中,您可以跳过与3 and 2
,6 and 2
的所有组合以及所有3位数字的可能组合(因为3位数字的总和不可除3)。
对于较大的数字(43522283000229
),您可以跳过很多,例如,所有带有数字的组合:
43, 35, 52, ...
435, 352, .., 283, ...
4352 (thus, including all possible combinations of those 4 digits), ...
43522, ...
43528, 43529, ...
43528309, ...
and so on
可能的算法:
Original number: 43522283000229
First, sort the digits: 00022222334589
Then find all distinct combinations of digits where
their sum is a multiple of 3:
left to right:
1 digit : 3, 9
2 digits: 03, 09, 24, 33, 39, 45, 48, ...
3 digits: 003, 009, 024, 033, 039, 222, 234, ...
n digits ...
Now, for all above numbers create every possible combination with their
digits, skip those with leading zeros.:
3, 9, 30, 90, 24, 42, 33, 39, 93, 45, 54, 48, 84, 300, 900,
204, 240, 402, 420, 309, 390, 903, 930, 222, 234, 243, ...
We don't have to check for division by 3, they all match.
We don't have to check for duplicates.
You could then sort the resulting list if needed.