【问题标题】:Generate valid combinations of numbers in array of digits在数字数组中生成有效的数字组合
【发布时间】:2017-12-26 11:13:36
【问题描述】:

我正在尝试从一个数字数组中生成所有有效的数字组合。假设我们有以下内容:

let arr = [1, 2, 9, 4, 7];

我们需要输出如下内容:

1 2 9 4 7
1 2 9 47
1 2 94 7
1 2 947
1 29 4 7
1 29 47
1 294 7
1 2947
12 9 4 7
12 9 47
12 94 7
12 947
129 4 7
129 47
1294 7
12947

无效的数字是 91、497、72 等等。

我试过了,但我对结果不满意:

const combination = (arr) => {

  let i, j, temp;
  let result = [];
  let arrLen = arr.length;
  let power = Math.pow;
  let combinations = power(2, arrLen);

  for (i = 0; i < combinations; i += 1) {
    temp = '';

    for (j = 0; j < arrLen; j++) {
      if ((i & power(2, j))) {
        temp += arr[j];
      }
    }
    result.push(temp);
  }
  return result;
}

const result = combination([1, 2, 9, 4, 7]);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

有什么想法吗?

【问题讨论】:

  • 不满意?您需要说明您遇到的问题或错误。
  • 你想用这个问题解决什么问题?
  • checkout github.com/dankogai/js-combinatorics - 你可能会找到适合排列的方法
  • @Xufox 你的代码完全符合我的要求。谢谢。但很难理解它是如何工作的。
  • @AlexandarTargov 我已经在答案中解释过了。

标签: javascript combinations combinatorics


【解决方案1】:

这段代码做你想做的事:

const arr = [1, 2, 9, 4, 7],
  result = Array.from({length: 2 ** (arr.length - 1)}, (_, index) => index.toString(2).padStart(arr.length - 1, "0"))
    .map((binary) => JSON.parse("[" + arr.map((num, position) => num + (Number(binary[position]) ? "," : "")).join("") + "]"));

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

结果:

[
  [12947],
  [1294, 7],
  [129, 47],
  [129, 4, 7],
  [12, 947],
  [12, 94, 7],
  [12, 9, 47],
  [12, 9, 4, 7],
  [1, 2947],
  [1, 294, 7],
  [1, 29, 47],
  [1, 29, 4, 7],
  [1, 2, 947],
  [1, 2, 94, 7],
  [1, 2, 9, 47],
  [1, 2, 9, 4, 7]
]

假设,预期结果不依赖于顺序,空格代表二进制模式:

12947     => 0000
1294 7    => 0001
129 47    => 0010
…
1 29 47   => 1010
…
1 2 9 4 7 => 1111

我们可以将此模式与我们转换为二进制字符串的计数器一起使用。我们还用 0 填充该字符串,因此它始终保持 4 位长度:

index.toString(2).padStart(arr.length - 1, "0")

对于arr中的n个数字,正好有2n - 1个组合,所以我们使用:

{length: 2 ** (arr.length - 1)}

这是一个 length 属性为 2arr.length - 1 的对象。

我们将这两件事结合到一个接受两个参数的Array.from 调用中:

  • 一个对象变成一个数组
  • 映射每个槽的函数

将具有length 属性的对象转换为数组意味着我们创建了一个具有length 很多槽的数组。

映射函数接受槽的索引作为第二个参数。我们只使用索引 — 作为我们二进制数的计数器。

所以,最后是整个表达式:

Array.from({length: 2 ** (arr.length - 1)}, (_, index) => index.toString(2).padStart(arr.length - 1, "0"))

计算为以下数组:

[
  "0000",
  "0001",
  "0010",
  "0011",
  "0100",
  "0101",
  "0110",
  "0111",
  "1000",
  "1001",
  "1010",
  "1011",
  "1100",
  "1101",
  "1110",
  "1111"
]

我们需要进一步将其映射到最终结果:

.map((binary) => …)

对于每个数组元素,binary 是上述数组中的二进制字符串之一。

为了转弯,例如"0110" 变成 "12,9,47" 之类的东西,我们也需要 map 超过 arrarr 中的每个数字 num 后面应该跟 , position,如果 binary1 position

arr.map((num, position) => num + (Number(binary[position]) ? "," : "")).join("")

表达式(Number(binary[position]) ? "," : "") 将指定位置的binary 计算为数字。如果它是truthy,即除了0 之外的任何东西,它评估为",",如果它是falsy,即0,它评估为""

所以中间数组看起来像["1", "2,", "9,", "4", "7"]。所有这些都加入到"12,9,47"

然后,JSON.parse("[" + ... + "]") 被当作一个数组来处理和解析,所以它变成了[12, 9, 47]。由于这些步骤适用于每个二进制字符串,因此您将得到最终结果。


  • 如果不支持 ECMAScript 7,2 ** (arr.length - 1) 可以替换为 Math.pow(2, arr.length - 1)
  • {length: 2 ** (arr.length - 1)} 可以替换为 new Array(2 ** (arr.length - 1))
  • (Number(binary[position]) ? "," : "") 可以替换为 ["", ","][Number(binary[position])]。在这种情况下,计算出的数字将用作临时数组的索引。

【讨论】:

  • Amazing Bro,我很高兴看到这个答案 :) 二进制模式位置描述
  • 一直以来,求幂的方法都是错误的……现在修好了……我希望。
【解决方案2】:

因此,您需要遍历所有数字之间的“空格”和“非空格”的所有组合。对于n 项,将有n - 1 空格和2 ** (n - 1) 不同的列表。

所以你可以这样做来获取所有可能的列表:

const combination = arr => {
    const len = arr.length;
    const n = Math.pow(2, len - 1);
    const combinations = [];
    for (let i = 0; i < n; i++) {
        let this_combination = [arr[0]];
        for (let j = 1; j < len; j++) {
           if (i & Math.pow(2, j - 1)) {
               // If the jth bit is on, no space. Append to the last element.
               const last_index = this_combination.length - 1;
               this_combination[last_index] = +(this_combination[last_index] + '' + arr[j]);
           } else {
               // Otherwise create a new list item.
               this_combination.push(arr[j]);
           }
        }
        // Consider making this function a generator and making this a yield.
        combinations.push(this_combination);
    }
    return combinations;
}

const result = combination([1, 2, 9, 4, 7]);
console.log(result.map(line => line.join(' ')).join('\n'));
.as-console-wrapper { max-height: 100% !important; top: 0; }

如果您想要单独的每个项目,对于数组中的每个项目,将其与其他项目组合,然后只与下一个项目组合,然后是接下来的 2 个项目,等等,直到结束:

const combination = arr => {
    const len = arr.length;
    const combinations = [];
    for (let i = 0; i < len; i++) {
        let item = arr[i];
        combinations.push(item);
        for (let j = i + 1; j < len; j++) {
            item = +(item + '' + arr[j]);
            combinations.push(item);
        }
    }
    return combinations;
}

const result = combination([1, 2, 9, 4, 7]);
console.log(result.join('\n'));
.as-console-wrapper { max-height: 100% !important; top: 0; }

【讨论】:

    【解决方案3】:

    您可以采用递归方法,通过迭代数组并插入或不插入空格,并使用递增索引分叉调用相同的函数。

    function combine(array) {
        function fork(i, p) {
            if (i === array.length) {
                result.push(p);
                return;
            }
            fork(i + 1, p + ' ' + array[i]);
            fork(i + 1, p + array[i]);
        }
        var result = [];
        fork(1, array[0].toString());
        return result;
    }
    
    console.log(combine([1, 2, 9, 4, 7]));
    .as-console-wrapper { max-height: 100% !important; top: 0; }

    【讨论】:

      【解决方案4】:

      您可以通过使用下面使用 3 指针的代码来做到这一点,

      1. 第一个指针将第 0 个位置打印到光标位置。
      2. 第二个指针打印光标到每次迭代中的差异位置。
      3. 第三个指针将光标位置打印到最后一个位置。

      let arr = [1, 2, 9, 4, 7];
      console.log(arr.join(','));
      for(let diff=2;diff<=arr.length;diff++){
        for(i=0,j=diff;arr.length>=i+diff;j++,i++){
          var temp = [];
          if(i>0)
            temp.push(arr.slice(0,i).join(','));
          temp.push(arr.slice(i,j).join(''));
          if(j<arr.length)
            temp.push(arr.slice(j,arr.length).join(','));
          console.log(temp.join(','));
        }
      }

      【讨论】:

        猜你喜欢
        • 2012-04-26
        • 1970-01-01
        • 1970-01-01
        • 2021-01-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多