How do I determine if *exactly* one boolean is true, without type conversion?

SCdF picture SCdF · Feb 15, 2013 · Viewed 28.1k times · Source

Given an arbitrary list of booleans, what is the most elegant way of determining that exactly one of them is true?

The most obvious hack is type conversion: converting them to 0 for false and 1 for true and then summing them, and returning sum == 1.

I'd like to know if there is a way to do this without converting them to ints, actually using boolean logic.

(This seems like it should be trivial, idk, long week)

Edit: In case it wasn't obvious, this is more of a code-golf / theoretical question. I'm not fussed about using type conversion / int addition in PROD code, I'm just interested if there is way of doing it without that.

Edit2: Sorry folks it's a long week and I'm not explaining myself well. Let me try this:

In boolean logic, ANDing a collection of booleans is true if all of the booleans are true, ORing the collection is true if least one of them is true. Is there a logical construct that will be true if exactly one boolean is true? XOR is this for a collection of two booleans for example, but any more than that and it falls over.

Answer

Anders Johannsen picture Anders Johannsen · Oct 21, 2015

You can actually accomplish this using only boolean logic, although there's perhaps no practical value of that in your example. The boolean version is much more involved than simply counting the number of true values.

Anyway, for the sake of satisfying intellectual curiosity, here goes. First, the idea of using a series of XORs is good, but it only gets us half way. For any two variables x and y,

xy

is true whenever exactly one of them is true. However, this does not continue to be true if you add a third variable z,

xyz

The first part, xy, is still true if exactly one of x and y is true. If either x or y is true, then z needs to be false for the whole expression to be true, which is what we want. But consider what happens if both x and y are true. Then xy is false, yet the whole expression can become true if z is true as well. So either one variable or all three must be true. In general, if you have a statement that is a chain of XORs, it will be true if an uneven number of variables are true.

Since one is an uneven number, this might prove useful. Of course, checking for an uneven number of truths is not enough. We additionally need to ensure that no more than one variable is true. This can be done in a pairwise fashion by taking all pairs of two variables and checking that they are not both true. Taken together these two conditions ensure that exactly one if the variables are true.

Below is a small Python script to illustrate the approach.

from itertools import product

print("x|y|z|only_one_is_true")
print("======================")
for x, y, z in product([True, False], repeat=3):
    uneven_number_is_true = x ^ y ^ z
    max_one_is_true = (not (x and y)) and (not (x and z)) and (not (y and z))
    only_one_is_true = uneven_number_is_true and max_one_is_true
    print(int(x), int(y), int(z), only_one_is_true)

And here's the output.

x|y|z|only_one_is_true
======================
1 1 1 False
1 1 0 False
1 0 1 False
1 0 0 True
0 1 1 False
0 1 0 True
0 0 1 True
0 0 0 False