一位面试官最近问了我这样一个问题:给定三个布尔变量a、b和c,如果三个变量中至少有两个为真,则返回true。

我的解决方案如下:

boolean atLeastTwo(boolean a, boolean b, boolean c) {
    if ((a && b) || (b && c) || (a && c)) {
        return true;
    }
    else{
        return false;
    }
}

他说这还可以进一步改进,但如何改进呢?


当前回答

public static boolean atLeast(int atLeastToBeTrue, boolean...bools){
    int booleansTrue = 0;
    for(boolean tmp : bools){
        booleansTrue += tmp ? 1 : 0;
    }
    return booleansTrue >= atLeastToBeTrue;
}

你可以从varargs a.k.a boolean[]中选择至少有多少你想为真:-)

其他回答

If the goal is to return a bitwise two-out-of-three value for three operands, arithmetic and iterative approaches are apt to be relatively ineffective. On many CPU architectures, a good form would be "return ((a | b) & c) | (a & b);". That takes four boolean operations. On single-accumulator machines (common in small embedded systems) that's apt to take a total of seven instructions per byte. The form "return (a & b) | (a & c) | (b & c);" is perhaps nicer looking, but it would require five boolean operations, or nine instructions per byte on a single-accumulator machine.

顺便提一下,在CMOS逻辑中,计算“不是三选二”需要12个晶体管(相比之下,逆变器需要2个晶体管,双输入NAND或NOR需要4个晶体管,而三输入NAND或NOR需要6个晶体管)。

而不是写:

if (someExpression) {
    return true;
} else {
    return false;
}

写:

return someExpression;

至于表达式本身,是这样的:

boolean atLeastTwo(boolean a, boolean b, boolean c) {
    return a ? (b || c) : (b && c);
}

或者这个(你觉得哪个更容易理解):

boolean atLeastTwo(boolean a, boolean b, boolean c) {
    return a && (b || c) || (b && c);
}

它只测试a和b一次,c最多测试一次。

参考文献

JLS 15.25条件运算符?:

总结一下。它被称为布尔代数是有原因的:

  0 x 0 = 0
  1 x 0 = 0
  1 x 1 = 1

  0 + 0 = 0
  1 + 0 = 1
  1 + 1 = 0 (+ carry)

如果你看那里的真值表,你可以看到乘法是布尔和,而简单的加法是异或。

回答你的问题:

return (a + b + c) >= 2

下面是一个测试驱动的通用方法。虽然不像目前提供的大多数解决方案那样“高效”,但是清晰、经过测试、有效且通用。

public class CountBooleansTest extends TestCase {
    public void testThreeFalse() throws Exception {
        assertFalse(atLeastTwoOutOfThree(false, false, false));
    }

    public void testThreeTrue() throws Exception {
        assertTrue(atLeastTwoOutOfThree(true, true, true));
    }

    public void testOnes() throws Exception {
        assertFalse(atLeastTwoOutOfThree(true, false, false));
        assertFalse(atLeastTwoOutOfThree(false, true, false));
        assertFalse(atLeastTwoOutOfThree(false, false, true));
    }

    public void testTwos() throws Exception {
        assertTrue(atLeastTwoOutOfThree(false, true, true));
        assertTrue(atLeastTwoOutOfThree(true, false, true));
        assertTrue(atLeastTwoOutOfThree(true, true, false));
    }

    private static boolean atLeastTwoOutOfThree(boolean b, boolean c, boolean d) {
        return countBooleans(b, c, d) >= 2;
    }

    private static int countBooleans(boolean... bs) {
        int count = 0;
        for (boolean b : bs)
            if (b)
                count++;
        return count;
    }
}

C解。

int two(int a, int b, int c) {
  return !a + !b + !c < 2;
}

或者你可能更喜欢:

int two(int a, int b, int c) {
  return !!a + !!b + !!c >= 2;
}