Check if at least two out of three booleans are true

后端 未结 30 1606
自闭症患者
自闭症患者 2020-11-28 16:47

An interviewer recently asked me this question: given three boolean variables, a, b, and c, return true if at least two out of the three are true.

My solution follow

相关标签:
30条回答
  • 2020-11-28 17:45

    Why not implement it literally? :)

    (a?1:0)+(b?1:0)+(c?1:0) >= 2
    

    In C you could just write a+b+c >= 2 (or !!a+!!b+!!c >= 2 to be very safe).

    In response to TofuBeer's comparison of java bytecode, here is a simple performance test:

    class Main
    {
        static boolean majorityDEAD(boolean a,boolean b,boolean c)
        {
            return a;
        }
    
        static boolean majority1(boolean a,boolean b,boolean c)
        {
            return a&&b || b&&c || a&&c;
        }
    
        static boolean majority2(boolean a,boolean b,boolean c)
        {
            return a ? b||c : b&&c;
        }
    
        static boolean majority3(boolean a,boolean b,boolean c)
        {
            return a&b | b&c | c&a;
        }
    
        static boolean majority4(boolean a,boolean b,boolean c)
        {
            return (a?1:0)+(b?1:0)+(c?1:0) >= 2;
        }
    
        static int loop1(boolean[] data, int i, int sz1, int sz2)
        {
            int sum = 0;
            for(int j=i;j<i+sz1;j++)
            {
                for(int k=j;k<j+sz2;k++)
                {
                    sum += majority1(data[i], data[j], data[k])?1:0; 
                    sum += majority1(data[i], data[k], data[j])?1:0; 
                    sum += majority1(data[j], data[k], data[i])?1:0; 
                    sum += majority1(data[j], data[i], data[k])?1:0; 
                    sum += majority1(data[k], data[i], data[j])?1:0; 
                    sum += majority1(data[k], data[j], data[i])?1:0; 
                }
            }
            return sum;
        }
    
        static int loop2(boolean[] data, int i, int sz1, int sz2)
        {
            int sum = 0;
            for(int j=i;j<i+sz1;j++)
            {
                for(int k=j;k<j+sz2;k++)
                {
                    sum += majority2(data[i], data[j], data[k])?1:0; 
                    sum += majority2(data[i], data[k], data[j])?1:0; 
                    sum += majority2(data[j], data[k], data[i])?1:0; 
                    sum += majority2(data[j], data[i], data[k])?1:0; 
                    sum += majority2(data[k], data[i], data[j])?1:0; 
                    sum += majority2(data[k], data[j], data[i])?1:0; 
                }
            }
            return sum;
        }
    
        static int loop3(boolean[] data, int i, int sz1, int sz2)
        {
            int sum = 0;
            for(int j=i;j<i+sz1;j++)
            {
                for(int k=j;k<j+sz2;k++)
                {
                    sum += majority3(data[i], data[j], data[k])?1:0; 
                    sum += majority3(data[i], data[k], data[j])?1:0; 
                    sum += majority3(data[j], data[k], data[i])?1:0; 
                    sum += majority3(data[j], data[i], data[k])?1:0; 
                    sum += majority3(data[k], data[i], data[j])?1:0; 
                    sum += majority3(data[k], data[j], data[i])?1:0; 
                }
            }
            return sum;
        }
    
        static int loop4(boolean[] data, int i, int sz1, int sz2)
        {
            int sum = 0;
            for(int j=i;j<i+sz1;j++)
            {
                for(int k=j;k<j+sz2;k++)
                {
                    sum += majority4(data[i], data[j], data[k])?1:0; 
                    sum += majority4(data[i], data[k], data[j])?1:0; 
                    sum += majority4(data[j], data[k], data[i])?1:0; 
                    sum += majority4(data[j], data[i], data[k])?1:0; 
                    sum += majority4(data[k], data[i], data[j])?1:0; 
                    sum += majority4(data[k], data[j], data[i])?1:0; 
                }
            }
            return sum;
        }
    
        static int loopDEAD(boolean[] data, int i, int sz1, int sz2)
        {
            int sum = 0;
            for(int j=i;j<i+sz1;j++)
            {
                for(int k=j;k<j+sz2;k++)
                {
                    sum += majorityDEAD(data[i], data[j], data[k])?1:0; 
                    sum += majorityDEAD(data[i], data[k], data[j])?1:0; 
                    sum += majorityDEAD(data[j], data[k], data[i])?1:0; 
                    sum += majorityDEAD(data[j], data[i], data[k])?1:0; 
                    sum += majorityDEAD(data[k], data[i], data[j])?1:0; 
                    sum += majorityDEAD(data[k], data[j], data[i])?1:0; 
                }
            }
            return sum;
        }
    
        static void work()
        {
            boolean [] data = new boolean [10000];
            java.util.Random r = new java.util.Random(0);
            for(int i=0;i<data.length;i++)
                data[i] = r.nextInt(2) > 0;
            long t0,t1,t2,t3,t4,tDEAD;
            int sz1 = 100;
            int sz2 = 100;
            int sum = 0;
    
            t0 = System.currentTimeMillis();
    
            for(int i=0;i<data.length-sz1-sz2;i++)
                sum += loop1(data, i, sz1, sz2);
    
            t1 = System.currentTimeMillis();
    
            for(int i=0;i<data.length-sz1-sz2;i++)
                sum += loop2(data, i, sz1, sz2);
    
            t2 = System.currentTimeMillis();
    
            for(int i=0;i<data.length-sz1-sz2;i++)
                sum += loop3(data, i, sz1, sz2);
    
            t3 = System.currentTimeMillis();
    
            for(int i=0;i<data.length-sz1-sz2;i++)
                sum += loop4(data, i, sz1, sz2);
    
            t4 = System.currentTimeMillis();
    
            for(int i=0;i<data.length-sz1-sz2;i++)
                sum += loopDEAD(data, i, sz1, sz2);
    
            tDEAD = System.currentTimeMillis();
    
            System.out.println("a&&b || b&&c || a&&c : " + (t1-t0) + " ms");
            System.out.println("   a ? b||c : b&&c   : " + (t2-t1) + " ms");
            System.out.println("   a&b | b&c | c&a   : " + (t3-t2) + " ms");
            System.out.println("   a + b + c >= 2    : " + (t4-t3) + " ms");
            System.out.println("       DEAD          : " + (tDEAD-t4) + " ms");
            System.out.println("sum: "+sum);
        }
    
        public static void main(String[] args) throws InterruptedException
        {
            while(true)
            {
                work();
                Thread.sleep(1000);
            }
        }
    }
    

    This prints the following on my machine (running Ubuntu on Intel Core 2 + sun java 1.6.0_15-b03 with HotSpot Server VM (14.1-b02, mixed mode)):

    First and second iterations:

    a&&b || b&&c || a&&c : 1740 ms
       a ? b||c : b&&c   : 1690 ms
       a&b | b&c | c&a   : 835 ms
       a + b + c >= 2    : 348 ms
           DEAD          : 169 ms
    sum: 1472612418
    

    Later iterations:

    a&&b || b&&c || a&&c : 1638 ms
       a ? b||c : b&&c   : 1612 ms
       a&b | b&c | c&a   : 779 ms
       a + b + c >= 2    : 905 ms
           DEAD          : 221 ms
    

    I wonder, what could java VM do that degrades performance over time for (a + b + c >= 2) case.

    And here is what happens if I run java with a -client VM switch:

    a&&b || b&&c || a&&c : 4034 ms
       a ? b||c : b&&c   : 2215 ms
       a&b | b&c | c&a   : 1347 ms
       a + b + c >= 2    : 6589 ms
           DEAD          : 1016 ms
    

    Mystery...

    And if I run it in GNU Java Interpreter, it gets almost 100 times slower, but the a&&b || b&&c || a&&c version wins then.

    Results from Tofubeer with the latest code running OS X:

    a&&b || b&&c || a&&c : 1358 ms
       a ? b||c : b&&c   : 1187 ms
       a&b | b&c | c&a   : 410 ms
       a + b + c >= 2    : 602 ms
           DEAD          : 161 ms
    

    Results from Paul Wagland with a Mac Java 1.6.0_26-b03-383-11A511

    a&&b || b&&c || a&&c : 394 ms 
       a ? b||c : b&&c   : 435 ms
       a&b | b&c | c&a   : 420 ms
       a + b + c >= 2    : 640 ms
       a ^ b ? c : a     : 571 ms
       a != b ? c : a    : 487 ms
           DEAD          : 170 ms
    
    0 讨论(0)
  • 2020-11-28 17:45

    Taking the answers (so far) here:

    public class X
    {
        static boolean a(final boolean a, final boolean b, final boolean c)
        {
        return ((a && b) || (b && c) || (a && c));
        }
    
        static boolean b(final boolean a, final boolean b, final boolean c)
        {
        return a ? (b || c) : (b && c);
        }
    
        static boolean c(final boolean a, final boolean b, final boolean c)
        {
        return ((a & b) | (b & c) | (c & a));
        }
    
        static boolean d(final boolean a, final boolean b, final boolean c)
        {
        return ((a?1:0)+(b?1:0)+(c?1:0) >= 2);
        }
    }
    

    and running them through the decompiler (javap -c X > results.txt):

    Compiled from "X.java"
    public class X extends java.lang.Object{
    public X();
      Code:
       0:   aload_0
       1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
       4:   return
    
    static boolean a(boolean, boolean, boolean);
      Code:
       0:   iload_0
       1:   ifeq    8
       4:   iload_1
       5:   ifne    24
       8:   iload_1
       9:   ifeq    16
       12:  iload_2
       13:  ifne    24
       16:  iload_0
       17:  ifeq    28
       20:  iload_2
       21:  ifeq    28
       24:  iconst_1
       25:  goto    29
       28:  iconst_0
       29:  ireturn
    
    static boolean b(boolean, boolean, boolean);
      Code:
       0:   iload_0
       1:   ifeq    20
       4:   iload_1
       5:   ifne    12
       8:   iload_2
       9:   ifeq    16
       12:  iconst_1
       13:  goto    33
       16:  iconst_0
       17:  goto    33
       20:  iload_1
       21:  ifeq    32
       24:  iload_2
       25:  ifeq    32
       28:  iconst_1
       29:  goto    33
       32:  iconst_0
       33:  ireturn
    
    static boolean c(boolean, boolean, boolean);
      Code:
       0:   iload_0
       1:   iload_1
       2:   iand
       3:   iload_1
       4:   iload_2
       5:   iand
       6:   ior
       7:   iload_2
       8:   iload_0
       9:   iand
       10:  ior
       11:  ireturn
    
    static boolean d(boolean, boolean, boolean);
      Code:
       0:   iload_0
       1:   ifeq    8
       4:   iconst_1
       5:   goto    9
       8:   iconst_0
       9:   iload_1
       10:  ifeq    17
       13:  iconst_1
       14:  goto    18
       17:  iconst_0
       18:  iadd
       19:  iload_2
       20:  ifeq    27
       23:  iconst_1
       24:  goto    28
       27:  iconst_0
       28:  iadd
       29:  iconst_2
       30:  if_icmplt   37
       33:  iconst_1
       34:  goto    38
       37:  iconst_0
       38:  ireturn
    }
    

    You can see that the ?: ones are slightly better then the fixed up version of your original. The one that is the best is the one that avoids branching altogether. That is good from the point of view of fewer instructions (in most cases) and better for branch prediction parts of the CPU, since a wrong guess in the branch prediction can cause CPU stalling.

    I'd say the most efficient one is the one from moonshadow overall. It uses the fewest instructions on average and reduces the chance for pipeline stalls in the CPU.

    To be 100% sure you would need to find out the cost (in CPU cycles) for each instruction, which, unfortunately isn't readily available (you would have to look at the source for hotspot and then the CPU vendors specs for the time taken for each generated instruction).

    See the updated answer by Rotsor for a runtime analysis of the code.

    0 讨论(0)
  • 2020-11-28 17:45

    We can convert the bools to integers and perform this easy check:

    (int(a) + int(b) + int(c)) >= 2
    
    0 讨论(0)
  • 2020-11-28 17:45
    return 1 << $a << $b << $c >= 1 << 2;
    
    0 讨论(0)
  • 2020-11-28 17:47

    You don't need to use the short circuiting forms of the operators.

    return (a & b) | (b & c) | (c & a);

    This performs the same number of logic operations as your version, however is completely branchless.

    0 讨论(0)
  • 2020-11-28 17:48

    Rather than writing:

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

    Write:

    return someExpression;
    

    As for the expression itself, something like this:

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

    or this (whichever you find easier to grasp):

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

    It tests a and b exactly once, and c at most once.

    References

    • JLS 15.25 Conditional Operator ? :
    0 讨论(0)
提交回复
热议问题