xor with 3 values
I need to do an xor conditional between 3 values, ie i need one of the three values to be true but not more than开发者_运维百科 one and not none.
I thought i could use the xor ^ operator for this but its not working as expected.
I expected that this would return false but it doesnt. (true ^ true ^ true)
all other combinations seem to work as i expected.
When looking at the docs for the xor operator they only talk about comparing 2 values and i cant find anything on doing this for 3 or more values online.
Can anyone shed any light or suggest a simple way of doing this?
One way would be to convert the Boolean values to an integer, add the results, and compare to 1.
((true ^ true) ^ true)
will return true
, which is not what you would expect from true ^ true ^ true
.
To make sure that you get the outcome you want (only one value to be true) do the following:
if ((a && !b && !c) || (!a && b && !c) || (!a && !b && c))
Alternatively, based on @jcomeau_ictx answer, you can do the following:
if( Convert.ToInt32(a) + Convert.ToInt32(b) + Convert.ToInt32(c) == 1 )
Or, you could create a function:
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: You might want to name the function TernaryXor
so that it is more clear as to the outcome of the function.
Because I can't get enough Linq, how about:
new[] { a, b, c }.Count(v => v) == 1
this is short !(a&&b&&c) && (a^b^c)
It's a tricky one, for sure. Given what you want:
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
This will do it:
rslt = (!a & (b ^ c)) || (a & !(b | c));
The first part handles the four cases where a
is 0. The second part, where a
is not 0.
A simpler way to look at it is this:
rslt = (a | b | c) & !((a & b) | (a & c) | (b & c))
That is, one of the three must be true, but no two (or more) can be true.
It seems like there should be a way to simplify further, but it's not coming to mind. Maybe I need more caffeine.
EDIT
I think this one is the solution I was looking for this morning:
rslt = a ? !(b | c) : (b ^ c);
Now, as to why I used |
instead of ||
:
It's a combination of a style issue and an old bias against branching (old habits die hard). !(b | c)
generates this IL code:
ldarg.1
ldarg.2
or
ldc.i4.0
ceq
stloc.0
There aren't any branches in that code. If I use ||
, as in !(b ||c)
, it generates:
ldarg.1
brfalse.s IL_009B
ldarg.2
br.s IL_009C
IL_009B:
ldc.i4.1
IL_009C:
stloc.0
Which has two branches. I don't know if the JIT-produced code will mirror that, but I suspect it will. So one bit of code is 6 instructions that are always executed. The other is 6 instructions of which sometimes only 4 are executed. But branching penalties might very well eat up any gains from not executing two of the instructions.
I realize that modern CPUs are much better at branching than the 8086 was, and there might not be any detectable difference in the runtime of these two code snippets. Even if there were, it's unlikely that it would make a significant difference in the overall runtime of the programs I typically write.
But I tell you that it certainly used to! On the 8086, where branching was very expensive, the difference between (b | c)
and (b || c)
was huge.
Finally, using |
, as you noted, forces evaluation of the entire expression. My original code says, in effect, "if this expression is true or that expression is true." Using &&
and ||
turns it into a bunch of conditionals and is, in my brain, more difficult to read.
So: an old prejudice based on most likely outdated performance considerations. But harmless.
One has to be careful, though, not to write something like (b() | c())
unless both functions must be evaluated.
Using XOR, if (false^false^false)
should return false
. It is working how XOR is supposted to work. Exclusive Or returns true if one and only one of the elements is true. If they are all false, it will return false.
If you want to do it with 3 values, say a,b and c, the expression should be something like: (a or b or c) and ~(a and b) and ~(a and c) and ~(b and c)
heres a more proper format: (a v b v c) * ~(a * b) * ~(a * c) * ~(b * c)
XOR is a binary operator and does not function on more than two operands. Given the order of operations, when you are looking at: (false ^ false ^ false)
you are really getting `((false ^ false) ^ false).
Bearing that in mindm, in terms of evaluating whether this is going to work for you, it may be useful to build yourself a truth table for your desired operation. In the case of the example listed above you are looking at:
(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
Which makes XOR a problem in the case where all operands are true and may require some special handling say by adding an AND not (Op1 AND Op2 AND Op3).
XOR is a binary operator, it can only be used on 2 values at a time. So when you do (true XOR true XOR true)
, it actually does ((true XOR true) XOR true)
...
The inner (true XOR true)
resolves to false
because they're the same. With that resolved, the remainder is (false XOR true)
, which resolves to true
.
It sounds like you're trying to be clever or super-efficient about matching your conditions. This would probably take me several minutes to puzzle out and then write a truth-table or test to make sure it treats all possible combinations correctly.
It is so much simpler to just count how many of them are true
and do if (countTrues == 1)
. The reason for this is that it doesn't have significant overhead, and it is actually readable and understandable compared to a solution using only bit-twiddling (there are a couple other answers here that demonstrate it).
Came across this while I went down the wrong path trying to solve my own problem (XOR across 4 values); decided that LINQ is the simplest way to handle this problem for an N-case;
bool OneAndOnlyOneIsTrue(IEnumerable<bool> conditions)
{
return conditions.Count(c => c) == 1;
}
Invoked like;
bool OneLuckyGuy(Person p1, Person p2, Person p3, Person p4)
{
return OneAndOnlyOneIsTrue(new [] {
p1.IsAMan(),
p2.IsAMan(),
p3.IsAMan(),
p4.IsAMan()
});
}
Instruction-wise, this will check each condition once, so function is O(n), but it's flexible and a damn site more readable than the bitwise alternative. ;-)
the solution simple it's
a = true; b = false; c = true
(a ^ b) || (a ^ c)
精彩评论