我需要在3个值之间做一个xor条件,即我需要三个值中的一个为真但不超过一个而不是没有。
我以为我可以使用xor ^运算符,但它没有按预期工作。
我预计这会返回false,但它不会。 (true ^ true ^ true)
所有其他组合似乎都符合我的预期。
在查看xor运算符的文档时,他们只讨论比较2个值,而且无法在线查找3个或更多值的任何内容。
任何人都可以提出任何理由或提出一个简单的方法吗?
答案 0 :(得分:16)
一种方法是转换Boolean values to an integer,添加结果,然后与1进行比较。
答案 1 :(得分:11)
因为我无法得到足够的Linq,怎么样:
new[] { a, b, c }.Count(v => v) == 1
答案 2 :(得分:10)
((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
,以便更清楚函数的结果。
答案 3 :(得分:4)
这很简短!(&& b&& c)&& (一个^ B ^ C)
答案 4 :(得分:4)
这肯定是一个棘手的问题。鉴于你想要的:
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())
之类的内容。
答案 5 :(得分:3)
解决方案简单
a = true; b = false; c = true
(a ^ b) || (a ^ c)
答案 6 :(得分:2)
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的解决方案相比,它实际上是可读的和可理解的(这里有一些其他答案可以证明它)。
答案 7 :(得分:2)
使用XOR,if (false^false^false)
应返回false
。正在研究如何使用XOR来工作。如果其中一个且只有一个元素为真,则 Exclusive或返回true。如果它们都是假的,它将返回false。
如果你想用3个值来表达,比如a,b和c,表达式应该是这样的: (a或b或c)和〜(a和b)和〜(a和c)和〜(b和c)
这是一种更合适的格式: (a v b v c)*〜(a * b)*〜(a * c)*〜(b * c)
答案 8 :(得分:2)
XOR是一个二元运算符,不能在两个以上的操作数上运行。根据操作的顺序,当您查看:(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)。
答案 9 :(得分:2)
当我沿着错误的路径试图解决我自己的问题时遇到这个问题(XOR跨越4个值);决定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),但它是灵活的,并且比按位替代方案更可读。 ; - )