【问题标题】:How do I determine if *exactly* one boolean is true, without type conversion?如何确定 *exactly* 一个布尔值是否为真,无需类型转换?
【发布时间】:2013-01-31 01:01:35
【问题描述】:

给定一个任意的布尔值列表,确定完全其中一个为真的最优雅的方法是什么?

最明显的 hack 是类型转换:将它们转换为 0 用于 false1 用于 true,然后将它们相加,然后返回 sum == 1

我想知道是否有办法做到这一点将它们转换为整数,实际上使用布尔逻辑

(这似乎应该是微不足道的,idk,漫长的一周)

编辑:如果不是很明显,这更像是一个代码高尔夫/理论问题。我对在 PROD 代码中使用类型转换/int 添加并不大惊小怪,我只是感兴趣是否有办法做到这一点。

Edit2:抱歉,这是漫长的一周,我没有很好地解释自己。让我试试这个:

在布尔逻辑中,如果所有布尔值都为真,则对一组布尔值进行与运算为真,如果其中至少一个为真,则对集合进行或运算为真。如果恰好一个布尔值为真,是否有一个逻辑结构为真?例如,XOR 是用于两个布尔值的集合,但除此之外,它就会失败。

【问题讨论】:

  • 转换是最优雅的方法。到目前为止。
  • 我很感兴趣是否有其他方法。我已经用类型转换编写了代码。如果这是答案,则答案可以是“您不能使用布尔逻辑执行此操作”。
  • 为什么异或不适合你?如果一个是正确的,它评估为真。
  • 好的,我意识到使用 XOR,true and true and true 将评估为 true,根据您的要求,情况并非如此。
  • @Shiva - 当我想指出你刚刚意识到的关于 true ^ true ^ true 的内容时,我不小心点了你的第一个 xor 评论。无论如何,忽略upvote! =)

标签: boolean logic


【解决方案1】:

您实际上可以仅使用布尔逻辑来完成此操作,尽管在您的示例中可能没有实际价值。布尔版本比简单地计算真值的数量要复杂得多。

不管怎样,为了满足求知欲,就这样吧。首先,使用一系列 XOR 的想法很好,但它只让我们成功了一半。对于任意两个变量xy

xy

只要其中一个为真,则为真。但是,如果您添加第三个变量 z,

,这将不再适用

xyz

第一部分,xy,如果 xy 中的一个恰好是真的。如果 xy 为真,那么 z 需要为假才能使整个表达式为真,这就是我们想要的。但是考虑一下如果 xy 都为真会发生什么。那么 xy 为假,但如果 z 也为真,则整个表达式可以为真。因此,一个变量或三个变量都必须为真。一般来说,如果您有一条 XOR 链的语句,那么如果 奇数个 变量为真,则该语句为真。

由于 1 是一个奇数,这可能很有用。当然,检查奇数个真相是不够的。我们还需要确保不超过一个变量为真。这可以通过取所有成对的两个变量并检查它们是否都为真来以成对的方式完成。将这两个条件结合起来确保变量为真时恰好是一个条件。

下面是一个小的 Python 脚本来说明该方法。

from itertools import product

print("x|y|z|only_one_is_true")
print("======================")
for x, y, z in product([True, False], repeat=3):
    uneven_number_is_true = x ^ y ^ z
    max_one_is_true = (not (x and y)) and (not (x and z)) and (not (y and z))
    only_one_is_true = uneven_number_is_true and max_one_is_true
    print(int(x), int(y), int(z), only_one_is_true)

这是输出。

x|y|z|only_one_is_true ======================= 1 1 1 错误 1 1 0 错误 1 0 1 错误 1 0 0 真 0 1 1 错误 0 1 0 真 0 0 1 真 0 0 0 错误

【讨论】:

  • 这似乎不能很好地扩展 4,5,... 输入。看起来您需要 # inputs choose 2 操作数来计算 max_one_is_true
  • 通过不使用x ^ y ^ z 来测试奇数个真值,而只使用x or y or z 来确保至少有一个为真,可以使这个解决方案更容易理解。
  • @TWiStErRob,对的数量比例为 n(n-1)/2,所以是二次的。在我看来,这还不错。
【解决方案2】:

经过您的澄清,这里没有整数。

 bool IsExactlyOneBooleanTrue( bool *boolAry, int size )
    {
      bool areAnyTrue = false;
      bool areTwoTrue = false;
      for(int i = 0; (!areTwoTrue) && (i < size); i++) {
        areTwoTrue = (areAnyTrue && boolAry[i]);
        areAnyTrue |= boolAry[i];
      }
      return ((areAnyTrue) && (!areTwoTrue));
    }

【讨论】:

  • 实现break 关键字的有趣方式。你想避免分支吗?
  • @TWiStErRob,你的意思是因为没有休息吗?主要原因是可读性。这样,所有的退出条件在循环开始时都是显而易见的;它让读者知道哪些条件会导致循环退出(以及循环的目的。)
  • areTwoTrue 的大多数用法都是为了停止循环。我想这是我们习惯的/语言约定是什么(C++ v Java)。我认为my approach 也是可读的(忽略我们如何迭代数组,这是特定于语言的):它清楚地表明我们只关心数组中的true 值,我们将在第二个停止。我猜圈复杂度是相似的,只是使用ifs 比使用|== &amp;&amp; 更普遍。好奇你的想法。
  • 两者都可以,这只是一个偏好问题。我宁愿不必查看循环来查看它为什么大部分时间都退出,而宁愿从 while/for 语句中读取它。但当然,有时在循环中中断或返回以使代码更具可读性是有意义的。各有各的。 (不过你说得对,“我的”版本可能会导致更少的分支,但如果编译器很聪明,那么无论如何,你和我的可能只会产生相同的硬件代码。)
【解决方案3】:

使用简单的布尔逻辑,可能无法实现您想要的。因为您要求的是不仅基于真值而且还基于附加信息(在这种情况下为计数)的真值评估。但是布尔求值是二进制逻辑,它不能依赖于其他任何东西,只能依赖于操作数本身。并且没有办法通过逆向工程来找到给定真值的操作数,因为可能有四种可能的操作数组合,但只有两种结果。给定一个假的,你能分辨出你的情况是因为F ^ F还是T ^ T,以便根据它来确定下一个评估?。

【讨论】:

  • 不正确。 c.fogelklou 的答案确实可以解释为简单的布尔逻辑。从理论上讲,SCdF 要求一个带有许多参数的布尔函数,我们知道任何布尔函数都可以只用合取和取反来实现。
  • 总是可以通过循环找出多个布尔值是否为真。我确信 OP 已经知道这一点。但据我所知,当 OP 最初询问时,他想要一个优雅的答案,而不需要循环或直接使用布尔逻辑(如 XOR 或类似的东西),只有当一个元素为真时才直接返回真。
【解决方案4】:

当然,你可以这样做(伪代码,因为你没有提到语言):

found = false;
alreadyFound = false;
for (boolean in booleans):
    if (boolean):
        found = true;
        if (alreadyFound):
            found = false;
            break;
        else:
            alreadyFound = true;
return found;

【讨论】:

    【解决方案5】:

    没有人提到我们正在寻找的这个“操作”是类似于大多数语言中的布尔 AND 和 OR 的快捷方式。这是 Java 中的一个实现:

    public static boolean exactlyOneOf(boolean... inputs) {
        boolean foundAtLeastOne = false;
        for (boolean bool : inputs) {
            if (bool) {
                if (foundAtLeastOne) {
                    // found a second one that's also true, shortcut like && and ||
                    return false;
                }
                foundAtLeastOne = true;
            }
        }
        // we're happy if we found one, but if none found that's less than one
        return foundAtLeastOne;
    }
    

    【讨论】:

      【解决方案6】:

      booleanList.Where(y => y).Count() == 1;

      【讨论】:

      • booleanList.Where(y => y).Take(2).Count() == 1;
      【解决方案7】:

      它可以通过递归很好地完成,例如在 Haskell 中

      -- there isn't exactly one true element in the empty list
      oneTrue [] = False 
      -- if the list starts with False, discard it
      oneTrue (False : xs) = oneTrue xs
      -- if the list starts with True, all other elements must be False
      oneTrue (True : xs) = not (or xs)
      

      【讨论】:

        【解决方案8】:

        由于目前的读取量很大,这里有一个快速清理和附加信息。

        选项 1:

        询问是否只有第一个变量为真,或者只有第二个,...,或者只有第 n 个变量。

        x1 & !x2 & ... & !xn |
        !x1 & x2 & ... & !xn |
        ...
        !x1 & !x2 & ... & xn
        

        这种方法在 O(n^2) 内扩展,在找到第一个正匹配后停止评估。因此,如果可能存在正匹配,则首选。

        选项 2:

        询问总共是否有至少一个变量为真。另外检查每一对是否包含至多一个真正的变量(Anders Johannsen 的答案)

        (x1 | x2 | ... | xn) &
        (!x1 | !x2) &
        ...
        (!x1 | !xn) &
        (!x2 | !x3) &
        ...
        (!x2 | !xn) &
        ...
        

        由于可能对的数量,此选项也可以在 O(n^2) 范围内进行缩放。延迟评估在第一个反例之后停止公式。因此,如果它可能存在否定匹配,则它是首选。

        (选项 3):

        此选项涉及减法,因此限制设置的有效答案。然而,它认为循环值可能不是不受限制的设置中最有益的解决方案。

        将 x1 ... xn 视为二进制数 x。减一,然后与结果。输出为零 x1 ... xn 最多包含一个真值。 (旧的“检查二的幂”算法)

        x    00010000
        x-1  00001111
        AND  00000000
        

        如果这些位已经存储在这样的位板上,这可能比循环更有利。不过,请记住,这会降低可读性,并且会受到可用电路板长度的限制。

        最后一点提醒大家注意:目前存在一个名为计算机科学的堆栈交换,它正是针对此类算法问题而设计的

        【讨论】:

          【解决方案9】:

          一种方法是执行成对的AND,然后检查是否有任何成对的比较返回真与链接OR。在python中,我会使用

          来实现它
          from itertools import combinations
          
          def one_true(bools):
              pairwise_comp = [comb[0] and comb[1] for comb in combinations(bools, 2)]
              return not any(pairwise_comp)
          

          这种方法很容易推广到任意长度的列表,尽管对于非常长的列表,可能对的数量增长非常快。

          【讨论】:

            【解决方案10】:

            Python:

            boolean_list.count(True) == 1
            

            【讨论】:

              【解决方案11】:

              // Javascript 在数组上使用 .filter() 并检查新数组的长度。

              // Example using array
              isExactly1BooleanTrue(boolean:boolean[]) {
                return booleans.filter(value => value === true).length === 1;
              }
              
              // Example using ...booleans
              isExactly1BooleanTrue(...booleans) {
                return booleans.filter(value => value === true).length === 1;
              }
              

              【讨论】:

                【解决方案12】:

                好的,再试一次。调用不同的布尔值b[i],并调用它们的一部分(数组的范围)b[i .. j]。定义函数none(b[i .. j])just_one(b[i .. j])(如果需要,可以替换递归定义以获得显式公式)。我们有,使用 C 表示法进行逻辑运算(&amp;&amp; 是和,|| 是或,^ 是异或(不是真的在 C 中),! 不是):

                none(b[i .. i + 1]) ~~> !b[i] && !b[i + 1]
                just_one(b[i .. i + 1]) ~~> b[i] ^ b[i + 1]
                

                然后递归:

                none(b[i .. j + 1]) ~~> none(b[i .. j]) && !b[j + 1]
                just_one(b[i .. j + 1] ~~> (just_one(b[i .. j]) && !b[j + 1]) ^ (none(b[i .. j]) && b[j + 1])
                

                你对just_one(b[1 .. n])感兴趣。

                表情会变得很糟糕。

                玩得开心!

                【讨论】:

                • 您的意思是生成代码还是提供功能性答案?如果可以基于此编写 C 风格的代码宏,那就太棒了。
                【解决方案13】:

                那个 python 脚本很好地完成了这项工作。这是它使用的单线:

                ((x ∨ (y ∨ z)) ∧ (¬(x ∧ y) ∧ (¬(z ∧ x) ∧ ¬(y ∧ z))))

                【讨论】:

                  【解决方案14】:

                  Retracted for PrivacyAnders Johannsen 提供了已经正确且简单的答案。但是这两种解决方案都不能很好地扩展(O(n^2))。如果性能很重要,您可以坚持以下解决方案,它在 O(n) 中执行:

                  def exact_one_of(array_of_bool):
                      exact_one = more_than_one = False
                      for array_elem in array_of_bool:
                          more_than_one = (exact_one and array_elem) or more_than_one
                          exact_one = (exact_one ^ array_elem) and (not more_than_one)
                      return exact_one
                  

                  (为了简单起见,我使用了 python 和 for 循环。当然,这个循环可以展开为一系列 NOT、AND、OR 和 XOR 操作)

                  它通过跟踪每个布尔变量/列表条目的两个状态来工作:

                  1. 从列表的开头到该条目是否正好有一个“True”?
                  2. 从列表的开头到此条目是否有多个“True”?

                  列表条目的状态可以简单地从先前的状态和相应的列表条目/布尔变量中导出。

                  【讨论】:

                    【解决方案15】:

                    Python: 让我们看看使用示例... 步骤:

                    1. 下面的函数exactly_one_topping接受三个参数

                    2. 在列表中将它们的值存储为TrueFalse

                    3. 通过检查计数是否精确为1来检查是否只有一个真值。

                    def exactly_one_topping(ketchup, mustard, onion):
                        
                      args = [ketchup,mustard,onion]
                      if args.count(True) == 1:   # check if Exactly one value is True
                          return True
                      else:
                          return False
                    

                    【讨论】:

                      【解决方案16】:

                      你想如何计算有多少是真实的,你知道,计算?当然,你可以做一些乱七八糟的事情(C 语法,我的 Python 很糟糕):

                      for(i = 0; i < last && !booleans[i]; i++)
                           ;
                      if(i == last)
                           return 0;  /* No true one found */
                      /* We have a true one, check there isn't another */
                      for(i++; i < last && !booleans[i]; i++)
                           ;
                      if(i == last)
                           return 1; /* No more true ones */
                      else
                           return 0; /* Found another true */ 
                      

                      我相信你会同意胜利(如果有的话)是微不足道的,而且可读性很差。

                      【讨论】:

                        【解决方案17】:

                        没有循环是不可能的。检查 Java 实现中的 BitSet cardinality()。 http://fuseyism.com/classpath/doc/java/util/BitSet-source.html

                        【讨论】:

                          【解决方案18】:

                          我们可以这样做:-

                          if (A=true or B=true)and(not(A=true and B=true)) then
                          <enter statements>
                          end if
                          

                          【讨论】:

                          • 你如何将这个表达式推广到一个任意个布尔值,这是个问题?
                          猜你喜欢
                          • 2010-09-27
                          • 1970-01-01
                          • 2016-09-19
                          • 2021-11-14
                          • 1970-01-01
                          • 2011-04-06
                          • 1970-01-01
                          • 1970-01-01
                          • 2021-07-29
                          相关资源
                          最近更新 更多