views:

614

answers:

10

I've got three boolean values A, B and C. I need to write an IF statement which will execute if and only if no more than one of these values is True. In other words, here is the truth table:

 A | B | C | Result
---+---+---+--------
 0 | 0 | 0 |   1
 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

What is the best way to write this? I know I can enumerate all possibilities, but that seems... too verbose. :P

Added: Just had one idea:

!(A && B) && !(B && C) && !(A && C)

This checks that no two values are set. The suggestion about sums is OK as well. Even more readable maybe...

(A?1:0) + (B?1:0) + (C?1:0) <= 1

P.S. This is for production code, so I'm going more for code readability than performance.

Added 2: Already accepted answer, but for the curious ones - it's C#. :) The question is pretty much language-agnostic though.

+10  A: 

how about treating them as integer 1's and 0's, and checking that their sum equals 1?

EDIT:

now that we know that it's c#.net, i think the most readable solution would look somewhat like

public static class Extensions
{
    public static int ToInt(this bool b)
    {
        return b ? 1 : 0;
    }
}

the above tucked away in a class library (appcode?) where we don't have to see it, yet can easily access it (ctrl+click in r#, for instance) and then the implementation will simply be:

public bool noMoreThanOne(params bool[] bools) 
{ 
    return bools.ToList().Sum(b => b.ToInt()) <= 1; 
}

...

bool check = noMoreThanOne(true, true, false, any, amount, of, bools);
David Hedlund
Sum equals 1 OR 0.
Vicky
that the sum is smaller or equal to 1
Josh
whoops, missed that condition, yeah =)
David Hedlund
Also this assumes that casting a boolean true to an integer gives 1. You might have to set up integer variables separately first for this, likeint a1 = a ? 1 : 0int b1 = b ? 1 : 0int c1 = c ? 1 : 0if (a1 + b1 + c1 <= 1) {... }
Vicky
I'll accept this. I think this will be the most readable way.
Vilx-
@Vicky: good point, maybe if the OP specified language we'd be on firmer ground :-)
Steve Jessop
See updated question. :)
Vilx-
Here's how this could look using a Python generator expression: sum(1 if x else 0 for x in (A,B,C)) <= 1
Anon
The ToList() is redundant. And why bother converting to int when you can just use a predicate and a count?
Matt Howells
You might also consider that this will always execute in O(n) in the best case, whereas a version with a short-circuit will be worst case O(n), best case O(2).
plinth
public static bool NoMoreThanOne(params bool[] bools){ int count = 0; bool noMoreThanOne = true; foreach (bool b in bools) { if (b) { count++; if (count > 1) { noMoreThanOne = false; break; } } } return noMoreThanOne;}
plinth
+6  A: 

(A XOR B XOR C) OR NOT (A OR B OR C)

Edit: As pointed out by Vilx, this isn't right.

If A and B are both 1, and C is 0, A XOR B will be 0, the overall result will be 0.

How about: NOT (A AND B) AND NOT (A AND C) AND NOT (B AND C)

Vicky
I don't think this corresponds to the truth table above....
Vilx-
Bugger, you're quite right. My mistake.
Vicky
This gives the incorrect value for when a, b, and c are all true.
Matt Howells
Yep, I already thought of the second version. :)
Vilx-
Your second attempt is right.
schnaader
+8  A: 

You shold familiarize yourself with Karnaugh maps. Concept is most often applied to electronics but is very useful here too. It's very easy (thought Wikipedia explanation does look long -- it's thorough).

Pasi Savolainen
Beat me to it. :)
Mikael Auno
A: 

Good ol' logic:

+ = OR
. = AND

R = Abar.Bbar.Cbar + Abar.Bbar.C + Abar.B.Cbar + A.Bbar.Cbar
  = Abar.Bbar.(Cbar + C) + Abar.B.Cbar + A.Bbar.Cbar
  = Abar.Bbar + Abar.B.Cbar + A.Bbar.Cbar
  = Abar.Bbar + CBar(A XOR B)
  = NOT(A OR B) OR (NOT C AND (A XOR B))

Take the hint and simplify further if you want.

And yeah, get your self familiar with Karnaugh Maps

Swanand
I think the end result here is illegible. Karnaugh maps are for optimising circuits, not for writing comprehensible boolean expressions...
Steve Jessop
Yeah, I guess I agree.
Swanand
Although illegible is perfectly personal, I've grown up designing circuits, so the end result here appears like "puts "Hello World!" to me.
Swanand
I think that what I don't like about it is that the original problem is symmetric wrt to A,B,C, but this is not immediately obvious in the result. I can see that your expression is correct, since to me it says "nothing; or C; or not C but one of A and B". But I think it would take me a while to get from a cold start to understanding what the expression actually achieves, if I didn't already know what the question is.
Steve Jessop
A: 

A general way of finding a minimal boolean expression for a given truth table is to use a Karnaugh map:

http://babbage.cs.qc.edu/courses/Minimize/

There are several online minimizers on the web. The one here (linked to from the article, it's in German, though) finds the following expression:

(!A && !B) || (!A && !C) || (!B && !C)

If you're going for code readability, though, I would probably go with the idea of "sum<=1". Take care that not all languages guarantee that false==0 and true==1 -- but you're probably aware of this since you've taken care of it in your own solution.

Martin B
A: 

Depends whether you want something where it's easy to understand what you're trying to do, or something that's as logically simple as can be. Other people are posting logically simple answers, so here's one where it's more clear what's going on (and what the outcome will be for different inputs):

  def only1st(a, b, c):
    return a and not b and not c

  if only1st(a, b, c) or only1st(b, a, c) or only1st(c, a, b):
    print "Yes"
  else:
    print "No"
You forgot the case where all three are false. :)
Vilx-
Remove "a and" from only1st, so it's "only" in the sense of "at most" rather than in the sense of "exactly", and you're done.
Steve Jessop
+2  A: 

If you turn the logic around, you want the condition to be false if you have any pair of booleans that are both true:

if (! ((a && b) || (a && c) || (b && c))) { ... }

For something completely different, you can put the booleans in an array and count how many true values there are:

if ((new bool[] { a, b, c }).Where(x => x).Count() <= 1) { ... }
Guffa
Ugh... now that's convulted! I'd upvote for originality though! :D
Vilx-
+2  A: 

I'd go for maximum maintainability and readability.

static bool ZeroOrOneAreTrue(params bool[] bools)
{
    return NumThatAreTrue(bools) <= 1;
}

static int NumThatAreTrue(params bool[] bools)
{
    return bools.Where(b => b).Count();
}
Matt Howells
A: 

I like the addition solution, but here's a hack to do that with bit fields as well.

inline bool OnlyOneBitSet(int x)
{
    // removes the leftmost bit, if zero, there was only one set.
    return x & (x-1) == 0;
}

// macro for int conversion
#define BOOLASINT(x) ((x)?1:0)

// turn bools a, b, c into the bit field cba
int i = (BOOLASINT(a) << 0) | BOOLASINT(b) << 1 | BOOLASINT(c) << 2;

if (OnlyOneBitSet(i)) { /* tada */ }
plinth
A: 

Code demonstration of d's solution:

int total=0;
if (A) total++;
if (B) total++;
if (C) total++;

if (total<=1) // iff no more than one is true.
{
    // execute

}
Dave Gamble
I think I had one in my question too... :P
Vilx-