查找可能的布尔值以将表达式计算为true

时间:2017-03-17 01:45:21

标签: logic expression logical-operators boolean-logic boolean-expression

如果我有一个带有许多布尔变量的给定布尔表达式(ANDOR操作),那么我想将此表达式计算为true,如何找到所有可能的布尔值的集合实现真正的表现?

例如,我有4个布尔变量abcd和一个表达式:

 (a ^ b) v (c ^ d)

我尝试做的最慢的方法是:

  • 我构建了一个表达式树来获取表达式中的所有变量,我得到了{a,b,c,d}集。
  • 我找到了集合的所有子集:{a}, {b}, {c}, {d}, {a,b}, {a,c}, {a,d}, {b,c}, {b,d}, {c,d}, {a,b,c}, {a,b,d}, {a,c,d}, {b,c,d}, {a,b,c,d}
  • 对于每个子集,我将每个变量设置为true,然后计算表达式。如果表达式返回true,我将使用值保存子集。

编辑:我删除NOT运算符以简化问题。

1 个答案:

答案 0 :(得分:0)

我认为我看到了一种计算方法而无需尝试所有排列,我的高级轮廓(如下所述)并不是非常复杂。我将概述基本方法,您将自己完成两项后续任务:

  • 解析逻辑表达式,例如" A&& (B || C)"进入一个经典的解析树,它代表表达式,树中的每个节点都是变量或布尔运算,"&&"," ||&#34 ;或者"!" (不),有两个孩子是它的操作数。这是一个经典的解析树。有关如何执行此操作的大量示例,请参阅Google。

  • 将我的大纲翻译成实际的C ++代码。这也取决于你,但我认为,一旦你将整个方法包围起来,实施应该是相当明显的。

要解决这个问题,我将采用两阶段方法。

  1. 我将使用proof by induction的一般方法,以便得出所有变量的所有潜在值集的暂定列表,其中布尔表达式将评估为true

  2. 在第二阶段,我将从所有潜在集合的列表中消除那些在逻辑上不可能的集合。这可能听起来令人困惑,所以我先解释第二阶段。

  3. 让我们使用以下数据类型。首先,我将使用此布尔表达式将评估为truefalse的可能值的数据类型:

    typedef std::set<std::pair<std::string, bool>> values_t;
    

    此处,std::pair<std::string, bool>表示变量,其名称为std::string,具有此bool值。例如:

    {"a", true}
    

    表示变量的值&#34; a&#34;的值为true。因此,std::set表示一组变量及其对应的值。

    所有这些潜在的解决方案将成为:

    typedef std::list<values_t> all_values_t;
    

    这就是我们如何表示所有变量值的所有组合的列表,它们产生truefalse的结果。您可以使用std::vector代替std::list,但这并不重要。

    现在请注意,values_t可能同时包含:

     {"a", true}
    

     {"a", false}
    

    在集合中。这意味着,为了使表达式评估为truefalse,&#34; a&#34;必须同时是真是假。

    但显然,这在逻辑上是不可能的。因此,在此解决方案的第2阶段,您需要简单地浏览values_t中的所有个人all_values_t,并删除&#34;不可能的&#34; values_t包含truefalse的某个变量。这样做的方式应该看起来相当明显,我不会浪费时间来描述它,但是第一阶段完成后,第二阶段应该是直截了当的。

    对于第1阶段,我们的目标是提出一个大致如此声明的功能:

    all_values_t phase1(expression_t expr, bool goal);
    

    expr是你的布尔表达式的解析表示,作为一个解析树(正如我在开头所提到的,做这部分将取决于你)。 goal是您希望如何评估已解析的表达式:phase1()all_values_t评估为expr或{{1}的所有可能true返回},如#34;目标&#34;所示。显然,您会false致电phase1()以及#34;目标&#34;为你的答案,因为这是你想要弄清楚的。但true会以递归方式调用自己,使用phase1()true&#34;目标&#34;来实现其魔力。

    在继续之前,现在阅读并理解描述感应证明如何运作的各种资源非常重要。在你完全理解这个一般概念之前,不要再继续了。

    好的,现在你明白了这个概念。如果你这样做,那么你现在必须同意我false已经完成。有用!归纳证明首先假设phase1()完成它应该做的事情。 phase1()将对自身进行递归调用,并且由于phase1()返回正确的结果,phase1()可以简单地依靠自己来解决所有问题。看看这有多容易?

    phase1()真的有一个&#34;简单&#34;手头的任务:

    • 检查解析树的顶级节点是什么。它将是变量节点或表达式节点(见上文)。

    • 根据该内容返回相应的phase1()

    那就是它。我们将同时采取两种可能性。

    1. 顶级节点是变量。
    2. 因此,如果您的表达式只是一个变量,并且您希望表达式返回all_values_t,那么:

      goal

      表达式只有一种可能的方式来评估values_t v{ {name, goal} }; :显而易见的是:变量,以及goal的值。

      并且只有一种可能的解决方案。没有其他选择:

      goal

      现在,另一种可能性是表达式中的顶级节点是布尔运算之一:和,或者不是。

      再次,我们将分裂并征服这一点,并逐一解决每一个问题。

      让我们说它不是&#34;而不是&#34;。那我们该怎么办?这应该很简单:

      all_values_t res;
      
      res.push_back(v);
      
      return res;
      

      只需递归调用return phase1(child1, !goal); ,传递&#34;不是&#34;表达式的子节点,phase1()逻辑反转。因此,如果您的goal成立,请使用goal返回&#34; not&#34;的值。子表达式为false,反之亦然。请记住,归纳证明假设phase1()与广告一样有效,因此您可以依靠它来获得子表达式的正确答案。

      现在应该开始明白phase1()的其余部分是如何工作的。只剩下两种可能性:&#34;和&#34;和#34;或&#34;逻辑运算。

      对于&#34;和&#34;在操作中,我们将分别考虑&#34;目标&#34; &#34;和&#34;操作应为phase1()true

      如果false为真,则必须使用goalphase1()提出两个子表达式为真:

      all_values_t

      然后将两个结果合并在一起。现在,请回想一下all_values_t left_result=phase1(child1, true); all_values_t right_result=phase1(child2, true); 是所有 可能的 值的列表。 all_values_t中的每个值(可以是空列表/向量)表示一种可能的解决方案。左侧和右侧子表达式都必须在逻辑上组合,但all_values_t中的任何可能解决方案都可以与任何left_result一起使用。左子表达式为true的任何潜在解决方案都可以(并且必须)与任何可能的解决方案一起使用,并且右子表达式为真。

      因此,在right_resultall_values_t之间执行cartesian product,即可获得需要返回的left_result。即:获取第一个值,right_result中的第一个values_t std::set,然后将第一个left_result right_result添加到此设置,然后是第一个std::set {1}}使用第二个left_result,依此类推;然后第二个right_result包含第一个left_result,然后是第二个right_result,依此类推。这些组合中的每一个都会right_result加入push_back(),该all_values_t会从此次调用返回phase1()。

      但是你的goal是拥有&#34;和&#34;表达式返回false,相反,您只需要对此进行三次变换。第一次使用phase1(child1, false)致电phase1(child2, false);然后phase1(child1, true)phase1(child2, false);最后是phase1(child1, false)phase1(child2, true)child1child2或两者都必须评估为false

      因此,照顾&#34;和&#34;操作

      最后,phase1()处理的最终可能性是逻辑或操作。你现在应该能够自己弄清楚如何做到这一点,但我只是简单地总结一下:

      如果goal为false,则必须使用phase1(child1, false)拨打phase1(child2, false),然后将两个结果合并为一个笛卡尔积。如果goal为真,您将为其他三种可能性进行三组递归调用,并将所有内容组合在一起。

      你已经完成了。 phase1()没有别的办法,我们通过归纳完成了我们的证明。

      嗯,我撒谎了一下。您还需要做一个小型的第3阶段&#34;。回想一下&#34;阶段2&#34;我们消除了所有不可能的解好吧,由于所有这一切,最终的可能解决方案列表可能会在values_t中出现多次all_values_t,所以您只需拥有重复数据删除。

      P.S。作为第1阶段的一部分,它也可以通过动态执行来避免离散阶段2.这种变化也将是你的家庭作业。