Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 给定一个布尔表达式,决定在何处放置括号,使其变为真_C_Algorithm_Boolean - Fatal编程技术网

C 给定一个布尔表达式,决定在何处放置括号,使其变为真

C 给定一个布尔表达式,决定在何处放置括号,使其变为真,c,algorithm,boolean,C,Algorithm,Boolean,我正在考虑创建一个程序,在给定一个布尔表达式的情况下,例如,true和false xor true会在“操作”周围加上括号,并返回结果为true的新方程。我该怎么做,从哪里开始?动态规划中是否有一个小算法我应该搜索?这里的一个观察结果是,如果您最终得到一个全括号表达式,那么整个表达式中将有一些顶级运算符。基于该运算符,如果多个不同情况中的一种情况成立,则整个表达式的计算结果将为true。例如,如果顶级运算符是AND,则如果AND左侧和右侧的子表达式为true,则表达式为true。如果顶级操作是O

我正在考虑创建一个程序,在给定一个布尔表达式的情况下,例如,true和false xor true会在“操作”周围加上括号,并返回结果为true的新方程。我该怎么做,从哪里开始?动态规划中是否有一个小算法我应该搜索?

这里的一个观察结果是,如果您最终得到一个全括号表达式,那么整个表达式中将有一些顶级运算符。基于该运算符,如果多个不同情况中的一种情况成立,则整个表达式的计算结果将为true。例如,如果顶级运算符是AND,则如果AND左侧和右侧的子表达式为true,则表达式为true。如果顶级操作是OR,则如果左或右子表达式的计算结果为true,则整个表达式的计算结果为true。如果顶级运算符为NOT,则如果NOT的子表达式为false,则整个表达式为true

您可以想象一个自上而下的递归算法,其工作原理如下:对于每个操作符,暂时假定它是顶级操作符,并递归地确定所讨论的子表达式是否可以具有适当的真值。如果是这样的话,这个公式就可以成立了。如果没有,它就不能

这里可能的子表达式/真值对的数量是O(n2),因为每个子表达式只是原始表达式的子数组,并且只有O(n2)。因此,将有许多重叠的子问题,因此您可以使用自顶向下的记忆或自下而上的wou动态规划计算结果

<>这似乎是个很有趣的问题,所以我实际上用C++来编码(不是,C,很不幸,因为这看起来很难)。
#包括
#包括
#包括
#包括
#包括
#包括
使用名称空间std;
/**
*枚举类型:符号
*
*表示可以出现在布尔表达式中的符号的类型。
*/
枚举符号{
是的,
假,,
以及,
或
不
暗示,
IFF,
异或
};
/**
*别名:子问题
*
*一个类型的名称,表示已记忆的文件中的一个子问题
*解决方案。每个子问题对应于一个子阵列(由一个起始点给出)
*和结束位置)和预期真值(真或假)
*/
使用子问题=元组;
名称空间标准{
模板结构哈希{
布尔运算符()(常量子问题&s)常量{
返回hash()(get(s))+31*hash()(get(s))+127*hash()(get(s));
}
};
}
/*内部执行*/
名称空间{
/**
*函数:canYield(常数向量和公式,
*大小开始,大小结束,
*无序映射和子问题,
*布尔(预期);
* --------------------------------------------------------------------------
*确定子表达式是否在公式的范围[开始,结束]中
*由公式给出的可对结果进行评估。记录其结果
*避免无关的计算。
*/
布尔产量(常数向量和公式,
大小开始,大小结束,
无序映射和子问题,
布尔(预计){
/*边缘案例检查:我们不应该有一个空范围*/
断言(结束>开始);
/*首先看看我们是否已经计算过了。如果已经计算过了,我们就完成了!
*如果没有,我们必须做一些工作。当这个If语句完成时,
*soln应该是有效的。
*/
const Subproblem thisSubproblem=make_tuple(开始、结束、预期);
if(!subproblem.count(thisSubproblem)){
/*基本情况:如果这是单个元素范围,则必须为
*无论是真是假,我们都应该能够读出答案
*直接的。
*/
如果(开始+1==结束){
/*这必须为真或假;否则公式格式不正确*/
断言(公式[start]==TRUE | |公式[start]==FALSE);
/*查看枚举常量是否与预期值匹配*/
bool isTrue=(公式[start]==TRUE);
子问题[thisSubproblem]=(isTrue==预期值);
}
/*否则,我们的射程会更长*/
否则{
/*如果此范围以NOT开头,一个选项是用括号括起来
*表达式的其余部分,试图得到与
*预料之中。
*/
如果(公式[开始]==不&&
canYield(公式、开始+1、结束、子问题、!预期)){
子问题[此子问题]=真;
}否则{
/*在该范围内迭代,尝试将所有二进制运算符置于
*如果可能的话,最高级别。如果其中任何一个有效,我们就完蛋了!
*/
对于(大小\u t i=开始;i<结束;i++){
/*为了处理和或,我们将使用de Morgan定律
*使我们能够将一系列案例结合在一起。
*/
if(公式[i]==和| |公式[i]==或){
/*这是观察家。如果我们想做出一个正确的判断
*或者一个或多个,我们需要让两个子公式匹配
*期望的真值。如果我们试图做出错误的判断
*或者一个或真,我们需要得到一个子公式来匹配
*期望真值。所以我们需要看到
*和/或的组合,以及我们是否希望为真/假。
*/
布尔奇偶性=((公式[i]==和)==预期值);
/*如果平价匹配,我们要么寻找AND/true
*或一个或/假。
*/
if(奇偶校验)&&
canYield(公式、开始、i、子问题、预期)&&
canYield(公式,i+1,结束,子问题,预期)){
子问题[此子问题]=真;
打破
}
/*如果双方不同意,我们要么寻找AND/false
*对。
*/
否则如果(!奇偶校验)&&
(canYield(公式、开始、i、子问题、预期)||
#include <string>
#include <vector>
#include <unordered_map>
#include <utility>
#include <cassert>
#include <iostream>
using namespace std;

/**
 * Enumerated type: Symbol
 *
 * A type representing a symbol that can appear in a boolean expression.
 */
enum Symbol {
  TRUE,
  FALSE,
  AND,
  OR,
  NOT,
  IMPLIES,
  IFF,
  XOR
};

/**
 * Alias: Subproblem
 *
 * A name for a type representing one of the subproblems in the memoized
 * solution. Each subproblem corresponds to a subarray (given by a start
 * and end position) and an expected truth value (either true or false.)
 */
using Subproblem = tuple<size_t, size_t, bool>;
namespace std {
  template <> struct hash<Subproblem> {
    bool operator()(const Subproblem& s) const {
      return hash<size_t>()(get<0>(s)) + 31 * hash<size_t>()(get<1>(s)) + 127 * hash<bool>()(get<2>(s));
    }
  };
}

/* Internal implementation. */
namespace {
  /**
   * Function: canYield(const vector<Symbol>& formula,
   *                    size_t start, size_t end,
   *                    unordered_map<Subproblem, bool>& subproblems,
   *                    bool expected);
   * --------------------------------------------------------------------------
   * Determines whether the subexpression in range [start, end) of the formula
   * given by formula can be made to evaluate to result. Memoizes its results
   * to avoid extraneous computations.
   */
  bool canYield(const vector<Symbol>& formula,
        size_t start, size_t end,
        unordered_map<Subproblem, bool>& subproblems,
        bool expected) {
    /* Edge case check: we shouldn't have an empty range. */
    assert (end > start);

    /* Begin by seeing if we've already computed this. If so, we're done!
     * If not, we have to do some work. By the time this if statement finishes,
     * soln should be valid.
     */
    const Subproblem thisSubproblem = make_tuple(start, end, expected);
    if (!subproblems.count(thisSubproblem)) {
      /* Base case: If this is a single-element range, it must be either
       * to TRUE or FALSE, and we should be able to read off the answer
       * directly.
       */
      if (start + 1 == end) {
    /* This has to be TRUE or FALSE; otherwise the formula is malformed. */
    assert (formula[start] == TRUE || formula[start] == FALSE);

    /* See whether the enumerated constant matches the expected value. */
    bool isTrue = (formula[start] == TRUE);
    subproblems[thisSubproblem] = (isTrue == expected);
      }
      /* Otherwise, we have a longer range. */
      else {
    /* If this range begins with a NOT, one option is to parenthesize
     * the rest of the expression to try to get the opposite of what's
     * expected.
     */
    if (formula[start] == NOT &&
        canYield(formula, start + 1, end, subproblems, !expected)) {
      subproblems[thisSubproblem] = true;
    } else {
      /* Iterate over the range, trying to put all binary operators at
       * the top level if possible. If any of them work, we're done!
       */
      for (size_t i = start; i < end; i++) {
        /* To handle AND and OR, we'll use the fact that de Morgan's laws
         * allows us to unite a bunch of cases together.
         */
        if (formula[i] == AND || formula[i] == OR) {
          /* Here's the observatino. If we're trying to make an AND true
           * or an OR false, we need to get both subformulas to match
           * the expected truth value. If we're trying to make an AND false
           * or an OR true, we need to get one subformula to match the
           * expected truth value. So we need to see the parity of the
           * combination of AND/OR and whether we want true/false.
           */
          bool parity = ((formula[i] == AND) == expected);

          /* If the parities match, we're either looking for an AND/true
           * or an OR/false.
           */
          if (parity &&
          canYield(formula, start, i, subproblems, expected) &&
          canYield(formula, i+1, end, subproblems, expected)) {
        subproblems[thisSubproblem] = true;
        break;
          }
          /* If the parities disagree, we're either looking for AND/false
           * or OR/true.
           */
          else if (!parity &&
               (canYield(formula, start, i, subproblems, expected) ||
            canYield(formula, i+1, end, subproblems, expected))) {
        subproblems[thisSubproblem] = true;
        break;          
          }
        }
        /* If we see an XOR or IFF, we can similarly use a de Morgan's
         * trick.
         */
        else if (formula[i] == XOR || formula[i] == IFF) {
          /* If we're looking for an XOR/true or an IFF/false, then we
           * need exactly one of the two subproblems to be true. If we're
           * looking for an XOR/false or an IFF/true, then we need both
           * of the subproblems to be true or both to be false. Let's
           * start by determining which case we're in.
           */
          bool parity = ((formula[i] == XOR) == expected);

          /* Fire off the four subproblems we need to consider. */
          bool fTrue  = canYield(formula, start, i, subproblems, true);
          bool fFalse = canYield(formula, start, i, subproblems, false);
          bool sTrue  = canYield(formula, i+1, end, subproblems, true);
          bool sFalse  = canYield(formula, i+1, end, subproblems, false);

          /* If we have true parity, we're looking for XOR/true or IFF/
           * false, so we want the first and second halves to disagree.
           */
          if (parity && ((fTrue && sFalse) || (fFalse && sTrue))) {
        subproblems[thisSubproblem] = true;
        break;
          }
          /* Otherwise, we're looking for equal parity, so we need the
           * first and second halves to agree.
           */
          else if (!parity && ((fTrue && sTrue) || (fFalse && sFalse))) {
        subproblems[thisSubproblem] = true;
        break;
          }
        }
        /* Implications are their own can of worms since they're so 
         * different than everything else.
         */
        else if (formula[i] == IMPLIES) {
          /* For 'true,' we check if the antecedent can be made false
           * or the consequent made true.
           */
          if (expected &&
          (canYield(formula, start, i, subproblems, false) ||
           canYield(formula, i+1, end, subproblems, true))) {
        subproblems[thisSubproblem] = true;
        break;
          }
          /* For 'false,' we see if the antecedent is true and the
           * consequent is false.
           */
          if (!expected &&
          canYield(formula, start, i, subproblems, true) &&
          canYield(formula, i+1, end, subproblems, false)) {
        subproblems[thisSubproblem] = true;
        break;
          }
        }
      }
    }
      }
    }
    /* Return the solution we found. Notice that as a cute trick, if we didn't
     * record a true value in the course of solving the problem, then this
     * read will default to false - which is what we wanted!
     */
    return subproblems[thisSubproblem];
  }
}

/**
 * Function: canParenthesizeToYieldTrue(const vector<Symbol>& formula);
 * Usage: if (canParentheiszeToYieldTrue({FALSE, AND, NOT, FALSE})) // false
 *        if (canParenthesizeToYieldTrue({NOT, FALSE, AND, FALSE})) // true
 * 
 * ----------------------------------------------------------------------------
 * Given a boolean expression, returns whether it's possible to add parentheses
 * into the expression in a way that causes that expression to evaluate to
 * true. It is assumed that this formula is well-formed and nonempty.
 */
bool canParenthesizeToYieldTrue(const vector<Symbol>& formula) {
  /* Table for memoizing whether each subproblem is solvable or not. */
  unordered_map<Subproblem, bool> subproblems;
  return canYield(formula, 0, formula.size(), subproblems, true);
}

/* Prints out an expression in a nice form. */
void printExpr(const vector<Symbol>& problem) {
  for (Symbol s: problem) {
    switch (s) {
    case TRUE:    cout << "true";     break;
    case FALSE:   cout << "false";    break;
    case AND:     cout << " /\\ ";    break;
    case OR:      cout << " \\/ ";    break;
    case NOT:     cout << "!";        break;
    case XOR:     cout << " + ";      break;
    case IFF:     cout << " <-> ";    break;
    case IMPLIES: cout << " -> ";     break;
    default: assert(false);
    }
  }
}

/* Runs a test case to see if the program behaves as expected. */
void check(const vector<Symbol>& problem, bool expected) {
  bool result = (canParenthesizeToYieldTrue(problem) == expected);
  if (result) {
    cout << "  pass: ";
    printExpr(problem);
    cout << endl;
  }
  else {
    cout << "! FAIL: ";
    printExpr(problem);

    string throwaway;
    getline(cin, throwaway);
  }
}

int main() {
  /* Basic logic checks. */
  check({TRUE}, true);
  check({FALSE}, false);
  check({NOT, TRUE}, false);
  check({NOT, FALSE}, true);
  check({TRUE, AND, TRUE}, true);
  check({TRUE, AND, FALSE}, false);
  check({FALSE, AND, TRUE}, false);
  check({FALSE, AND, FALSE}, false);
  check({TRUE, OR, TRUE}, true);
  check({TRUE, OR, FALSE}, true);
  check({FALSE, OR, TRUE}, true);
  check({FALSE, OR, FALSE}, false);
  check({TRUE, XOR, TRUE}, false);
  check({TRUE, XOR, FALSE}, true);
  check({FALSE, XOR, TRUE}, true);
  check({FALSE, XOR, FALSE}, false);
  check({TRUE, IFF, TRUE}, true);
  check({TRUE, IFF, FALSE}, false);
  check({FALSE, IFF, TRUE}, false);
  check({FALSE, IFF, FALSE}, true);
  check({TRUE, IMPLIES, TRUE}, true);
  check({TRUE, IMPLIES, FALSE}, false);
  check({FALSE, IMPLIES, TRUE}, true);
  check({FALSE, IMPLIES, FALSE}, true);

  /* Harder ones! */
  check({TRUE, AND, NOT, TRUE}, false);
  check({NOT, TRUE, AND, TRUE}, false);
  check({FALSE, AND, NOT, FALSE}, false);
  check({NOT, FALSE, AND, FALSE}, true);

  check({TRUE, OR, NOT, TRUE}, true);
  check({NOT, TRUE, OR, TRUE}, true);
  check({FALSE, OR, NOT, TRUE}, false);
  check({NOT, TRUE, OR, FALSE}, false);

  check({NOT, FALSE, IMPLIES, TRUE}, true);
  check({NOT, FALSE, IMPLIES, FALSE}, false);

  check({NOT, FALSE, XOR, TRUE}, false);
  check({NOT, FALSE, IFF, FALSE}, false);

  /* Ridiculous */
  check({NOT, NOT, FALSE, OR, FALSE, OR, FALSE}, false);
}