提高这种组合算法的性能?

时间:2018-08-07 10:33:17

标签: javascript algorithm performance combinations permutation

我正在使用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),执行此算法要花费非常长的时间。

如何提高此代码的性能,还是应该将其完全废弃?

2 个答案:

答案 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(...);
}

然而,真正的改进是选择了一种更好的算法。如果利用问题的数学结构,则可能无需遍历所有解决方案即可找到解决方案的数量。

以下见解可能会有所帮助:

  • 一个数字可以被三整除,且前提是其数字的总和为三。
  • 当且仅当除以3的余数之和为0时,数字之和可以除以3。
  • 输入中的数字顺序无关紧要

答案 1 :(得分:0)

一个(第一个)优化是仅检查或生成数字的数字之和可被3整除的数字,因为只有那些数字可被3整除。

因此,在您的示例(362中,您可以跳过与3 and 26 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.