具有3个值的xor
本文关键字:xor 3个 具有 | 更新日期: 2023-09-27 17:58:30
我需要在3个值之间执行异或条件运算,即我需要三个值中的一个为true,但不能超过一个,也不能为零。
我想我可以使用xor^运算符,但它并没有像预期的那样工作。
我原以为这会是假的,但事实并非如此。(true^true^true)
所有其他组合似乎都如我所料。
当查看xor运算符的文档时,他们只谈论比较2个值,而我在网上找不到任何关于比较3个或更多值的内容。
有人能阐明或提出一个简单的方法吗?
一种方法是将布尔值转换为整数,将结果相加,然后与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)