如何计算用于括号化布尔表达式字符串以计算所需结果的方法的数量

时间:2017-11-17 00:41:08

标签: javascript algorithm recursion dynamic-programming boolean-expression

直接离开CTCI,8.14:给定一个由符号0(假),1(真)和&组成的布尔表达式。 (AND),| (OR)和^(XOR),以及所需的布尔结果值结果,实现一个函数来计算括号表达式的方法数,使其计算结果。

我尝试使用蛮力方法来计算每个可能的组合,如果匹配所需的结果,则将其添加到数组(组合)并返回该结果长度。它似乎适用于大多数表达式,但不是给出的第二个例子。我似乎缺少什么?

function countEval(s, goalBool, combos = []) {
    // on first call make s into array since theyre easier to work with
    if (!(s instanceof Array)) {
        // and turn 1s and 0s into their bool equivalent
        s = s.split('').map((item) => {
            if (item === '1') {
                return true;
            } else if (item === '0'){
                return false;
            } else {
                return item;
            }
        });
    }
    if (s.length === 1 && s[0] === goalBool) {
        combos.push(s[0]); // can be anything really
    } else {
        for (let i = 0; i < s.length - 2; i = i + 2) {
            // splice out the next 3 items
            const args = s.splice(i, 3);
            // pass them to see what they evaluate too
            const result = evalHelper(args[0], args[1], args[2]);
            // splice that result back in s array
            s.splice(i, 0, result);
            // pass that array to recurse
            countEval(s, goalBool, combos);
            // remove said item that was just put in
            s.splice(i, 1);
            // and reset array for next iteration
            s.splice(i, 0, ...args);
        }
    }
    return combos.length;
}

function evalHelper(a, op, b) {
    if (op === '|') {
        return a || b;
    } else if (op === '&') {
        return a && b;
    } else if (op === '^') {
        return a !== b;
    }
}

给出2个例子,它适用于第一个,但不适用于第二个......

console.log(countEval('1^0|0|1', false)); // 2, correct
console.log(countEval('0&0&0&1^1|0', true)); // 30, should be 10!?!?!

1 个答案:

答案 0 :(得分:3)

Bug

您的计划没有考虑重叠。

实施例

s = '1|1|1|1'时考虑您的计划。

在深度优先搜索迭代之一中,您的算法将进行缩减s = (1|1)|1|1。然后在同一搜索中的更深层的递归级别,您的算法将进行缩减s = (1|1)|(1|1)。现在s已完全缩小,因此您可以增加组合的长度。

在不同的深度优先搜索迭代中,您的算法将首先进行缩减s = 1|1|(1|1)。然后在同一搜索中的更深层的递归级别,您的算法将进行缩减s = (1|1)|(1|1)。现在s已完全缩小,因此您可以增加组合的长度。

请注意,对于这两种情况,s都以相同的方式括起来,因此您的程序不会考虑重叠。

更好的解决方案

很多时候,当一个问题询问可以做多少事情时,这通常是一个很好的指标,动态编程可能是一个潜在的解决方案。这个问题的重现关系有点棘手。

我们只需要选择&#34;原则&#34;运算符,然后确定左侧和右侧可以评估为truefalse的方式的数量。然后,基于&#34;原则&#34;运算符和目标布尔值,我们可以推导出表达式可以计算到目标布尔值的方式的公式,因为我们选择的运算符是&#34;原则&#34;操作

代码

function ways(expr, res, i, j, cache, spaces) {
  if (i == j) {
    return parseInt(expr[i]) == res ? 1 : 0;
  } else if (!([i, j, res] in cache)) {
    var ans = 0;
    for (var k = i + 1; k < j; k += 2) {
      var op = expr[k];
      var leftTrue = ways(expr, 1, i, k - 1, cache);
      var leftFalse = ways(expr, 0, i, k - 1, cache);
      var rightTrue = ways(expr, 1, k + 1, j, cache);
      var rightFalse = ways(expr, 0, k + 1, j, cache);
      if (op == '|') {
        if (res) {
          ans += leftTrue * rightTrue + leftTrue * rightFalse + leftFalse * rightTrue;
        } else {
          ans += leftFalse * rightFalse;
        }
      } else if (op == '^') {
        if (res) {
          ans += leftTrue * rightFalse + leftFalse * rightTrue;
        } else {
          ans += leftTrue * rightTrue + leftFalse * rightFalse;
        }
      } else if (op == '&') {
        if (res) {
          ans += leftTrue * rightTrue;
        } else {
          ans += leftFalse * rightFalse + leftTrue * rightFalse + leftFalse * rightTrue;
        }
      }
    }
    cache[[i, j, res]] = ans;
  }
  return cache[[i, j, res]];
}

function countEval(expr, res) {
  return ways(expr, res ? 1 : 0, 0, expr.length - 1, {});
}