41

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.

SCdF
  • 57,260
  • 24
  • 77
  • 113
  • 2
    Conversion is the most elegant way to do this. By far. – RBarryYoung Feb 15 '13 at 04:33
  • I'm interested if there is another way. I've already written the code with type conversion. The answer is allowed to be "You can't do this with boolean logic" if that's the answer. – SCdF Feb 15 '13 at 04:49
  • 1
    Why is XOR not suitable for you? It evaluates to true iff one is true right. – Shiva Kumar Feb 15 '13 at 06:32
  • 2
    Ok, I realise that with XOR, `true and true and true` will evaluate to `true` which should not be the case as per your requirement. – Shiva Kumar Feb 15 '13 at 09:11
  • @Shiva - I accidentally upvoted your first xor comment when I meant to point out what you just realized about true ^ true ^ true. Anyway, ignore the upvote! =) – c.fogelklou Feb 15 '13 at 10:37
  • "The most elegant way" is right on the line of "not constructive." You should accept either @ShivaKumar's answer that explains how it's not possible without an additional variable, or the recursive answer, depending on which was the question you actually meant to ask. – djechlin Feb 15 '13 at 13:43
  • Do we have a name for this beauty? – TWiStErRob Sep 05 '16 at 17:04
  • Are you allowed to use any programming language? Or do you need to express the computation using a single algebraic formula? What is the intended use case? – HelloGoodbye Jun 05 '20 at 00:13
  • @ShivaKumar No, XOR will evaluate to true iff _an odd number_ of them are true. – HelloGoodbye Jun 05 '20 at 00:16

18 Answers18

14

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
  • 1
    This doesn't seem to scale well 4,5,... inputs. It looks like you need `# inputs choose 2` operands to calculate `max_one_is_true`. – TWiStErRob Sep 05 '16 at 16:51
  • This solution can be made more easy to understand by not using `x ^ y ^ z` to test for an uneven number of true values, but just using `x or y or z` to ensure at least one is true. – R. Schreurs Feb 12 '19 at 14:58
  • @TWiStErRob, the number of pairs scale as n(n-1)/2, so quadraticly. That is not really bad, in my opinion. – R. Schreurs Feb 12 '19 at 15:01
5

Sure, you could do something like this (pseudocode, since you didn't mention language):

found = false;
alreadyFound = false;
for (boolean in booleans):
    if (boolean):
        found = true;
        if (alreadyFound):
            found = false;
            break;
        else:
            alreadyFound = true;
return found;
Eric Galluzzo
  • 3,191
  • 1
  • 20
  • 20
5

After your clarification, here it is with no integers.

 bool IsExactlyOneBooleanTrue( bool *boolAry, int size )
    {
      bool areAnyTrue = false;
      bool areTwoTrue = false;
      for(int i = 0; (!areTwoTrue) && (i < size); i++) {
        areTwoTrue = (areAnyTrue && boolAry[i]);
        areAnyTrue |= boolAry[i];
      }
      return ((areAnyTrue) && (!areTwoTrue));
    }
c.fogelklou
  • 1,781
  • 1
  • 13
  • 26
  • Interesting way to implement the `break` keyword. Did you want to avoid branching? – TWiStErRob Sep 05 '16 at 17:06
  • @TWiStErRob, do you mean because there is no break? The main reason is for readability. This way, all exit conditions are apparent in the start of the loop; it lets the reader know which conditions will cause the loop to exit (and hence the purpose of the loop.) – c.fogelklou Sep 10 '16 at 06:48
  • Most the usages of `areTwoTrue` exist to stop the loop. I guess it's what we're used to / what the language conventions are (C++ v Java). I think [my approach](http://stackoverflow.com/a/39334985/253468) is readable too (ignore how we iterate the array, that's language specific): it clearly shows that we are only concerned about the `true` values in the array and that we'll stop at the second. I guess the cyclomatic complexity is similar, it's just more prevalent using `if`s than `|=` and `= &&`. Curious what you think. – TWiStErRob Sep 10 '16 at 07:32
  • Either is OK, it's just a matter of preference. I prefer to not have to look into a loop to see why it exits most of the time, and would rather read it from the while/for statement. But of course, sometimes it make sense to break or return from in the loop to make code more readable. To each his own. (you were right, though, "my" version might result in fewer branches, but if the compiler is smart, yours and mine might just result in the same hardware code anyway.) – c.fogelklou Sep 11 '16 at 12:07
5

No-one mentioned that this "operation" we're looking for is shortcut-able similarly to boolean AND and OR in most languages. Here's an implementation in Java:

public static boolean exactlyOneOf(boolean... inputs) {
    boolean foundAtLeastOne = false;
    for (boolean bool : inputs) {
        if (bool) {
            if (foundAtLeastOne) {
                // found a second one that's also true, shortcut like && and ||
                return false;
            }
            foundAtLeastOne = true;
        }
    }
    // we're happy if we found one, but if none found that's less than one
    return foundAtLeastOne;
}
TWiStErRob
  • 44,762
  • 26
  • 170
  • 254
4

booleanList.Where(y => y).Count() == 1;

J.T. Taylor
  • 4,147
  • 1
  • 23
  • 23
4

With plain boolean logic, it may not be possible to achieve what you want. Because what you are asking for is a truth evaluation not just based on the truth values but also on additional information(count in this case). But boolean evaluation is binary logic, it cannot depend on anything else but on the operands themselves. And there is no way to reverse engineer to find the operands given a truth value because there can be four possible combinations of operands but only two results. Given a false, can you tell if it is because of F ^ F or T ^ T in your case, so that the next evaluation can be determined based on that?.

Shiva Kumar
  • 3,111
  • 1
  • 26
  • 34
  • 1
    Not true. c.fogelklou's answer can indeed be interpreted as plain boolean logic. Theoretically, SCdF is asking for a boolean function with many arguments, and we know that any boolean function can be implemented with just Conjunction and Negation. – took Apr 10 '14 at 08:11
  • It is always possible to find out if more than one boolean is true by looping. I am sure the OP already knew this. But to my knowledge when the OP originally asked, he wanted an elegant answer without looping or by using directly boolean logic(like a XOR or similar thing) which directly returned true only if one and one element was true. – Shiva Kumar Apr 10 '14 at 09:50
3

Due to the large number of reads by now, here comes a quick clean up and additional information.

Option 1:

Ask if only the first variable is true, or only the second one, ..., or only the n-th variable.

x1 & !x2 & ... & !xn |
!x1 & x2 & ... & !xn |
...
!x1 & !x2 & ... & xn

This approach scales in O(n^2), the evaluation stops after the first positive match is found. Hence, preferred if it is likely that there is a positive match.

Option 2:

Ask if there is at least one variable true in total. Additionally check every pair to contain at most one true variable (Anders Johannsen's answer)

(x1 | x2 | ... | xn) &
(!x1 | !x2) &
...
(!x1 | !xn) &
(!x2 | !x3) &
...
(!x2 | !xn) &
...

This option also scales in O(n^2) due to the number of possible pairs. Lazy evaluation stops the formula after the first counter example. Hence, it is preferred if its likely there is a negative match.

(Option 3):

This option involves a subtraction and is thus no valid answer for the restricted setting. Nevertheless, it argues how looping the values might not be the most beneficial solution in an unrestricted stetting.

Treat x1 ... xn as a binary number x. Subtract one, then AND the results. The output is zero <=> x1 ... xn contains at most one true value. (the old "check power of two" algorithm)

x    00010000
x-1  00001111
AND  00000000

If the bits are already stored in such a bitboard, this might be beneficial over looping. Though, keep in mind this kills the readability and is limited by the available board length.

A last note to raise awareness: by now there exists a stack exchange called computer science which is exactly intended for this type of algorithmic questions

Chad Broski
  • 111
  • 1
  • 6
2

It can be done quite nicely with recursion, e.g. in Haskell

-- there isn't exactly one true element in the empty list
oneTrue [] = False 
-- if the list starts with False, discard it
oneTrue (False : xs) = oneTrue xs
-- if the list starts with True, all other elements must be False
oneTrue (True : xs) = not (or xs)
Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
2

Python:

boolean_list.count(True) == 1
HelloGoodbye
  • 3,624
  • 8
  • 42
  • 57
2

// Javascript Use .filter() on array and check the length of the new array.

// Example using array
isExactly1BooleanTrue(boolean:boolean[]) {
  return booleans.filter(value => value === true).length === 1;
}

// Example using ...booleans
isExactly1BooleanTrue(...booleans) {
  return booleans.filter(value => value === true).length === 1;
}
TimothyBrake
  • 551
  • 6
  • 9
1

One way to do it is to perform pairwise AND and then check if any of the pairwise comparisons returned true with chained OR. In python I would implement it using

from itertools import combinations

def one_true(bools):
    pairwise_comp = [comb[0] and comb[1] for comb in combinations(bools, 2)]
    return not any(pairwise_comp)

This approach easily generalizes to lists of arbitrary length, although for very long lists, the number of possible pairs grows very quickly.

Neuneck
  • 294
  • 1
  • 3
  • 14
0

OK, another try. Call the different booleans b[i], and call a slice of them (a range of the array) b[i .. j]. Define functions none(b[i .. j]) and just_one(b[i .. j]) (can substitute the recursive definitions to get explicit formulas if required). We have, using C notation for logical operations (&& is and, || is or, ^ for xor (not really in C), ! is not):

none(b[i .. i + 1]) ~~> !b[i] && !b[i + 1]
just_one(b[i .. i + 1]) ~~> b[i] ^ b[i + 1]

And then recursively:

none(b[i .. j + 1]) ~~> none(b[i .. j]) && !b[j + 1]
just_one(b[i .. j + 1] ~~> (just_one(b[i .. j]) && !b[j + 1]) ^ (none(b[i .. j]) && b[j + 1])

And you are interested in just_one(b[1 .. n]).

The expressions will turn out horrible.

Have fun!

vonbrand
  • 11,412
  • 8
  • 32
  • 52
  • Did you mean to generate code or provide a functional answer? It would be awesome if C-style a code macro could be written based on this. – TWiStErRob Sep 05 '16 at 17:11
0

That python script does the job nicely. Here's the one-liner it uses:

((x ∨ (y ∨ z)) ∧ (¬(x ∧ y) ∧ (¬(z ∧ x) ∧ ¬(y ∧ z))))

0

Python: let see using example... steps:

  1. below function exactly_one_topping takes three parameter

  2. stores their values in the list as True, False

  3. Check whether there exists only one true value by checking the count to be exact 1.

def exactly_one_topping(ketchup, mustard, onion):
    
  args = [ketchup,mustard,onion]
  if args.count(True) == 1:   # check if Exactly one value is True
      return True
  else:
      return False
0

Retracted for Privacy and Anders Johannsen provided already correct and simple answers. But both solutions do not scale very well (O(n^2)). If performance is important you can stick to the following solution, which performs in O(n):

def exact_one_of(array_of_bool):
    exact_one = more_than_one = False
    for array_elem in array_of_bool:
        more_than_one = (exact_one and array_elem) or more_than_one
        exact_one = (exact_one ^ array_elem) and (not more_than_one)
    return exact_one

(I used python and a for loop for simplicity. But of course this loop could be unrolled to a sequence of NOT, AND, OR and XOR operations)

It works by tracking two states per boolean variable/list entry:

  1. is there exactly one "True" from the beginning of the list until this entry?
  2. are there more than one "True" from the beginning of the list until this entry?

The states of a list entry can be simply derived from the previous states and corresponding list entry/boolean variable.

mrh1997
  • 922
  • 7
  • 18
-1

How do you want to count how many are true without, you know, counting? Sure, you could do something messy like (C syntax, my Python is horrible):

for(i = 0; i < last && !booleans[i]; i++)
     ;
if(i == last)
     return 0;  /* No true one found */
/* We have a true one, check there isn't another */
for(i++; i < last && !booleans[i]; i++)
     ;
if(i == last)
     return 1; /* No more true ones */
else
     return 0; /* Found another true */ 

I'm sure you'll agree that the win (if any) is slight, and the readability is bad.

vonbrand
  • 11,412
  • 8
  • 32
  • 52
-1

It is not possible without looping. Check BitSet cardinality() in java implementation. http://fuseyism.com/classpath/doc/java/util/BitSet-source.html

TarekZ
  • 19
  • 3
-4

We can do it this way:-

if (A=true or B=true)and(not(A=true and B=true)) then
<enter statements>
end if
Souravi Sinha
  • 93
  • 1
  • 2
  • 11
  • 2
    and how do you generalize this expression to an *arbitrary* number of boolean, which was the question ? – Pac0 Jul 14 '17 at 13:53