Periksa apakah setidaknya dua dari tiga boolean benar


579

Seorang pewawancara baru-baru ini bertanya kepada saya pertanyaan ini: diberi tiga variabel boolean, a, b, dan c, kembalikan benar jika setidaknya dua dari tiga itu benar.

Solusi saya sebagai berikut:

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

Dia mengatakan bahwa ini dapat ditingkatkan lebih lanjut, tetapi bagaimana caranya?


170
Sebariskan pernyataan kembali.
Finglas

45
Kedengarannya seperti wawancara "yang memiliki IQ tertinggi". Saya akan gagal.
Chris Dutrow

79
atLeastTwo(iWantYou, iNeedYou, imEverGonnaLoveYou)
Andrew Grimm

92
Mengapa orang memilih pertanyaan yang paling sepele?
BlueRaja - Danny Pflughoeft

46
Pertanyaan yang umum dan mudah dimengerti mendapatkan banyak suara. Pertanyaan yang sangat spesifik dan teknis tidak.
Jay

Jawaban:


820

Daripada menulis:

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

Menulis:

return someExpression;

Adapun ekspresi itu sendiri, kira-kira seperti ini:

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

atau ini (mana yang menurut Anda lebih mudah dipahami):

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

Tes adan btepat sekali, dan cpaling banyak sekali.

Referensi


144
+1: solusi yang bagus untuk teka-teki, tapi mudah-mudahan kita tidak melihat hal seperti ini di dunia nyata :)
Juliet

124
@ Juliet: Saya tidak tahu, saya pikir jika ini adalah persyaratan dunia nyata (dengan nama variabel nyata) itu akan membaca dengan cukup baik. Pertimbangkan return hasGoodAttendance ? (passedCoursework || passed Exam) : (passedCoursework && passedExam), itu terlihat baik bagi saya.
Andrzej Doyle

18
Saya tidak berpikir itu terlihat buruk , tetapi jika persyaratan dalam domain dipahami sebagai "setidaknya dua", saya pikir itu akan lebih mudah dibaca atLeastTwo(hasgoodAttendance, passedCoursework, passedExam). Gagasan "setidaknya 2 bools benar" cukup generik sehingga layak digunakan.
Ken

17
@Lese: Meminta kode yang paling dioptimalkan secara mikro dalam wawancara tatap muka tidak praktis, dan berani saya katakan, tidak berguna. Mikro-optimasi, ketika didorong oleh kebutuhan, dipandu oleh hasil profil runtime, bukan naluri manusia (yang dikenal mengerikan). Anda tentu dapat menanyakan kepada orang yang diwawancarai proses di mana Anda akan mengoptimalkan ini lebih jauh; itu lebih penting daripada hasilnya sendiri.
polygenelubricants

17
Operator ternary adalah idiom umum yang harus Anda baca. Jika Anda tidak bisa membacanya, Anda harus mempelajarinya sampai Anda bisa. Penggunaan operator ternary bukanlah sesuatu yang saya anggap "pintar" dalam arti yang merendahkan. Tapi ya, saya akan menempatkan ini sebagai tubuh pemanggilan metode jika Anda biasanya menggunakan logika "setidaknya dua".
Stephen P

494

Hanya demi menggunakan XOR untuk menjawab masalah yang relatif lurus ke depan ...

return a ^ b ? c : a

160
Wow, solusi keren. Tetapi bagi saya versinya yang terbalik lebih mudah dipahami: a == b? a: c
Rotsor

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

4
Yay, .. XOR mendapat pers yang buruk dan Anda jarang mendapatkan kesempatan untuk menggunakannya.
EightyOne Unite

19
@ Stimul8d mungkin karena, untuk boolean, itu sama dengan! = Tapi kurang bisa dibaca? Mencari tahu itu adalah momen eureka bagi saya ...
Tikhon Jelvis

2
Saya lebih suka bentuk biner murni: return ((a ^ b) & c) | (a & b). Ini tidak bercabang (lebih cepat) dan mudah dibaca: (a atau b benar dan c benar) atau (a dan b keduanya benar). Perhatikan bahwa (a | b) dan (a ^ b) keduanya bekerja dengan rumus ini.
flanglet

217

Mengapa tidak menerapkannya secara harfiah? :)

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

Dalam C Anda bisa menulis a+b+c >= 2(atau !!a+!!b+!!c >= 2menjadi sangat aman).

Menanggapi perbandingan TofuBeer tentang bytecode java, berikut ini adalah tes kinerja sederhana:

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

Ini mencetak yang berikut ini di mesin saya (menjalankan Ubuntu pada Intel Core 2 + sun java 1.6.0_15-b03 dengan HotSpot Server VM (14.1-b02, mode campuran)):

Iterasi pertama dan kedua:

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

Iterasi selanjutnya:

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

Saya bertanya-tanya, apa yang bisa dilakukan java VM yang menurunkan kinerja seiring waktu untuk kasus (a + b + c> = 2).

Dan inilah yang terjadi jika saya menjalankan java dengan -clientswitch 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

Misteri...

Dan jika saya menjalankannya di GNU Java Interpreter , ia menjadi hampir 100 kali lebih lambat, tetapia&&b || b&&c || a&&c versi itu yang menang.

Hasil dari Tofubeer dengan kode terbaru menjalankan 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

Hasil dari Paul Wagland dengan 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: ini tidak bekerja dengan negatif, bukan? Anda mungkin harus melakukan !!ahal itu, saya tidak yakin.
polygenelubricants

8
<s> -1. Anda tidak boleh melakukan itu untuk C. Anda tidak tahu apa nilai true (itu bisa dengan mudah menjadi -1). </s> Sebenarnya saya kira C99 termasuk dalam standarnya bahwa true didefinisikan sebagai 1. Tapi Saya masih tidak akan melakukan ini.
Mark Peters

1
Apakah itu mungkin jika input Anda adalah hasil dari operasi boolean? Dan apakah itu mungkin untuk tipe "bool" dalam C ++?
Rotsor

2
@Rotsor: Tidak ada yang mengatakan input harus hasil dari operasi boolean. Bahkan tanpa negatif Anda bermain api, seolah-olah Anda mendefinisikannya sebagai 2 kondisi Anda akan memiliki positif palsu. Tetapi saya tidak peduli tentang hal itu sama seperti saya tidak menyukai gagasan untuk memasukkan boolean ke dalam aritmatika. Solusi Java Anda jelas karena tidak bergantung pada konversi bernuansa dari boolean ke tipe integer.
Mark Peters

7
Berhati-hatilah dengan microbenchmarks: java.sun.com/docs/hotspot/HotSpotFAQ.html#benchmarking_simple
BalusC

143

Pertanyaan semacam ini dapat diselesaikan dengan Peta Karnaugh :

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

dari mana Anda menyimpulkan bahwa Anda memerlukan grup untuk baris pertama dan dua grup untuk kolom pertama, untuk mendapatkan solusi optimal dari poligenelegricants:

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

10
@Justin, Peta Karnaugh mengurangi jumlah operasi logis dari 3 AND dan 2 OR menjadi 2 AND dan 2 OR. @Jack, Terima kasih telah mengingatkan saya tentang keberadaan Peta Karnaugh.
Tachy

14
+1 untuk sesuatu yang baru. Spek fungsional saya berikutnya akan menyertakan K-map, apakah itu perlu atau tidak.
Justin R.

2
Mungkin keterbacaan yang buruk dapat dikompensasi dengan (1) tabel yang sesuai dalam komentar dan (2) tes unit yang sesuai ... +1 untuk sesuatu yang berguna yang dipelajari di sekolah.
moala

140

Keterbacaan harus menjadi tujuannya. Seseorang yang membaca kode harus segera memahami maksud Anda. Jadi, inilah solusi saya.

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

return howManyBooleansAreTrue >= 2;

21
Saya setuju dengan premis, tetapi (a && b) || (b && c) || (a && c) jauh lebih mudah dibaca daripada IMHO solusi Anda.
Adrian Grigore

62
Hmm, sekarang aku butuh versi "dua dari EMPAT boolean" ... versi danatel jauh lebih mudah sekarang.
Arafangion

6
Atau di Scala:Seq(true, true, false).map(if (_) 1 else 0).sum >= 2
retronym

5
@retronym: Hmm, tidak. Cara Java berfungsi dengan baik di Scala dan keduanya lebih mudah dibaca dan lebih efisien.
Seun Osewa

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

Penjelasan:

Jika a==b, maka keduanya benar atau keduanya salah. Jika keduanya benar, kami telah menemukan dua boolean sejati kami, dan dapat mengembalikan true (dengan kembali a). Jika keduanya salah tidak mungkin ada dua boolean sejati bahkan jika citu benar, jadi kami mengembalikan false (dengan mengembalikan a). Itu (a==b) ? abagiannya. Bagaimana dengan : c? Baik jika a==bsalah, maka tepat satu aatau bharus benar, jadi kami telah menemukan boolean pertama yang benar, dan satu-satunya yang tersisa yang penting adalah jika cjuga benar, jadi kami kembali csebagai jawabannya.


8
c bahkan tidak pernah diuji ... brilian!
CurtainDog

Menggunakan hubungan transitif persamaan dan fakta bahwa boolean benar atau salah +1
Christophe Roussy

3
Sangat elegan! Saya harus memeriksa dengan pena dan kertas untuk mempercayainya :) Salam kepada Anda, Tuan!
Adrian

3
Saya berpikir tentang ini sebagai "jika adan bsetuju, mereka memiliki suara mayoritas, jadi pergilah dengan apa pun itu, jika tidak, mereka tidak setuju, demikian cjuga suara yang menentukan"
Ben Millwood

34

Anda tidak perlu menggunakan bentuk hubungan pendek dari operator.

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

Ini melakukan jumlah operasi logika yang sama dengan versi Anda, namun benar-benar tanpa cabang.


11
Mengapa Anda ingin memaksakan 5 evaluasi ketika saya bisa melakukannya? Ini benar-benar tidak melakukan jumlah operasi logika yang sama dalam kebenaran. Bahkan, ia akan selalu tampil lebih baik.
Mark Peters

2
Saya pikir mencampur aritmatika biner dan aritmatika boolean adalah ide yang buruk. Itu seperti sekrup penggerak di dinding dengan kunci inggris. Yang terburuk dari semuanya adalah mereka memiliki semantik yang berbeda.
Peter Tillemans

12
@ Mark - mungkin lebih cepat ... tergantung pada efek prediksi cabang yang salah pada pipa CPU. Namun, yang terbaik adalah menyerahkan optimasi mikro tersebut ke kompiler JIT.
Stephen C

4
Tidak apa-apa untuk melakukan hal seperti ini di Jawa (atau bahasa lain) ... dengan beberapa peringatan: 1) perlu lebih cepat (dalam hal ini, saya percaya ini, lihat jawaban kedua saya) 2) lebih disukai secara signifikan lebih cepat (tidak yakin apakah itu), 3) yang paling penting didokumentasikan karena "aneh". Selama ini memiliki tujuan dan didokumentasikan, tidak apa-apa untuk "melanggar aturan" ketika itu masuk akal.
TofuBeer

11
@ Peter Tillemans Tidak ada pencampuran dengan operator biner, di Jawa ini adalah operator boolean.
starblue

27

Inilah pendekatan umum yang digerakkan oleh tes. Bukan sebagai "efisien" karena sebagian besar solusi sejauh ini ditawarkan, tetapi jelas, diuji, bekerja, dan digeneralisasi.

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
Wow, saya belum pernah melihat metode yang sepenuhnya diuji sebelum melihat yang satu ini.
Rotsor

51
Saya pribadi menemukan kode ini mengerikan, karena banyak alasan. Saya tidak akan menurunkan suara, tetapi jika saya pernah melihat ini dalam kode produksi saya akan mengutuk. Operasi boolean yang sangat sederhana tidak perlu rumit seperti ini.
CaptainCasey

10
Saya akan sangat tertarik untuk mengetahui alasan Anda, @CaptainCasey. Saya pikir ini kode yang cukup bagus. Ada fungsi umum yang bagus yang mudah dimengerti, mudah diverifikasi, dan fungsi khusus yang memanfaatkannya, juga mudah dimengerti & diverifikasi. Di dunia nyata, saya akan mempublikasikannya dan menempatkan mereka di kelas lain; selain itu - saya dengan senang hati akan membuat kode ini dalam produksi. Oh - yeah - Saya akan mengganti nama countBooleans () menjadi countTrue ().
Carl Manaster

5
jika ini bukan tentang kinerja, solusi ini terlihat hampir sempurna untuk saya: sangat mudah dibaca dan dapat diperpanjang. Itulah tepatnya tujuan var-args dibuat.
atamanroman

7
Apa-apaan, teman-teman? Ini adalah kode yang jelas dan teruji dengan baik, dan satu-satunya alasan sepertinya banyak adalah karena itu termasuk tes. A +++, akan berubah lagi.
Christoffer Hammarström

24

Jumlahkan. Ini disebut aljabar boolean karena suatu alasan:

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

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

Jika Anda melihat tabel kebenaran di sana, Anda dapat melihat bahwa perkalian adalah boolean dan, dan tambahannya adalah xor.

Untuk menjawab pertanyaan Anda:

return (a + b + c) >= 2

2
Ini adalah solusi paling elegan, menurut saya.
Torbjørn Kristoffersen

9
Kesalahan Rookie, nilai boolean BUKAN 0, itu tidak berarti selalu 1.
tomdemuyt

13
Kecuali bahwa tag pada posting mengatakan "Java", dan Anda tidak dapat menulis "a + b + c" ketika mereka didefinisikan sebagai boolean di Jawa.
Jay

Untuk bekerja di Jawa, itu harus return ((a?1:0) + (b?1:0) + (c?1:0)) >= 2.
David R Tribble

Duh, saya memilih ini karena saya pikir itu adalah pertanyaan C ++ ... mengapa saya membaca pertanyaan java? : /
Carlo Wood

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

15

Itu benar-benar tergantung apa yang Anda maksud dengan "ditingkatkan":

Lebih jelas?

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

Terser?

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

Lebih umum?

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

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

        if(count > x) return true;
    }

    return false;
}

Lebih scalable?

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

Lebih cepat?

// Only profiling can answer this.

Yang mana yang "diperbaiki" sangat bergantung pada situasinya.


14

Berikut implementasi lain menggunakan peta / kurangi. Ini berskala baik hingga miliaran boolean © dalam lingkungan terdistribusi. Menggunakan MongoDB:

Membuat basis data valuesboolean:

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

Membuat peta, mengurangi fungsi:

Sunting : Saya suka jawaban CurtainDog tentang memiliki peta / mengurangi berlaku untuk daftar umum, jadi inilah fungsi peta yang menerima panggilan balik yang menentukan apakah suatu nilai harus dihitung atau tidak.

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

Menjalankan peta / kurangi:

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: saya sangat suka M / R dan paparan yang diberikan Google baru-baru ini (meskipun bukan M / R yang benar dari FP), saya cenderung menyebut omong kosong! T pada jawaban Anda. Ada miliaran dan miliaran baris kode yang melakukan "barang" Dunia Nyata [TM] di mana tidak ada satu baris peta / pengurangan yang digunakan. Seseorang yang menjawab pertanyaan semacam ini dengan pasti ditandai dalam buku saya sebagai: "berusaha memainkan smartie" . Belum lagi sebagian besar pewawancara tidak akan bisa mengatakan apakah Anda mencoba omong kosong! T mereka atau tidak karena mereka sebenarnya tidak pernah menulis satu program menggunakan M / R dalam karir mereka.
SyntaxT3rr0r

2
@ Sintaks - Setiap orang berhak atas pendapat mereka. Jawaban saya hanyalah satu lagi pendekatan dalam memandang masalah. Tentu, kedengarannya berlebihan untuk 3 nilai boolean, tapi itu tidak berarti saya mencoba untuk menjadi orang pintar di sini. Ini adalah pendekatan umum untuk pemecahan masalah yang digunakan semua orang - memecah masalah menjadi beberapa bagian kecil. Begitulah cara kerja induksi matematika, itulah cara kerja sebagian besar algoritma rekursif, dan itulah cara orang memecahkan masalah secara umum.
Anurag

13

Mengambil jawaban (sejauh ini) di sini:

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

dan menjalankannya melalui 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
}

Anda dapat melihat bahwa?: Yang sedikit lebih baik daripada versi perbaikan dari sumber asli Anda. Salah satu yang terbaik adalah yang menghindari percabangan sama sekali. Itu bagus dari sudut pandang instruksi yang lebih sedikit (dalam banyak kasus) dan lebih baik untuk bagian prediksi cabang CPU, karena tebakan yang salah dalam prediksi cabang dapat menyebabkan kemacetan CPU.

Saya akan mengatakan yang paling efisien adalah yang dari moonshadow secara keseluruhan. Ini menggunakan instruksi paling sedikit rata-rata dan mengurangi kemungkinan warung pipa di CPU.

Untuk menjadi 100% yakin Anda perlu mengetahui biaya (dalam siklus CPU) untuk setiap instruksi, yang, sayangnya tidak tersedia (Anda harus melihat sumber untuk hotspot dan kemudian vendor CPU menentukan spesifikasi untuk waktu itu). diambil untuk setiap instruksi yang dihasilkan).

Lihat jawaban yang diperbarui oleh Rotsor untuk analisis runtime kode.


5
Anda hanya melihat bytecode. Untuk semua yang Anda tahu, JIT akan mengambil versi dengan cabang di bytecode dan mengubahnya menjadi versi tanpa cabang dalam kode asli. Tetapi orang akan cenderung berpikir bahwa lebih sedikit cabang dalam bytecode akan lebih baik.
David Conrad

13

Contoh lain dari kode langsung:

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

Jelas itu bukan kode yang paling ringkas.

Tambahan

Versi lain (sedikit dioptimalkan) ini:

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

Ini mungkin berjalan sedikit lebih cepat, dengan asumsi bahwa perbandingan terhadap 0 akan menggunakan kode yang lebih cepat (atau mungkin lebih sedikit) daripada perbandingan terhadap 2.


+1 @Loadmaster, saya minta maaf, tapi Anda salah! Ini adalah jawaban yang paling ringkas di sini. (Yaitu singkat DAN jelas diungkapkan);)
Ash

Optimalisasi mikro: ++nlebih cepat daripada n++ karena Anda harus membuat salinan lain sebelum melakukan penambahan .
M. Mimpen

@ M.Mimpen: Hanya untuk objek kelas. Untuk tipe primitif (seperti di natas), setiap kompiler yang layak akan mengkompilasi setiap ++operasi menjadi satu instruksi CPU, apakah itu pra atau posting.
David R Tribble

12

Namun cara lain untuk melakukan ini tetapi tidak terlalu bagus:

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

Nilai Booleankode hash ditetapkan pada 1231 untuk true dan 1237 untuk false sehingga bisa digunakan secara setara<= 3699


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

12

Set peningkatan yang paling jelas adalah:

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

lalu

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

Tetapi perbaikan itu kecil.


10

Saya tidak suka ternary ( return a ? (b || c) : (b && c);dari jawaban atas), dan saya tidak berpikir saya pernah melihat orang menyebutkannya. Itu ditulis seperti ini:

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

8

Di Clojure :

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

Pemakaian:

(at-least 2 true false true)

2
+1 Versi generik yang hebat menunjukkan kekuatan Lisps. Terima kasih,
dsmith

6

Saya rasa saya belum melihat solusi ini:

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

Keuntungannya adalah begitu mencapai angka yang Anda cari, itu rusak. Jadi, jika ini "setidaknya 2 dari 1.000.000 nilai ini benar" di mana dua yang pertama sebenarnya benar, maka itu harus lebih cepat daripada beberapa solusi yang lebih "normal".


Itu mungkin seharusnya: if (++ counter == howMany) alih-alih menambah dan kemudian memeriksa secara terpisah.
Joe Enos

2
Atau bahkan lebih pendek: if (b && (++ counter == howMany))
Joe Enos

1
Saya akan melakukannya boolean ... boolValueslebih mudah untuk menelepon, tetapi masih membutuhkan array
Stephen

Saya tidak up to date di Java saya - tidak tahu itu ada. Agak sintaks yang aneh, tetapi ini berguna - sesekali saya akan melakukannya di C # (kata kunci params), dan itu membuat hal-hal yang lebih baik untuk dipanggil. Atau, saya tidak tahu tentang Java, tetapi dalam. NET, array dan semua koleksi mengimplementasikan IEnumerable <T>, jadi saya mungkin akan menggunakan apa pun yang setara dengan Java.
Joe Enos

Bagaimana kinerja ini dibandingkan dengan contoh 2of3? mengembalikan a? (b || c): (b && c);
Iain Sproat

6

Kami dapat mengonversi bool menjadi bilangan bulat dan melakukan pemeriksaan mudah ini:

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

6

Karena tidak ditentukan bagaimana kode harus ditingkatkan, saya akan berusaha untuk memperbaiki kode dengan membuatnya lebih lucu. Inilah solusi saya:

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

Jika ada yang bertanya-tanya apakah kode ini berfungsi, berikut adalah penyederhanaan menggunakan logika yang sama:

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; 

}

Ini dapat diringkas lebih lanjut sebagai berikut:

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

Tapi sekarang tidak lucu lagi.


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

Terlalu banyak cara untuk melakukan ini ...


3
Terlihat lebih seperti C #. Ini harus disebutkan dalam jawaban karena pertanyaannya adalah Java-tertarget :)
BalusC

5

Solusi AC.

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

atau Anda mungkin lebih suka:

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

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

Tidak melihat jawaban Suvega sebelum mengajukan ini, hampir sama saja.
Kevin

Apakah ini berhasil? Saya berasumsi ini adalah PHP, tetapi saya tidak memiliki akses ke sana, tetapi saya hanya akan bertanya: apa yang terjadi jika $ a adalah 0?
Mark Edgar

@ Mark Sebenarnya tidak bekerja jika $ a adalah 0. Itu adalah kekhilafan. Terima kasih telah menunjukkannya. :)
Kevin

4

Cara paling sederhana (IMO) yang tidak membingungkan dan mudah dibaca:

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

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

Secara fungsional, itu sama. Secara sintaksis, ini memudahkan pembacaan bagi mereka yang tidak terbiasa dengan penggunaan operator tanda tanya. Saya berani bertaruh lebih banyak orang tahu bagaimana menggunakan operator AND dan OR daripada jumlah orang yang tahu bagaimana menggunakan operator tanda tanya bersyarat. Pertanyaan aslinya meminta "jawaban yang ditingkatkan". Jawaban yang diterima memang menyederhanakan jawaban, tetapi menimbulkan pertanyaan yang sangat menarik tentang apa yang dianggap perbaikan. Apakah Anda memprogram untuk keterbacaan universal atau untuk kesederhanaan? Bagi saya, ini merupakan peningkatan dari jawaban yang diterima :)
abelito

Preferensi pribadi. Bagi saya itu jauh lebih mudah untuk memahami operator ternary yang lebih bersih daripada solusi ini.
nico

1
Ah ya, saya melihat masalah ini dan bertanya-tanya mengapa tidak ada orang lain yang menyebutkan solusi ini. Jika Anda menuliskan logika OP sebagai aljabar boolean, Anda mendapatkan A B + A C + B C, yang memiliki lima operasi. Dengan properti asosiatif, Anda dapat menulis A * (B + C) + B C, yang memiliki empat operasi.
Sungai Vivian

Itu sama dengan jawaban Jack (19 Juni) (C && (A || B)) || (A && B)baru saja mengubah nama * variabel` ...
user85421

4

Interpretasi literal akan bekerja dalam semua bahasa utama:

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

Tapi saya mungkin akan membuat lebih mudah bagi orang untuk membaca, dan diperluas menjadi lebih dari tiga - sesuatu yang tampaknya dilupakan oleh banyak programmer:

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

Sebagai tambahan untuk postingan @TofuBeer TofuBeer yang luar biasa, pertimbangkan jawaban @pdox pdox:

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

Pertimbangkan juga versinya yang dibongkar seperti yang diberikan oleh "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

jawaban pdox mengkompilasi kode byte lebih sedikit daripada jawaban sebelumnya. Bagaimana waktu pelaksanaannya dibandingkan dengan yang lain?

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

Setidaknya di komputer saya, jawaban pdox hanya sedikit lebih cepat daripada jawaban @moonshadow moonshadow, menjadikan pdox yang tercepat secara keseluruhan (pada laptop HP / Intel saya).



3

Dia mungkin tidak mencari sesuatu yang berbelit-belit seperti operator perbandingan bitwise (biasanya berbelit-belit tetapi dengan boolean, sangat aneh untuk menggunakan operator bitwise) atau sesuatu yang sangat bundar seperti mengkonversi ke int dan menjumlahkannya.

Cara paling langsung dan alami untuk menyelesaikan ini adalah dengan ekspresi seperti ini:

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

Masukkan fungsi jika Anda mau, tetapi tidak terlalu rumit. Solusinya singkat dan efisien secara logis.


3

Dalam C:

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

Sebenarnya, jawaban ini salah ... seharusnya> = 2, karena Anda membutuhkan setidaknya dua boolean sejati, bukan dua.
Paul Wagland

@ Paul Wagland: Terima kasih atas tangkapannya.
Matt Joiner

@ergosys: Bagaimana saya menjawab dua kali?
Matt Joiner
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.