【问题标题】:Find m numbers that sum to n in a sequence在一个序列中找到m个总和为n的数字
【发布时间】:2021-06-14 11:47:49
【问题描述】:

给定一个从 1 到 n 的序列,我想找到所有 唯一 大小为 m 且总和为 的子序列n。子序列不需要是连续的。例如

if m = 2, n = 5
The possible unique subsequences of size 2 are {1,4} and {2,3}

到目前为止,我已经能够使用递归生成所有子序列,但我的代码不仅仅返回唯一的子序列,结果中有一些重复。

function allCombinations(m, n) {
if (m < 1) {
    return [];
}

let helper = function(m, n, arr, result, index){
    if (n < 0 || m < 0) {
        return 0;
    }

    if (m === 0 && n === 0) {
        result.push(arr.slice());
        return 1;
    }

    for (let i = index; i <= n; i++){
        arr.push(i);
        helper(m - 1, n - i, arr, result, index + 1);
        arr.pop();
    }

}

let result = [];
helper(m, n, [], result, 0);

return result;
}

当调用时

let res = allCombinations(2, 5);

结果是

[ [ 1, 4 ], [ 2, 3 ], [ 3, 2 ] ]

如您所见,{3,2} 是 {2,3} 的副本。如何更改我的代码以使其仅返回唯一序列?

【问题讨论】:

    标签: javascript algorithm recursion


    【解决方案1】:

    我会为这类问题推荐生成器 -

    1. 1..n找到解决方案
    2. 大小为m
    3. 其中q是目标总和,初始化为n

    function* solve(n, m, q = n)
    { if (m == 0 && q == 0)
        yield []
      else if (n == 0 || q < 0)
        return
      else
      { for (const s of solve(n - 1, m - 1, q - n))
          yield [...s, n]
        yield *solve(n - 1, m, q)
      }
    }
    
    for (const p of solve(5, 2))
      console.log(p)
    
    // [1,4]
    // [2,3]

    我们可以通过使用解决方案的另一个默认参数s 来折叠上面的for 循环 -

    function* solve(n, m, q = n, s = [])             // s = []
    { if (m == 0 && q == 0)
        yield s
      else if (n == 0 || q < 0)
        return
      else
      { yield *solve(n - 1, m - 1, q - n, [n, ...s]) // update s
        yield *solve(n - 1, m, q, s)                 // pass s
      }
    }
    
    for (const p of solve(5, 2))
      console.log(p)
    
    // [1,4]
    // [2,3]

    我们可以使用早期的return 折叠if 语句-

    function* solve(n, m, q = n, s = [])
    { if (m == 0 && q == 0) return yield s  // <-
      if (n == 0 || q < 0) return
      yield *solve(n - 1, m - 1, q - n, [n, ...s])
      yield *solve(n - 1, m, q, s)
    }
    
    console.log(JSON.stringify(Array.from(solve(5,2))))
    // [[1,4],[2,3]]
    
    console.log(JSON.stringify(Array.from(solve(9,3))))
    // [[1,2,6],[1,3,5],[2,3,4]]
    
    console.log(JSON.stringify(Array.from(solve(27,6))))
    // [[1,2,3,4,5,12],[1,2,3,4,6,11],[1,2,3,4,7,10],[1,2,3,5,6,10],[1,2,3,4,8,9],[1,2,3,5,7,9],[1,2,4,5,6,9],[1,2,3,6,7,8],[1,2,4,5,7,8],[1,3,4,5,6,8],[2,3,4,5,6,7]]

    如果您不喜欢solve 上的额外参数,我们可以使用内部loop -

    function solve(n, m)
    { function* loop(n, m, q, s)                   // "helper"
      { if (m == 0 && q == 0) return yield s
        if (n == 0 || q < 0) return
        yield *loop(n - 1, m - 1, q - n, [n, ...s])
        yield *loop(n - 1, m, q, s)
      }
      return loop(n, m, n, [])                     // n, m, q = n, s = []
    }
    
    console.log(JSON.stringify(Array.from(solve(5,2))))
    // [[1,4],[2,3]]
    
    console.log(JSON.stringify(Array.from(solve(9,3))))
    // [[1,2,6],[1,3,5],[2,3,4]]
    
    console.log(JSON.stringify(Array.from(solve(27,6))))
    // [[1,2,3,4,5,12],[1,2,3,4,6,11],[1,2,3,4,7,10],[1,2,3,5,6,10],[1,2,3,4,8,9],[1,2,3,5,7,9],[1,2,4,5,6,9],[1,2,3,6,7,8],[1,2,4,5,7,8],[1,3,4,5,6,8],[2,3,4,5,6,7]]

    如果您不喜欢它公开生成器,您可以使用 Array.from 返回所有结果 -

    function solve(n, m)
    { function* loop(n, m, q, s)
      { if (m == 0 && q == 0) return yield s
        if (n == 0 || q < 0) return
        yield *loop(n - 1, m - 1, q - n, [n, ...s])
        yield *loop(n - 1, m, q, s)
      }
      return Array.from(loop(n, m, n, []))   // Array.from
    }
    
    console.log(JSON.stringify(solve(5,2)))  // solve now returns array
    // [[1,4],[2,3]]
    
    console.log(JSON.stringify(solve(9,3)))  // <-
    // [[1,2,6],[1,3,5],[2,3,4]]
    
    console.log(JSON.stringify(solve(27,6))) // <-
    // [[1,2,3,4,5,12],[1,2,3,4,6,11],[1,2,3,4,7,10],[1,2,3,5,6,10],[1,2,3,4,8,9],[1,2,3,5,7,9],[1,2,4,5,6,9],[1,2,3,6,7,8],[1,2,4,5,7,8],[1,3,4,5,6,8],[2,3,4,5,6,7]]

    随心所欲地混合搭配任何这些技巧!

    【讨论】:

      【解决方案2】:

      您可以通过使用i 而不是index 调用递归辅助函数来强制下一个选择大于当前选择:

      for (let i = index; i <= n; i++){
          arr.push(i);
          helper(m - 1, n - i, arr, result, i + 1);
          arr.pop();
      }
      

      这将产生唯一的序列,其中元素按升序排序。如果您只使用i 调用它,则允许重复选择。

      除非您从包装函数调用索引为 1 的助手,否则更改此项将允许选择零:

      let result = [];
      helper(m, n, [], result, 1);
      

      【讨论】:

      • 这是我正在寻找的答案,我知道它与索引有关,但找不到问题所在。感谢您指出这一点:)
      【解决方案3】:
      const res = allCombinations(2, 5);
      
      const unique = res.reduce((acc, curr) => {
         const exists = acc.some(item => item.sort().toString() === curr.sort().toString());
      
         return exists ? acc : [...acc, curr]; 
      }, []);
      

      【讨论】:

      • 感谢您的解决方案,我实际上是在避免过滤掉结果,但这肯定适用于我拥有的代码
      猜你喜欢
      • 1970-01-01
      • 2011-02-08
      • 1970-01-01
      • 1970-01-01
      • 2014-09-04
      • 2014-09-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多