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