JavaScript生成器函数在尝试复制Python时超时';s itertools.组合

JavaScript生成器函数在尝试复制Python时超时';s itertools.组合,javascript,python,ecmascript-6,combinations,permutation,Javascript,Python,Ecmascript 6,Combinations,Permutation,在这里有几个答案的帮助下,我已经能够开始学习生成器并开发以下功能: function* icombinations(arr, k) { function* getCombinations(newArr, shift) { if (newArr.length === k) { yield newArr; } for (let i = shift; i < arr.length; i++) { yield* getCombinations

在这里有几个答案的帮助下,我已经能够开始学习生成器并开发以下功能:

function* icombinations(arr, k) {

  function* getCombinations(newArr, shift) {
    if (newArr.length === k) {
      yield newArr;
    }

    for (let i = shift; i < arr.length; i++) {
      yield* getCombinations([...newArr, arr[i]], i + 1);
    }
  }

  yield* getCombinations([], 0);

  return [];
}
函数*i组合(arr,k){
函数*getCombinations(newArr、shift){
if(newArr.length==k){
产量新增;
}
for(设i=shift;i
以下是repl.it的链接:

我可能还没有完全理解这个概念,因为上面的函数会超时很长时间,因为我试图先生成所有可能的组合,然后生成每个组合。你知道我如何重构函数,这样我就不会先生成所有的组合了吗

以下是我试图解决的挑战的描述:

编写一个名为
icombinations
的函数,该函数应该是一个生成器函数,其行为类似于 Python的itertools.compositions。您将获得一个唯一的数组
arr
项目和整数
k

您应该在长度
arr
中生成每个元素的唯一组合
k
在没有可能的唯一性之前不进行替换 左侧组合,此时应终止生成器 功能。在某些情况下,将使用
next()
调用生成器 它将被调用,直到完成

此外,以相同的格式返回组合也很重要 按原始阵列的顺序排列
arr
。(见下面的例子)

例如:

给定一个唯一元素数组
示例\u arr
和一个整数
示例k

其中
example_arr=['a','b','c','d']
example_k=2

调用迭代器的
next()
方法应返回
['a','b']

如果我们再次调用
next()
,我们应该得到
['a','c']
等等 在

因此,如果我们得到生成器产生的所有值,我们将 以下是:

['a','b']['a','c']['a','d']['b','c']['b','d'][
“c”,“d']
请再次注意上面的顺序,因为您需要 在您的解决方案中复制它

还有一些事情需要考虑:

如果您的解决方案超时,可能是因为您试图 首先生成所有可能的组合,然后生成每个组合。 这违背了发电机的观点。一些输入值将被删除 大的

arr
中的值始终是唯一的,但它们可能是不同类型的 (即字符串、整数和其他对象)

您无法生成组合的唯一情况 是指
arr
为空或长度小于
k
的值。在里面 在这些情况下,您应该返回一个空数组


您可能能够在代码审查方面获得更好的建议,但您可以尝试的一个改进是删减一些“死胡同”递归路径。因为您知道每个结果必须是length
k
,所以只有当源数组中剩下足够的元素来实际完成k子集时,才应该递归

function* icombinations(arr, k) {

    function* getCombinations(newArr, shift) {
        if (newArr.length === k) {
            yield newArr;
        } 
        // if what's available is >= what's needed
        else if (arr.length - shift >= k - newArr.length) {
            for (let i = shift; i < arr.length; i++) {
                yield* getCombinations([...newArr, arr[i]], i + 1);
            }
        }
    }

    yield* getCombinations([], 0);

    return [];
}
函数*i组合(arr,k){
函数*getCombinations(newArr、shift){
if(newArr.length==k){
产量新增;
} 
//如果可用的>=需要的
else if(arr.length-shift>=k-newArr.length){
for(设i=shift;i
但是如果没有您的测试用例或对
arr.length
k
的限制,我们就无法知道这是否足够好。您提到,
arr.length
可以是50,这意味着当
k
为25时,最多有126410606437752个子集。无论算法有多高效,都无法在合理的时间内完成。即使
k
为5(或相当于45),您也会看到2118760个组合

您可以尝试的另一件事是在内部函数外部预先分配子集数组(
newArr
),然后在每次递归调用之前就地更新数组。这避免了每次要向其追加值时都需要复制
newArr
,但在基本情况下仍需要生成
newArr
的副本。然而,与分支修剪相比,这更像是一种微观优化。首先自己尝试修剪,看看每个更改能带来多少改进


最后,您还可以切换到迭代实现,看看它是否有效。

不要先生成所有的实现;这就是拥有发电机的意义所在。您的描述明确地说明了这一点(首先要考虑的事情)。谢谢Scott,我已经读了好几遍了,不知怎的,我不知道如何修改函数。我想唯一的问题是
返回[]
。据我所知,这不属于那里。@ScottSauyet我需要添加
return[]
语句,因为质询底部的要求。该函数提供所有必需的结果,唯一的问题是它对长数组超时。@PiotrBerebecki在您仅调用
icombinations()
或迭代结果时超时吗?还有,这些“长数组”有多长?