41

What is the simplest way to do a three-way exclusive OR?

In other words, I have three values, and I want a statement that evaluates to true IFF only one of the three values is true.

So far, this is what I've come up with:

((a ^ b) && (a ^ c) && !(b && c)) || ((b ^ a) && (b ^ c) && !(a && c)) || ((c ^ a) && (c ^ b) && !(a && b))

Is there something simpler to do the same thing?


Here's the proof that the above accomplishes the task:

a = true; b = true; c = true
((a ^ b) && (a ^ c) && !(b && c)) || ((b ^ a) && (b ^ c) && !(a && c)) || ((c ^ a) && (c ^ b) && !(a && b))
=> false

a = true; b = true; c = false
((a ^ b) && (a ^ c) && !(b && c)) || ((b ^ a) && (b ^ c) && !(a && c)) || ((c ^ a) && (c ^ b) && !(a && b))
=> false

a = true; b = false; c = true
((a ^ b) && (a ^ c) && !(b && c)) || ((b ^ a) && (b ^ c) && !(a && c)) || ((c ^ a) && (c ^ b) && !(a && b))
=> false

a = true; b = false; c = false
((a ^ b) && (a ^ c) && !(b && c)) || ((b ^ a) && (b ^ c) && !(a && c)) || ((c ^ a) && (c ^ b) && !(a && b))
=> true

a = false; b = true; c = true
((a ^ b) && (a ^ c) && !(b && c)) || ((b ^ a) && (b ^ c) && !(a && c)) || ((c ^ a) && (c ^ b) && !(a && b))
=> false

a = false; b = true; c = false
((a ^ b) && (a ^ c) && !(b && c)) || ((b ^ a) && (b ^ c) && !(a && c)) || ((c ^ a) && (c ^ b) && !(a && b))
=> true

a = false; b = false; c = true
((a ^ b) && (a ^ c) && !(b && c)) || ((b ^ a) && (b ^ c) && !(a && c)) || ((c ^ a) && (c ^ b) && !(a && b))
=> true

a = false; b = false; c = false
((a ^ b) && (a ^ c) && !(b && c)) || ((b ^ a) && (b ^ c) && !(a && c)) || ((c ^ a) && (c ^ b) && !(a && b))
=> false
polygenelubricants
  • 376,812
  • 128
  • 561
  • 623
Josh
  • 1,387
  • 2
  • 13
  • 15

9 Answers9

53

For exactly three terms, you can use this expression:

(a ^ b ^ c) && !(a && b && c)

The first part is true iff one or three of the terms are true. The second part of the expression ensures that not all three are true.

Note that the above expression does NOT generalize to more terms. A more general solution is to actually count how many terms are true, so something like this:

int trueCount =
   (a ? 1 : 0) +
   (b ? 1 : 0) +
   (c ? 1 : 0) +
   ... // more terms as necessary 

return (trueCount == 1); // or some range check expression etc
polygenelubricants
  • 376,812
  • 128
  • 561
  • 623
  • Great, but the general solution does not short-circuit. – Ani Aug 28 '10 at 09:35
  • 2
    `a ? 1 : 0` can be simplified to `!!a` – kaspersky May 26 '14 at 07:46
  • 4
    @gg.kaspersky, only in JavaScript, C, and languages which have truthy/falsy tests via the `!` operator. For example, this wouldn't work in Java or C#. – Drew Noakes Sep 03 '14 at 14:50
  • @DrewNoakes, when I made the comment, I thought for some reason that the question was C specific. Indeed, certain syntax constructs are valid only in certain languages. – kaspersky Sep 03 '14 at 17:52
20
bool result = (a?1:0)+(b?1:0)+(c?1:0) == 1;
Hemant
  • 19,486
  • 24
  • 91
  • 127
9

a^b^c is only 1 if an uneven number of variables is 1 (two '1' would cancel each other out). So you just need to check for the case "all three are 1":

result = (a^b^c) && !(a&&b&&c)
Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
9

Another possibility:

a ? !b && !c : b ^ c

which happens to be 9 characters shorter than the accepted answer :)

Timwi
  • 65,159
  • 33
  • 165
  • 230
  • 1
    Kinda glad this is not the accepted answer for readability reasons, but still, I had to upvote as shortness still has some kinky appeal to me. On university in C class, we had a contest in writing the shortest source code that generates a song similar to Heidis Kuken, and I was in TOP 5 in a contest. – Vlasec Nov 22 '19 at 22:23
  • @Vlasec I assume you know about [code golf](https://codegolf.stackexchange.com/), but if you didn't here you go. – ThomasPM Jun 07 '23 at 21:33
3

Better yet on Python:

result = (1 if a else 0)+(1 if b else 0)+(1 if c else 0) == 1

This can be used also on if statements!

It saved my day for CLI mutually exclusive arguments through Click (everyone hates click)

2

You could also try (in C):

!!a + !!b + !!c == 1

kaspersky
  • 3,959
  • 4
  • 33
  • 50
  • This is only valid in some languages [eg. javascript], and it's not a simplification. In a language which auto-converts boolean to number for `+`, if `a`, `b`, and `c` are already boolean, you also don't need the double negation `!!`: just `a+b+c===1` will be equivalent. – blgt Jun 20 '14 at 10:23
  • 1
    @blgt, my mistake, I should specify that I meant to be an answer for C language. – kaspersky Jun 20 '14 at 14:18
  • 1
    Using `!!` to guarantee a C true is `1` is actually pretty neat. A bit paranoid in the context of a boolean logic question though. If the rest of your code is well-written `a+b+c==1` should still be sufficient – blgt Jun 20 '14 at 14:35
1
f= lambda{ |a| [false, false, true].permutation.to_a.uniq.include? a }
p f.call([false, true, false])
p f.call([false, true, true])

$ true

$ false

Because I can.

nurettin
  • 11,090
  • 5
  • 65
  • 85
1

Here's a general implementation that fails quickly when more than one bool is found to be true.

Usage:

XOR(a, b, c);

Code:

public static bool XOR(params bool[] bools)
{
    return bools.Where(b => b).AssertCount(1);
}

public static bool AssertCount<T>(this IEnumerable<T> source, int countToAssert)
{
    int count = 0;
    foreach (var t in source)
    {
        if (++count > countToAssert) return false;
    }

    return count == countToAssert;
}
Ani
  • 111,048
  • 26
  • 262
  • 307
0

In C:

#include <stdbool.h>

bool array_xor(size_t array_size, bool[] array) {
    int count = 0;

    for (int i = 0; i < array_size && count < 2; i++) {
        if (array[i]) {
            count++;
        }
    }

    return count == 1;
}
Jivan Pal
  • 182
  • 10
  • I'm not sure this works, nor is it simple. – Jaan May 05 '23 at 04:27
  • Define "simple". It definitely works, as what it's doing is checking that exactly one element of the array is `true`. It works for an arbitrarily large array of booleans, not just 3 elements, and short circuits as soon as it finds a second element that is `true` (hence `&& count < 2`). – Jivan Pal May 05 '23 at 11:36