xor有3个值

我需要在3个值之间做一个xor条件,即我需要三个值中的一个为真但不超过一个而不是无。

我以为我可以使用xor ^运算符,但它没有按预期工作。

我希望这会返回假,但它不会。 (true ^ true ^ true)

所有其他组合似乎按我的预期工作。

在查看xor运算符的文档时,他们只讨论比较2个值,并且我无法在线查找3个或更多值的任何内容。

任何人都可以放弃任何亮点或建议一个简单的方法吗?

((true ^ 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)); } 

编辑:你可能想要命名函数TernaryXor以便更清楚函数的结果。

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

因为我无法得到足够的Linq,怎么样:

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

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

当然,这是一个棘手的问题。 鉴于你想要的:

 abc 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())除非必须对两个函数进行求值。

解决方案很简单

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

使用XOR, if (false^false^false)应该返回false 。 正在研究如何使用XOR来工作。 如果只有一个元素为真,则返回true。 如果它们都是假的,它将返回false。

如果你想用3个值来表达,比如a,b和c,表达式应该是:(a或b或c)和〜(a和b)和〜(a和c)和〜(b和C)

下面是一个更合适的格式:(avbvc)*〜(a * b)*〜(a * c)*〜(b * c)

XOR是二元运算符,不能在两个以上的操作数上运行。 根据操作的顺序,当你看到: (false ^ false ^ false)你真的得到`((false ^ false)^ false)。

考虑到这一点,在评估这是否适合您的过程中,为您自己的操作建立一个真值表可能是有用的。 在上面列出的示例中,您将看到:

 (Op1 XOR Op2) XOR Op3 = Result FFFF FFTTFTFTFTTF TFFT TFTF TTFF TTTT 

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

XOR是二元运算符,一次只能用于2个值。 所以当你这样做时(true XOR true XOR true) ,它实际上是((true XOR true) XOR true)

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

听起来你正试图在匹配条件时聪明或超级高效。 这可能需要几分钟时间才能解开,然后编写真值表或测试以确保它正确处理所有可能的组合。

只计算其中有多少是true并且if (countTrues == 1)那么简单得多。 这样做的原因是它没有显着的开销,并且与仅使用bit-twiddling的解决方案相比,它实际上是可读的和可理解的(这里有几个其他答案可以certificate它)。

遇到这种情况时,我走错了路,试图解决我自己的问题(4个值的XOR); 决定LINQ是处理N-case的这个问题的最简单方法;

 bool OneAndOnlyOneIsTrue(IEnumerable 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),但它是灵活的,该死的站点比按位替代更可读。 😉