C# 三值异或
我需要在3个值之间做一个异或条件,即我需要三个值中的一个为真,但不超过一个且不为零 我想我可以使用xor^运算符来实现这一点,但它没有按预期工作 我原以为这会返回false,但事实并非如此。 (对^对^对) 所有其他组合似乎都像我预期的那样起作用 当查看xor运算符的文档时,他们只讨论比较2个值,我在网上找不到任何关于对3个或更多值进行比较的内容C# 三值异或,c#,operators,conditional,C#,Operators,Conditional,我需要在3个值之间做一个异或条件,即我需要三个值中的一个为真,但不超过一个且不为零 我想我可以使用xor^运算符来实现这一点,但它没有按预期工作 我原以为这会返回false,但事实并非如此。 (对^对^对) 所有其他组合似乎都像我预期的那样起作用 当查看xor运算符的文档时,他们只讨论比较2个值,我在网上找不到任何关于对3个或更多值进行比较的内容 有人能解释一下或建议一种简单的方法吗?XOR是一种二进制运算符,一次只能对2个值使用。所以当你做(真异或真异或真),它实际上做((真异或真)异或真)
有人能解释一下或建议一种简单的方法吗?XOR是一种二进制运算符,一次只能对2个值使用。所以当你做
(真异或真异或真)
,它实际上做((真异或真)异或真)
内部(true或XOR true)
解析为false
,因为它们是相同的。解决该问题后,余数为(false或true)
,解析为true
听起来你在试图聪明或高效地匹配你的条件。这可能会花费我几分钟的时间来弄清楚,然后写一个真值表或测试,以确保它正确处理所有可能的组合
如果(countTrues==1),只需计算其中有多少是true
,并执行if(countTrues==1)
,就简单多了。这是因为它没有显著的开销,而且与只使用位旋转的解决方案相比,它实际上是可读和可理解的(这里有几个其他的答案可以证明它)。使用异或,if(false^false^false)
应该返回false
。XOR的工作方式是支持XOR的。如果一个且只有一个元素为true,则Exclusive Or返回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)一种方法是转换,添加结果,并与1进行比较。
((true^true)^true)
将返回true
,这不是您从true^true^true
中所期望的结果
要确保获得想要的结果(只有一个值为真),请执行以下操作:
if ((a && !b && !c) || (!a && b && !c) || (!a && !b && c))
if( Convert.ToInt32(a) + Convert.ToInt32(b) + Convert.ToInt32(c) == 1 )
或者,根据@jcomeau_ictx answer,您可以执行以下操作:
if ((a && !b && !c) || (!a && b && !c) || (!a && !b && c))
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
,以便更清楚地了解函数的结果。XOR是一个二进制运算符,不能在两个以上的操作数上运行。根据操作顺序,当您查看:(false^false^false)
时,您确实得到了“((false^false)^false)”
在mindm中,在评估这是否适用于您方面,为您所需的操作构建一个真值表可能很有用。对于上面列出的示例,您将看到:
(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、Op2和Op3)。因为我无法获得足够的Linq,那么:
new[]{a,b,c}。计数(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())
的东西,除非必须对这两个函数进行求值。在我试图解决自己的问题时遇到了这个问题(4个值的异或);确定LINQ是处理N情况下此问题的最简单方法
bool OneAndOnlyOneIsTrue(IEnumerable<bool> conditions)
{
return conditions.Count(c => c) == 1;
}
指令方面,这将检查每个条件一次,所以函数是O(n),但它是灵活和该死的站点mo