Kiểm tra xem ít nhất hai trong số ba booleans có đúng không


579

Một người phỏng vấn gần đây đã hỏi tôi câu hỏi này: đưa ra ba biến boolean, a, b và c, trả về true nếu ít nhất hai trong ba biến là đúng.

Giải pháp của tôi như sau:

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

Ông nói rằng điều này có thể được cải thiện hơn nữa, nhưng làm thế nào?


170
Nội tuyến tuyên bố trả lại.
Fingerlas

45
Nghe giống như một cuộc phỏng vấn "ai có IQ cao nhất". Tôi sẽ thất bại.
Chris Dutrow

79
atLeastTwo(iWantYou, iNeedYou, imEverGonnaLoveYou)
Andrew Grimm

92
Tại sao mọi người nêu lên những câu hỏi tầm thường nhất?
BlueRaja - Daniel Pflughoeft

46
Các câu hỏi chung chung và dễ hiểu nhận được rất nhiều phiếu bầu. Các câu hỏi rất cụ thể và kỹ thuật không.
Jay

Câu trả lời:


820

Thay vì viết:

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

Viết:

return someExpression;

Đối với chính biểu thức, một cái gì đó như thế này:

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

hoặc cái này (cái nào bạn thấy dễ nắm bắt hơn):

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

Nó kiểm tra abchính xác một lần, và cnhiều nhất một lần.

Người giới thiệu


144
+1: giải pháp đáng yêu cho câu đố, nhưng hy vọng chúng ta không thấy bất cứ điều gì như thế này trong thế giới thực :)
Juliet

124
@Juliet: Tôi không biết, tôi nghĩ rằng nếu đây là một yêu cầu trong thế giới thực (với tên biến thực) thì nó sẽ đọc khá tốt. Hãy xem xét return hasGoodAttendance ? (passedCoursework || passed Exam) : (passedCoursework && passedExam), điều đó có vẻ tốt với tôi.
Andrzej Doyle

18
Tôi không nghĩ rằng nó trông tệ , nhưng nếu yêu cầu trong miền được hiểu là "ít nhất hai", tôi nghĩ nó sẽ dễ đọc hơn atLeastTwo(hasgoodAttendance, passedCoursework, passedExam). Ý tưởng về "ít nhất 2 bools là đúng" đủ chung chung để xứng đáng với chức năng của chính nó.
Ken

17
@Lese: Yêu cầu mã được tối ưu hóa vi mô nhất trong các cuộc phỏng vấn trực tiếp là không thực tế, và tôi dám nói, vô dụng. Tối ưu hóa vi mô, khi được thúc đẩy bởi nhu cầu, được hướng dẫn bởi các kết quả định hình thời gian chạy, chứ không phải bản năng của con người (được biết là khủng khiếp). Bạn chắc chắn có thể yêu cầu người được phỏng vấn quá trình bạn tối ưu hóa điều này hơn nữa; điều đó quan trọng hơn kết quả.
đa gen

17
Toán tử ternary là một thành ngữ phổ biến mà bạn sẽ có thể đọc. Nếu bạn không thể đọc nó, bạn nên nghiên cứu nó cho đến khi bạn có thể. Việc sử dụng toán tử ternary không phải là thứ tôi cho là "thông minh" theo nghĩa xúc phạm. Nhưng vâng, tôi sẽ coi đây là phần thân của một cuộc gọi phương thức nếu bạn thường sử dụng logic "ít nhất hai".
Stephen P

494

Chỉ vì lợi ích của việc sử dụng XOR để trả lời một vấn đề tương đối đơn giản ...

return a ^ b ? c : a

160
Wow, giải pháp tuyệt vời. Nhưng đối với tôi phiên bản đảo ngược của nó dễ hiểu hơn: a == b? a: c
Rotsor

5
a ^ b? c: a ^ b? c: a ^ b? c: a
alexanderpas

4
Yay, .. XOR bị báo chí xấu như vậy và bạn hiếm khi có cơ hội sử dụng nó.
EightyOne Đoàn kết

19
@ Stimul8d có thể bởi vì, đối với booleans, nó giống như! = Nhưng ít đọc hơn? Hình dung ra đó là một khoảnh khắc eureka đối với tôi ...
Tikhon Jelvis

2
Tôi thích dạng nhị phân thuần túy: return ((a ^ b) & c) | (a & b). Nó không phân nhánh (nhanh hơn) và dễ đọc: (a hoặc b là đúng và c là đúng) hoặc (a và b đều đúng). Lưu ý rằng (a | b) và (a ^ b) đều hoạt động với công thức này.
flanglet

217

Tại sao không thực hiện nó theo nghĩa đen? :)

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

Trong C bạn chỉ có thể viết a+b+c >= 2(hoặc !!a+!!b+!!c >= 2rất an toàn).

Để đối phó với TofuBeer so sánh java bytecode 's, đây là một thử nghiệm hiệu suất đơn giản:

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);
        }
    }
}

Thao tác này sẽ in như sau trên máy của tôi (chạy Ubuntu trên Intel Core 2 + sun java 1.6.0_15-b03 với HotSpot Server VM (14.1-b02, chế độ hỗn hợp)):

Lặp lại thứ nhất và thứ hai:

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

Lặp lại sau:

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

Tôi tự hỏi, java VM có thể làm gì mà làm giảm hiệu suất theo thời gian cho trường hợp (a + b + c> = 2).

Và đây là những gì xảy ra nếu tôi chạy java bằng -clientcông tắc VM:

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

Huyền bí...

Và nếu tôi chạy nó trong Trình thông dịch Java GNU , nó sẽ chậm hơn gần 100 lần, nhưng a&&b || b&&c || a&&cphiên bản sẽ thắng.

Kết quả từ Tofubeer với mã mới nhất chạy 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

Kết quả từ Paul Wagland với 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

4
a+b+c >= 2: điều này không hoạt động với tiêu cực, phải không? Bạn có thể phải làm !!ađiều đó, tôi không chắc chắn.
đa sinh học

8
<s> -1. Bạn không bao giờ nên làm điều đó cho C. Bạn không biết giá trị của true là gì (nó có thể dễ dàng là -1). </ S> Thật ra tôi đoán C99 bao gồm trong tiêu chuẩn của nó rằng true được định nghĩa là 1. Nhưng Tôi vẫn sẽ không làm điều này.
Mark Peters

1
Có thể nếu đầu vào của bạn là kết quả của các hoạt động boolean? Và điều đó có khả thi cho kiểu "bool" trong C ++ không?
Rotsor

2
@Rotsor: Không ai nói đầu vào phải là kết quả của các hoạt động boolean. Ngay cả khi không có tiêu cực, bạn đang chơi với lửa, như thể bạn xác định đó là 2 điều kiện của bạn sẽ có kết quả dương tính giả. Nhưng tôi không quan tâm đến điều đó nhiều như tôi không thích ý tưởng xen kẽ các booleans vào số học. Giải pháp Java của bạn rõ ràng ở chỗ nó không dựa vào các chuyển đổi sắc thái từ boolean sang một kiểu số nguyên.
Mark Peters

7
Hãy thận trọng với microbenchmark: java.sun.com/docs/hotspot/HotSpotFAQ.html#benchmarking_simple
BalusC

143

Loại câu hỏi này có thể được giải quyết bằng Bản đồ Karnaugh :

      | C | !C
------|---|----
 A  B | 1 | 1 
 A !B | 1 | 0
!A !B | 0 | 0
!A  B | 1 | 0

từ đó bạn suy ra rằng bạn cần một nhóm cho hàng đầu tiên và hai nhóm cho cột đầu tiên, thu được giải pháp tối ưu của đa gen:

(C && (A || B)) || (A && B)  <---- first row
       ^
       |
   first column without third case

10
@Justin, Bản đồ Karnaugh đã giảm số lượng các phép toán logic từ 3 AND và 2 OR xuống còn 2 AND và 2 OR. @Jack, Cảm ơn bạn đã nhắc nhở tôi về sự tồn tại của Bản đồ Karnaugh.
Tachy

14
+1 cho một cái gì đó mới. Thông số chức năng tiếp theo của tôi sẽ bao gồm một bản đồ K, cho dù nó cần hay nó.
Justin R.

2
Có thể khả năng đọc kém có thể được bù bằng (1) bảng thích hợp trong nhận xét và (2) bài kiểm tra đơn vị phù hợp ... +1 cho một cái gì đó hữu ích học được ở trường.
moala

140

Khả năng đọc nên là mục tiêu. Một người đọc mã phải hiểu ý định của bạn ngay lập tức. Vì vậy, đây là giải pháp của tôi.

int howManyBooleansAreTrue =
      (a ? 1 : 0)
    + (b ? 1 : 0)
    + (c ? 1 : 0);

return howManyBooleansAreTrue >= 2;

21
Tôi đồng ý với tiền đề, nhưng (a && b) || (b && c) || (a && c) dễ đọc hơn nhiều so với giải pháp IMHO của bạn.
Adrian Grigore

62
Hmm, bây giờ tôi cần một phiên bản "hai trong số BỐN Booleans" ... phiên bản của danatel bây giờ dễ dàng hơn nhiều .
Arafangion

6
Hoặc trong Scala:Seq(true, true, false).map(if (_) 1 else 0).sum >= 2
retronym

5
@retronym: Hmm, không. Cách Java hoạt động tốt trong Scala và cả dễ đọc và hiệu quả hơn.
Seun Osewa

134
return (a==b) ? a : c;

Giải trình:

Nếu a==b, thì cả hai đều đúng hoặc cả hai đều sai. Nếu cả hai đều đúng, chúng tôi đã tìm thấy hai booleans thật của mình và có thể trả về true (bằng cách trả về a). Nếu cả hai đều sai thì không thể có hai booleans đúng ngay cả khi clà true, vì vậy chúng ta trả về false (bằng cách trả về a). Đó là (a==b) ? amột phần. Thế còn : c? Vâng, nếu a==blà sai, thì chính xác là một trong ahoặc bphải là đúng, vì vậy chúng tôi đã tìm ra boolean đúng đầu tiên, và điều duy nhất còn lại là nếu cnó cũng đúng, vì vậy chúng tôi trả clời là câu trả lời.


8
c thậm chí không bao giờ được thử nghiệm ... rực rỡ!
RèmDog

Sử dụng mối quan hệ bắc cầu của sự bình đẳng và thực tế là một boolean là đúng hoặc sai +1
Barshe Roussy

3
Thật thanh lịch! Tôi đã phải kiểm tra bằng bút và giấy để tin điều đó :) Kudos cho bạn!
Adrian

3
Tôi nghĩ về điều này là "nếu abđồng ý, họ có đa số phiếu bầu, vì vậy hãy đi với bất cứ điều gì, nếu không, họ không đồng ý, vì vậy clà phiếu quyết định"
Ben Millwood

34

Bạn không cần phải sử dụng các hình thức ngắn mạch của các nhà khai thác.

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

Điều này thực hiện cùng số lượng hoạt động logic như phiên bản của bạn, tuy nhiên hoàn toàn không có chi nhánh.


11
Tại sao bạn muốn buộc 5 đánh giá khi 1 có thể làm? Nó thực sự không thực hiện cùng một số hoạt động logic trong thực tế. Trong thực tế, nó sẽ luôn luôn thực hiện nhiều hơn.
Mark Peters

2
Tôi nghĩ rằng trộn số học nhị phân và số học boolean là một ý tưởng tồi. Nó giống như lái vít trong tường bằng cờ lê. Điều tồi tệ nhất là họ có ngữ nghĩa khác nhau.
Peter Tillemans

12
@Mark - nó có thể nhanh hơn ... tùy thuộc vào tác động của dự đoán nhánh không chính xác trên đường ống CPU. Tuy nhiên, tốt nhất là để lại các tối ưu hóa vi mô như vậy cho trình biên dịch JIT.
Stephen C

4
Thật tốt khi làm điều gì đó như thế này trong Java (hoặc bất kỳ ngôn ngữ nào khác) ... với một vài lưu ý: 1) cần nhanh hơn (trong trường hợp này, tôi tin rằng, hãy xem câu trả lời thứ hai của tôi) 2) nhanh hơn đáng kể (không chắc chắn nếu có), 3) quan trọng nhất là tài liệu vì nó là "lẻ". Miễn là nó phục vụ một mục đích và nó được ghi lại là tốt để "phá vỡ các quy tắc" khi nó có ý nghĩa.
TofuBeer

11
@Peter Tillemans Không có sự pha trộn với các toán tử nhị phân, trong Java đây là các toán tử boolean.
starblue

27

Đây là một cách tiếp cận thử nghiệm, hướng chung. Không "hiệu quả" như hầu hết các giải pháp cho đến nay, nhưng rõ ràng, được thử nghiệm, làm việc và khái quát hóa.

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;
    }
}

8
Ồ, tôi chưa bao giờ thấy một phương pháp được thử nghiệm đầy đủ trước khi nhìn thấy phương pháp này.
Rotsor

51
Cá nhân tôi thấy mã này khủng khiếp, vì rất nhiều lý do. Tôi sẽ không downvote, nhưng nếu tôi từng thấy điều này trong mã sản xuất, tôi sẽ nguyền rủa. Một hoạt động boolean cực kỳ đơn giản không cần phải phức tạp như thế này.
Thuyền trưởngCasey

10
Tôi rất muốn biết lý do của bạn, @CaptainCasey. Tôi nghĩ rằng đây là mã khá tốt. Có một chức năng tổng quát đẹp, dễ hiểu, dễ xác minh và một chức năng cụ thể tận dụng lợi thế của nó, cũng dễ hiểu và xác minh. Trong thế giới thực, tôi sẽ công khai chúng và đưa chúng vào một lớp khác; ngoài điều đó - tôi rất vui khi đưa mã này vào sản xuất. Ồ - vâng - Tôi sẽ đổi tên CountBooleans () thành CountTrue ().
Carl Manaster

5
Nếu nó không phải là về hiệu suất, giải pháp này có vẻ gần như hoàn hảo đối với tôi: rất dễ đọc và có thể mở rộng. Đó chính xác là những gì var-args được tạo ra cho.
atamanroman

7
Cái quái gì vậy mọi người? Đây là mã rõ ràng và được kiểm tra tốt, và lý do duy nhất có vẻ như rất nhiều là vì nó bao gồm các bài kiểm tra. Một +++, sẽ upvote một lần nữa.
Christoffer Hammarström

24

Tổng kết lại. Nó được gọi là đại số boolean vì một lý do:

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

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

Nếu bạn nhìn vào các bảng chân lý ở đó, bạn có thể thấy phép nhân là boolean và, đơn giản là phép cộng là xor.

Để trả lời câu hỏi của bạn:

return (a + b + c) >= 2

2
Đây là giải pháp thanh lịch nhất, theo ý kiến ​​của tôi.
Torbjørn Kristoffersen

9
Mặc dù sai lầm của tân binh, giá trị boolean KHÔNG phải là 0, điều đó không có nghĩa là luôn luôn là 1.
tomdemuyt

13
Ngoại trừ thẻ trên bài đăng có ghi "Java" và bạn không thể viết "a + b + c" khi chúng được định nghĩa là booleans trong Java.
Jay

Để làm việc trong Java, nó sẽ phải như vậy return ((a?1:0) + (b?1:0) + (c?1:0)) >= 2.
David R Tribble

Duh, tôi đã bỏ phiếu này vì tôi nghĩ đó là câu hỏi của C ++ ... tại sao tôi lại đọc câu hỏi java? : /
Carlo Wood

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

15

Nó thực sự phụ thuộc vào những gì bạn có nghĩa là "cải thiện":

Rõ ràng hơn?

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

Khó hơn?

boolean moreThanTwo(boolean a, boolean b, boolean c)
{
    return a == b ? a : c;
}

Tổng quát hơn?

boolean moreThanXTrue(int x, boolean[] bs)
{
    int count = 0;

    for(boolean b : bs)
    {
        count += b ? 1 : 0;

        if(count > x) return true;
    }

    return false;
}

Khả năng mở rộng hơn?

boolean moreThanXTrue(int x, boolean[] bs)
{
    int count = 0;

    for(int i < 0; i < bs.length; i++)
    {
        count += bs[i] ? 1 : 0;

        if(count > x) return true;

        int needed = x - count;
        int remaining = bs.length - i;

        if(needed >= remaining) return false;
    }

    return false;
}

Nhanh hơn?

// Only profiling can answer this.

Cái nào được "cải thiện" phụ thuộc nhiều vào tình huống.


14

Đây là một cách thực hiện khác bằng cách sử dụng map / less. Điều này quy mô tốt cho hàng tỷ booleans © trong một môi trường phân tán. Sử dụng MongoDB:

Tạo một cơ sở dữ liệu valuesvề booleans:

db.values.insert({value: true});
db.values.insert({value: false});
db.values.insert({value: true});

Tạo bản đồ, giảm chức năng:

Chỉnh sửa : Tôi thích câu trả lời của RèmDog về việc áp dụng bản đồ / thu nhỏ cho các danh sách chung, do đó, ở đây có một chức năng bản đồ có một cuộc gọi lại xác định xem có nên tính giá trị hay không.

var mapper = function(shouldInclude) {
    return function() {
        emit(null, shouldInclude(this) ? 1 : 0);
    };
}

var reducer = function(key, values) {
    var sum = 0;
    for(var i = 0; i < values.length; i++) {
        sum += values[i];
    }
    return sum;
}

Chạy bản đồ / giảm:

var result = db.values.mapReduce(mapper(isTrue), reducer).result;

containsMinimum(2, result); // true
containsMinimum(1, result); // false


function isTrue(object) {
    return object.value == true;
}

function containsMinimum(count, resultDoc) {
    var record = db[resultDoc].find().next();
    return record.value >= count;
}

@Anurag: nhiều như tôi yêu M / R và sự tiếp xúc mà Google gần đây đã đưa ra cho nó (ngay cả khi đó không phải là M / R thực sự từ FP), tôi có xu hướng gọi những câu trả lời nhảm nhí. Có hàng tỷ và hàng tỷ dòng mã thực hiện "công cụ" trong thế giới thực trong đó không có một dòng bản đồ / thu nhỏ nào được sử dụng. Ai đó trả lời một câu hỏi như vậy với điều này chắc chắn được gắn cờ trong cuốn sách của tôi là: "cố gắng chơi smartie" . Chưa kể hầu hết những người phỏng vấn sẽ không thể biết bạn có đang cố gắng nhảm nhí hay không bởi vì họ thực sự chưa bao giờ viết một chương trình duy nhất sử dụng M / R trong sự nghiệp của họ.
Cú phápT3rr0r

2
@Syntax - Mọi người đều có quyền với ý kiến ​​của họ. Câu trả lời của tôi chỉ là một cách tiếp cận khác để xem xét vấn đề. Chắc chắn, nó có vẻ phóng đại cho 3 giá trị booleans, nhưng điều đó không có nghĩa là tôi đang cố gắng trở thành người thông minh ở đây. Đây là một cách tiếp cận phổ biến để giải quyết vấn đề mà mọi người đều sử dụng - chia vấn đề thành nhiều phần nhỏ. Đó là cách hoạt động của cảm ứng toán học, đó là cách mà hầu hết các thuật toán đệ quy hoạt động và đó là cách mọi người nói chung giải quyết vấn đề.
Anurag

13

Lấy câu trả lời (cho đến nay) ở đây:

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);
    }
}

và chạy chúng thông qua trình dịch ngược (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
}

Bạn có thể thấy rằng ?: Phiên bản tốt hơn một chút so với phiên bản gốc đã sửa. Một trong đó là tốt nhất là một trong đó tránh phân nhánh hoàn toàn. Điều đó tốt từ quan điểm của ít hướng dẫn hơn (trong hầu hết các trường hợp) và tốt hơn cho các phần dự đoán nhánh của CPU, vì dự đoán sai trong dự đoán nhánh có thể khiến CPU bị đình trệ.

Tôi muốn nói rằng hiệu quả nhất là tổng thể từ ánh trăng. Nó sử dụng các hướng dẫn trung bình ít nhất và làm giảm cơ hội cho các quầy hàng đường ống trong CPU.

Để chắc chắn 100%, bạn sẽ cần tìm hiểu chi phí (theo chu kỳ CPU) cho mỗi hướng dẫn, điều không may là không có sẵn (bạn sẽ phải xem nguồn cho hotspot và sau đó các nhà cung cấp CPU sẽ chỉ định thời gian thực hiện cho mỗi hướng dẫn được tạo ra).

Xem câu trả lời được cập nhật bởi Rotsor để biết phân tích thời gian chạy của mã.


5
Bạn chỉ đang nhìn vào mã byte. Đối với tất cả những gì bạn biết, JIT sẽ lấy một phiên bản có các nhánh trong mã byte và biến nó thành một phiên bản không có các nhánh trong mã gốc. Nhưng người ta có xu hướng nghĩ rằng ít nhánh hơn trong mã byte sẽ tốt hơn.
David Conrad

13

Một ví dụ khác về mã trực tiếp:

int  n = 0;
if (a) n++;
if (b) n++;
if (c) n++;
return (n >= 2);

Rõ ràng đó không phải là mã ngắn gọn nhất.

Phụ lục

Một phiên bản khác (tối ưu hóa một chút) này:

int  n = -2;
if (a) n++;
if (b) n++;
if (c) n++;
return (n >= 0);

Điều này có thể chạy nhanh hơn một chút, giả sử rằng so sánh với 0 sẽ sử dụng mã nhanh hơn (hoặc có thể ít hơn) so với so sánh với 2.


+1 @Loadmaster, tôi xin lỗi nhưng bạn đã nhầm! Đây là câu trả lời ngắn gọn nhất ở đây. (tức là ngắn gọn VÀ được thể hiện rõ ràng);)
Ash


@ M.Mimpen: Chỉ dành cho các đối tượng lớp. Đối với các kiểu nguyên thủy (như nở trên), bất kỳ trình biên dịch tử tế nào cũng sẽ biên dịch từng ++thao tác thành một lệnh CPU, cho dù đó là trước hay sau.
David R Tribble

12

Một cách khác để làm điều này nhưng không phải là một cách rất tốt:

return (Boolean.valueOf(a).hashCode() + Boolean.valueOf(b).hashCode() + Boolean.valueOf(c).hashCode()) < 3705);

Các Booleangiá trị băm được cố định ở mức 1231 cho đúng và 1237 cho sai vì vậy có thể sử dụng như nhau<= 3699


1
hoặc (a? 1: 0) + (b? 1: 0) + (c? 1: 0)> = 2
Peter Lawrey

12

Nhóm cải tiến rõ ràng nhất là:

// There is no point in an else if you already returned.
boolean atLeastTwo(boolean a, boolean b, boolean c) {
    if ((a && b) || (b && c) || (a && c)) {
        return true;
    }
    return false;
}

và sau đó

// There is no point in an if(true) return true otherwise return false.
boolean atLeastTwo(boolean a, boolean b, boolean c) {
    return ((a && b) || (b && c) || (a && c));
}

Nhưng những cải tiến đó là nhỏ.


10

Tôi không thích ternary ( return a ? (b || c) : (b && c);từ câu trả lời hàng đầu), và tôi không nghĩ rằng tôi đã thấy ai đề cập đến nó. Nó được viết như thế này:

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

8

Trong Clojure :

(defn at-least [n & bools]
  (>= (count (filter true? bools)) n)

Sử dụng:

(at-least 2 true false true)

2
+1 Phiên bản chung tuyệt vời cho thấy sức mạnh của Lisps. Cảm ơn,
thợ rèn

6

Tôi không nghĩ rằng tôi đã thấy giải pháp này chưa:

boolean atLeast(int howMany, boolean[] boolValues) {
  // check params for valid values

  int counter = 0;
  for (boolean b : boolValues) {
    if (b) {
      counter++;

      if (counter == howMany) {
        return true;
      }
    }
  }
  return false;
}

Ưu điểm của nó là một khi nó đạt đến con số mà bạn đang tìm kiếm, nó sẽ phá vỡ. Vì vậy, nếu đây là "ít nhất 2 trong số 1.000.000 giá trị này là đúng" trong đó hai giá trị đầu tiên thực sự đúng, thì nó sẽ đi nhanh hơn một số giải pháp "bình thường" hơn.


Đó có lẽ nên là: if (++ counter == howMany) thay vì tăng và sau đó kiểm tra riêng.
Joe Enos

2
Hoặc thậm chí ngắn hơn: if (b && (++ counter == howMany))
Joe Enos

1
Tôi sẽ làm boolean ... boolValuesnhư vậy để gọi dễ dàng hơn, nhưng vẫn mất một mảng
Stephen

Tôi không cập nhật trên Java của mình - không biết điều đó đã tồn tại. Một loại cú pháp lạ, nhưng nó rất hữu ích - thỉnh thoảng tôi sẽ làm điều đó trong C # (từ khóa params), và nó làm cho mọi thứ dễ gọi hơn. Hoặc, tôi không biết về Java, nhưng trong .NET, các mảng và tất cả các bộ sưu tập đều triển khai IEnumerable <T>, vì vậy tôi có thể sử dụng bất cứ thứ gì tương đương với Java.
Joe Enos

Làm thế nào để hiệu suất của so sánh này với ví dụ 2of3? trả lại a? (b || c): (b && c);
Iain Spneum

6

Chúng tôi có thể chuyển đổi các bools sang số nguyên và thực hiện kiểm tra dễ dàng này:

(int(a) + int(b) + int(c)) >= 2

6

Vì nó không được chỉ định cách cải thiện mã, tôi sẽ nỗ lực cải thiện mã bằng cách làm cho nó trở nên thú vị hơn. Đây là giải pháp của tôi:

boolean atLeastTwo(boolean t, boolean f, boolean True) {
    boolean False = True;
    if ((t || f) && (True || False)) 
        return "answer" != "42";
    if (t && f) 
        return !"France".contains("Paris");
    if (False == True) 
        return true == false;
    return Math.random() > 0.5;
}

Trong trường hợp bất cứ ai tự hỏi liệu mã này có hoạt động hay không, đây là một đơn giản hóa bằng cách sử dụng cùng một logic:

boolean atLeastTwo(boolean a, boolean b, boolean c) {
    if ((a || b) && (c)) 
        return true;
    if (a && b) 
        return true;
    if (true) 
        return false;
    // The last line is a red herring, as it will never be reached:
    return Math.random() > 0.5; 

}

Điều này có thể được đun sôi thêm về sau:

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

Nhưng bây giờ nó không còn buồn cười nữa.


5
Function ReturnTrueIfTwoIsTrue(bool val1, val2, val3))
{
     return (System.Convert.ToInt16(val1) +
             System.Convert.ToInt16(val2) +
             System.Convert.ToInt16(val3)) > 1;
}

Quá nhiều cách để làm điều này ...


3
Nhìn giống C # hơn. Điều này nên được đề cập như vậy trong câu trả lời vì câu hỏi được nhắm mục tiêu Java :)
BalusC

5

Dung dịch AC.

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

hoặc bạn có thể thích:

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

4
return 1 << $a << $b << $c >= 1 << 2;

Không thấy câu trả lời của Suvega trước khi đưa ra điều này, gần giống như vậy.
Kevin

Điều này thực sự làm việc? Tôi cho rằng đây là PHP, nhưng tôi không có quyền truy cập vào nó, nhưng tôi sẽ hỏi bạn: điều gì xảy ra nếu $ a bằng 0?
Đánh dấu Edgar

@Mark Nó thực sự không hoạt động nếu $ a bằng 0. Đó là một sự giám sát. Cảm ơn đã chỉ ra rằng. :)
Kevin

4

Cách đơn giản nhất (IMO) không khó hiểu và dễ đọc:

// Three booleans, check if two or more are true

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

Về mặt chức năng, nó là như nhau. Về mặt cú pháp, nó giúp dễ đọc hơn cho những người không quen sử dụng toán tử điều kiện dấu hỏi. Tôi sẵn sàng đặt cược nhiều người biết cách sử dụng toán tử AND và OR hơn số người biết cách sử dụng các toán tử có điều kiện đánh dấu câu hỏi. Câu hỏi ban đầu yêu cầu một "câu trả lời được cải thiện" .. Câu trả lời được chấp nhận không đơn giản hóa câu trả lời, nhưng đặt ra một câu hỏi rất thú vị về những gì người ta xem xét cải tiến. Bạn có lập trình cho dễ đọc phổ quát hoặc đơn giản? Đối với tôi, đây là một cải tiến so với câu trả lời được chấp nhận :)
abelito

Sở thích cá nhân. Đối với tôi, cách dễ dàng hơn để hiểu toán tử ternary sạch hơn giải pháp này.
nico

1
Ah yeah, tôi đã thấy vấn đề này và đã tự hỏi tại sao không ai khác đề cập đến giải pháp này. Nếu bạn viết logic của OP dưới dạng đại số boolean, bạn sẽ nhận được A B + A C + B C, có năm thao tác. Theo thuộc tính kết hợp, bạn có thể viết A * (B + C) + B C, có bốn thao tác.
Sông Vivian

Nó giống như câu trả lời của Jack (ngày 19 tháng 6) (C && (A || B)) || (A && B)vừa thay đổi tên * biến '...
user85421

4

Một cách giải thích theo nghĩa đen sẽ hoạt động trong tất cả các ngôn ngữ chính:

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

Nhưng tôi có lẽ sẽ giúp mọi người đọc dễ dàng hơn và có thể mở rộng ra hơn ba - điều mà dường như bị nhiều lập trình viên lãng quên:

boolean testBooleans(Array bools)
{
     int minTrue = ceil(bools.length * .5);
     int trueCount = 0;

     for(int i = 0; i < bools.length; i++)
     {
          if(bools[i])
          {
               trueCount++;
          }
     }
     return trueCount >= minTrue;
}

4

Là một bổ sung cho bài đăng tuyệt vời của @TofuBeer TofuBer'ser, hãy xem xét câu trả lời của @pdox pdox:

static boolean five(final boolean a, final boolean b, final boolean c)
{
    return a == b ? a : c;
}

Cũng xem xét phiên bản tháo rời của nó như được đưa ra bởi "javap -c":

static boolean five(boolean, boolean, boolean);
  Code:
    0:    iload_0
    1:    iload_1
    2:    if_icmpne    9
    5:    iload_0
    6:    goto    10
    9:    iload_2
   10:    ireturn

Câu trả lời của pdox biên dịch thành mã byte ít hơn bất kỳ câu trả lời nào trước đó. Làm thế nào để thời gian thực hiện của nó so với những người khác?

one                5242 ms
two                6318 ms
three (moonshadow) 3806 ms
four               7192 ms
five  (pdox)       3650 ms

Ít nhất trên máy tính của tôi, câu trả lời của pdox chỉ nhanh hơn một chút so với câu trả lời của @moonshadow moonshadow, làm cho pdox trở thành tổng thể nhanh nhất (trên máy tính xách tay HP / Intel của tôi).



3

Có lẽ anh ta không tìm kiếm bất cứ thứ gì phức tạp như các toán tử so sánh bitwise (không phải là hỗn hợp thông thường nhưng với booleans, việc sử dụng các toán tử bitwise rất kỳ quặc) hoặc một cái gì đó rất tròn trịa như chuyển đổi sang int và tổng hợp chúng.

Cách trực tiếp và tự nhiên nhất để giải quyết vấn đề này là với một biểu thức như thế này:

a ? (b || c): (b && c)

Đặt nó trong một chức năng nếu bạn thích, nhưng nó không phức tạp lắm. Giải pháp ngắn gọn và hiệu quả.


3

Trong C:

return !!a + !!b + !!c >= 2;

Trên thực tế, câu trả lời này là sai, nó phải> = 2, vì bạn cần ít nhất hai booleans đúng, không chính xác là hai.
Paul Wagland

@Paul Wagland: Cảm ơn bạn đã bắt được.
Matt Joiner

@ergosys: Wth tôi đã trả lời hai lần?
Matt Tham gia
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.