查找给定输入中各种字符的独特组合

时间:2018-07-13 07:13:21

标签: javascript recursion

我面临一个问题,对于给定数量的合同,我们需要针对所有可能的独特定价方案进行投标。例如,说有2个合同A和B。我们必须对三种情况下仅获胜A,仅获B或A&B的价格进行定价。

如果有三个合同A,B和C,根据我的计算,这将导致7种可能的定价方案组合; A,B,C,AB,AC,BC,ABC。

我使用Javascript计算了公式,如下所示:

function contractCount(numContracts) {
    return (2 ** numContracts) - 1;
}

我是通过反复试验并在电子表格中进行操作来做到这一点的。这似乎是一个递归问题,但是我不知道如何递归地实现该解决方案?另外,我希望有人可以用简单的方式解释为什么我的上述解决方案有效。我可以理解,答案始终是合同数量加上(合同数量-1)中可能出现的情况,上述解决方案似乎与我对问题的头脑理解完全没有关系。

5 个答案:

答案 0 :(得分:1)

这只是出于一些考虑

  

'A'产生

A
     

使用'A', 'B',您将获得

A 
AB         B is the value of the next call and this is added to A
B          and used as single result
     

使用'A', 'B', 'C',您将获得

A 
AB
ABC
AC
B
BC
C

function f(v, ...a) {
    var temp = a.length ? f(...a) : [];
    return a.length
        ? [[v], ...temp.map(a => [v, ...a]), ...temp]
        : [[v]];
}

console.log(f('A').map(a => a.join('')));
console.log(f('A', 'B').map(a => a.join('')));
console.log(f('A', 'B', 'C').map(a => a.join('')));
console.log(f('A', 'B', 'C', 'D').map(a => a.join('')));
.as-console-wrapper { max-height: 100% !important; top: 0; }

答案 1 :(得分:0)

假设1 =获胜而0-未获胜

考虑A和B合约

那么所有可能的组合都是

A | B

0 | 0

0 | 1

1 | 0

1 | 1

但是您要消除0-0组合,因为至少应该有一个获胜。因此,不可能的组合是3 = 2 ^ 2-1-

对于3个合约A,B,C

A | B | C

0 | 0 | 0

0 | 0 | 1

0 | 1 | 0

0 | 1 | 1

1 | 0 | 0

1 | 0 | 1

1 | 1 | 0

1 | 1 | 1

再次,我们必须消除0-0-0组合。因此,不可能的组合是7 = 2 ^ 3-1

同样,在您的用例中,可能的组合数= 2 ^ n-1

答案 2 :(得分:0)

简而言之, 2 ** 3表示2x2x2等于8,并且在组合中必须有两个操作数,因此开始操作该操作数时不能与他自己组合,因此我们必须减去一个数字,这就是为什么要使用的原因。

(2 ** 3)-1

答案 3 :(得分:0)

我不确定您是要查找数量还是实际组合。计数只是一个数学问题,但是返回组合需要更多的工作。递归查找组合是backtracking algorithms

的经典应用程序

function groupByCount(arr, result = []) {
  const backtrack = (n, k = 0, a = []) => {
    if (k == n) return result.push(a.map((c, i) => c ? arr[i] : undefined).filter(i => i != undefined))
    k++
    for (let i = 0; i < 2; i++) {
      a[k - 1] = i
      backtrack(n, k, a)
    }
  }
  backtrack(arr.length)
  return result
}
let comb = groupByCount(['A', 'B', 'C'])
console.log(comb.join(' | '))

console.log(groupByCount(['A', 'B']).join(' | '))

这将构建一个布尔区域,表示每个索引是否在解决方案中。每次阵列填满时,都会推送解决方案。在它点击之前,我需要花些时间盯着它-至少对我来说是如此。注意:从技术上讲,空集是解决方案的一部分,因此它也将返回它。

如果您只是在考虑组合的数量,这是一种考虑的好方法。您可以考虑一下,如果您的每个合同都是二进制数字。因此,在您的A,B,C示例中,只有A可能表示为001,只有B 010和C 100。当您组合它们时,您只需组合位,以便AB为011,AC,101,ABC 111。等等,您会注意到,所有组合均以二进制数字表示,位数与您拥有的项数相同,并且每种组合的计数范围为001111。换句话说,组合的数量将始终与二进制数字相同,其中的许多数字用1填充。

使用3:111 = 7 4:1111 = 15

您可以使用parseInt('1'.repeat(n), 2)在JavaScript中进行检查,其中n是合同编号。

for (let n = 1; n < 8; n++){
  let b = '1'.repeat(n)
  console.log(n + ': ' + b + ' = ' + parseInt(b, 2) + ' = ' + (2**n -1))
  }

当然,这并不是一种非常有效的方法,因为它只是表示组合数是n -1的幂2的另一种方式。

答案 4 :(得分:0)

发电机非常适合这种计算

const None =
  Symbol ()

const combinations = (...xs) =>
{ const loop = function* (comb, [x = None, ...xs])
  { if (x === None)
      return yield comb
    
    yield* loop ([...comb, x], xs)
    yield* loop (comb, xs)
  }
  return Array.from (loop ([], xs))
}

console.log (combinations ('a'))
// [ [a], [] ]

console.log (combinations ('a', 'b'))
// [ [a,b], [a], [b], [] ]

console.log (combinations ('a', 'b', 'c'))
// [ [a,b,c], [a,b], [a,c], [a], [b,c], [b], [c], [] ]