具有3个值的xor

本文关键字:xor 3个 具有 | 更新日期: 2023-09-27 17:58:30

我需要在3个值之间执行异或条件运算,即我需要三个值中的一个为true,但不能超过一个,也不能为零。

我想我可以使用xor^运算符,但它并没有像预期的那样工作。

我原以为这会是假的,但事实并非如此。(true^true^true)

所有其他组合似乎都如我所料。

当查看xor运算符的文档时,他们只谈论比较2个值,而我在网上找不到任何关于比较3个或更多值的内容。

有人能阐明或提出一个简单的方法吗?

具有3个值的xor

一种方法是将布尔值转换为整数,将结果相加,然后与1进行比较。

((true ^ true) ^ true)将返回true,这不是您对true ^ true ^ true的期望。

为了确保您得到想要的结果(只有一个值为真),请执行以下操作:

if ((a && !b && !c) || (!a && b && !c) || (!a && !b && c))

或者,基于@jcomeau_ictx答案,您可以执行以下操作:

if( Convert.ToInt32(a) + Convert.ToInt32(b) + Convert.ToInt32(c) == 1 )

或者,您可以创建一个函数:

public bool TernaryXor(bool a, bool b, bool c)
{
    //return ((a && !b && !c) || (!a && b && !c) || (!a && !b && c));
    // taking into account Jim Mischel's comment, a faster solution would be:
    return (!a && (b ^ c)) || (a && !(b || c));
}

EDIT:您可能希望将函数命名为TernaryXor,以便更清楚地了解函数的结果。

因为我不能得到足够的Linq,怎么样:

new[] { a, b, c }.Count(v => v) == 1

这很短!(a&b&c)&(a^b^c)

这当然是一个棘手的问题。给定您想要的:

a b c rslt
0 0 0  0
0 0 1  1
0 1 0  1
0 1 1  0
1 0 0  1
1 0 1  0
1 1 0  0
1 1 1  0

这样可以做到:

rslt = (!a & (b ^ c)) || (a & !(b | c));

第一部分处理a为0的四种情况。第二部分,其中a不为0。

一个更简单的方法是:

rslt = (a | b | c) & !((a & b) | (a & c) | (b & c))

也就是说,三者中必须有一个是真的,但没有两个(或更多)是真的。

似乎应该有一种进一步简化的方法,但它并没有出现在脑海中。也许我需要更多的咖啡因。

编辑

我想这就是我今天早上想要的解决方案:

rslt = a ? !(b | c) : (b ^ c);

现在,关于为什么我使用|而不是||:

这是一个风格问题和对分支的旧偏见的结合(旧习惯很难养成)。!(b | c)生成此IL代码:

ldarg.1
ldarg.2
or
ldc.i4.0
ceq
stloc.0

该代码中没有任何分支。如果我使用||,就像在!(b ||c)中一样,它会生成:

  ldarg.1
  brfalse.s IL_009B
  ldarg.2
  br.s IL_009C
IL_009B:
  ldc.i4.1
IL_009C:
  stloc.0

它有两个分支。我不知道JIT生成的代码是否会反映这一点,但我怀疑它会。因此,一位代码就是6条总是被执行的指令。另一个是6条指令,其中有时只有4条被执行。但分支惩罚很可能会吞噬不执行其中两条指令的任何收益。

我意识到,现代CPU在分支方面比8086要好得多,而且这两个代码片段的运行时间可能没有任何可检测的差异。即使有,它也不太可能对我通常编写的程序的整体运行时间产生重大影响。

但我告诉你,过去确实如此!在8086上,分支非常昂贵,(b | c)(b || c)之间的差异非常大。

最后,如您所述,使用|强制对整个表达式进行求值。实际上,我的原始代码说,"如果这个表达式是真的那个表达式是真。"使用&&||会把它变成一堆条件句,在我的大脑中更难阅读。

因此:基于最有可能过时的性能考虑的旧偏见。但无害。

不过,必须小心,除非必须对这两个函数进行求值,否则不要编写类似(b() | c())的内容。

使用XOR,if (false^false^false)应返回false。XOR是如何工作的ExclusiveOr如果其中一个且只有一个元素为true,则返回true。如果它们都是假的,它就会返回false。

如果你想用3个值来做,比如a、b和c,表达式应该是这样的:(a或b或c)和~(a和b)和~

这里有一个更合适的格式:(a v b v c)*~(a*b)*~。给定操作顺序,当您看到:(false ^ false ^ false)时,您实际上得到了`((false^false)^false)。

考虑到这一点,在评估这是否对你有效时,为自己建立一个所需操作的真值表可能会很有用。在上面列出的例子中,您看到的是:

(Op1 XOR Op2) XOR Op3 = Result    
 F       F        F     F
 F       F        T     T 
 F       T        F     T 
 F       T        T     F
 T       F        F     T
 T       F        T     F
 T       T        F     F
 T       T        T     T

这使得在所有操作数都为真的情况下XOR成为一个问题,并且可能需要一些特殊处理,例如通过添加and not(Op1 and Op2 and Op3)。

XOR是一个二进制运算符,一次只能对两个值使用。所以当你做(true XOR true XOR true)时,它实际上是做((true XOR true) XOR true)。。。

内部(true XOR true)解析为false,因为它们是相同的。解析后,余数为(false XOR true),其解析为true

这听起来像是你在努力聪明或高效地匹配你的条件。这可能需要我几分钟的时间来弄清楚,然后写一个真值表或测试,以确保它正确处理所有可能的组合。

只计算其中有多少是true并进行if (countTrues == 1)要简单得多。这样做的原因是它没有显著的开销,而且与只使用bit twidling的解决方案相比,它实际上是可读和可理解的(这里还有其他几个答案可以证明这一点)。

在我试图解决自己的问题时遇到了这个问题(4个值之间的XOR);决定LINQ是处理N情况下这个问题的最简单方法;

bool OneAndOnlyOneIsTrue(IEnumerable<bool> conditions)
{
    return conditions.Count(c => c) == 1;
}

像;

bool OneLuckyGuy(Person p1, Person p2, Person p3, Person p4)
{
     return OneAndOnlyOneIsTrue(new [] {         
         p1.IsAMan(),
         p2.IsAMan(),
         p3.IsAMan(),
         p4.IsAMan()
     });
}

在指令方面,这将检查每个条件一次,所以函数是O(n),但它很灵活,而且是一个比比特替代方案更可读的该死的站点。;-)

简单的解决方案是

a = true; b = false; c = true
(a ^ b) || (a ^ c)