Controlla se almeno due booleani su tre sono veri


579

Di recente un intervistatore mi ha posto questa domanda: date tre variabili booleane, a, b e c, ritornano vere se almeno due su tre sono vere.

La mia soluzione segue:

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

Ha detto che questo può essere ulteriormente migliorato, ma come?


170
Inline la dichiarazione di ritorno.
Finglas,

45
Sembra un'intervista "chi ha il QI più alto". Io fallirei.
Chris Dutrow,

79
atLeastTwo(iWantYou, iNeedYou, imEverGonnaLoveYou)
Andrew Grimm,

92
Perché le persone votano le domande più banali?
BlueRaja - Danny Pflughoeft il

46
Le domande che sono generali e di facile comprensione ottengono molti voti positivi. Domande molto specifiche e tecniche no.
Jay,

Risposte:


820

Invece di scrivere:

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

Scrivi:

return someExpression;

Per quanto riguarda l'espressione stessa, qualcosa del genere:

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

o questo (qualunque sia più facile da capire):

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

Esegue il test ae besattamente una volta, e cal massimo una volta.

Riferimenti


144
+1: bella soluzione al puzzle, ma speriamo di non vedere nulla di simile nel mondo reale :)
Juliet

124
@Juliet: Non lo so, penso che se questo fosse un requisito del mondo reale (con nomi di variabili reali) avrebbe letto abbastanza bene. Pensa return hasGoodAttendance ? (passedCoursework || passed Exam) : (passedCoursework && passedExam), mi sembra perfetto.
Andrzej Doyle,

18
Non penso che sembri negativo , ma se i requisiti nel dominio sono "almeno due", penso che sarebbe più facile da leggere atLeastTwo(hasgoodAttendance, passedCoursework, passedExam). L'idea di "almeno 2 bool sono veri" è abbastanza generica da meritare la propria funzione.
Ken,

17
@Lese: Chiedere il codice più micro-ottimizzato nelle interviste faccia a faccia è poco pratico, e oserei dire, inutile. Le micro-ottimizzazioni, quando guidate dalla necessità, sono guidate dai risultati della profilazione di runtime, non dagli istinti umani (che sono noti per essere terribili). Puoi certamente chiedere agli intervistati il ​​processo attraverso il quale ottimizzeresti ulteriormente; è più importante del risultato stesso.
poligenelubrificanti

17
L'operatore ternario è un linguaggio comune che dovresti essere in grado di leggere. Se non riesci a leggerlo, dovresti studiarlo finché non puoi. L'uso dell'operatore ternario non è qualcosa che considero "intelligente" in senso dispregiativo. Ma sì, lo metterei come il corpo di una chiamata di metodo se stai usando comunemente la logica "almeno due".
Stephen P,

494

Solo per il gusto di usare XOR per rispondere a un problema relativamente semplice ...

return a ^ b ? c : a

160
Wow, bella soluzione. Ma per me la sua versione invertita è più facile da capire: a == b? a: c
Rotsor,

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

4
Yay, .. XOR ha una cattiva stampa e raramente hai la possibilità di usarla.
EightyOne Unite,

19
@ Stimul8d forse perché, per i booleani, è lo stesso di! = Ma meno leggibile? Capire che è stato un momento eureka per me ...
Tikhon Jelvis,

2
Preferisco la forma puramente binaria: return ((a ^ b) & c) | (a & b). È privo di diramazioni (più veloce) e di facile lettura: (aob è vero e c è vero) oppure (aeb sono entrambi veri). Nota che (a | b) e (a ^ b) funzionano entrambi con questa formula.
flanglet

217

Perché non implementarlo letteralmente? :)

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

In C potresti semplicemente scrivere a+b+c >= 2(o !!a+!!b+!!c >= 2essere molto al sicuro).

In risposta al confronto di TofuBeer sul bytecode java, ecco un semplice test delle prestazioni:

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

Questo stampa quanto segue sul mio computer (eseguendo Ubuntu su Intel Core 2 + sun java 1.6.0_15-b03 con HotSpot Server VM (14.1-b02, modalità mista)):

Prima e seconda iterazione:

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

Iterazioni successive:

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

Mi chiedo cosa potrebbe fare Java VM che degrada le prestazioni nel tempo per il caso (a + b + c> = 2).

Ed ecco cosa succede se eseguo java con uno -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

Mistero...

E se lo eseguo in GNU Java Interpreter , diventa quasi 100 volte più lento, ma la a&&b || b&&c || a&&cversione vince allora.

Risultati da Tofubeer con l'ultimo codice che esegue 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

Risultati di Paul Wagland con un 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: questo non funziona con i negativi, giusto? Potrebbe essere necessario fare la !!acosa, non sono sicuro.
poligenilubrificanti,

8
<s> -1. Non dovresti mai farlo per C. Non sai quale sia il valore di true (potrebbe essere altrettanto facilmente -1). </s> In realtà immagino che C99 includa nel suo standard che true è definito come 1. Ma Non lo farei ancora.
Mark Peters,

1
È possibile se il tuo input è il risultato di operazioni booleane? Ed è possibile per il tipo "bool" in C ++?
Rotsor,

2
@Rotsor: nessuno ha detto che l'input deve essere il risultato di operazioni booleane. Anche senza aspetti negativi stai giocando con il fuoco, come se lo definissi come 2 la tua condizione avrebbe falsi positivi. Ma non mi interessa tanto quanto non mi piace l'idea di mescolare i booleani in aritmetica. La tua soluzione Java è chiara in quanto non si basa su conversioni sfumate da booleano a un tipo intero.
Mark Peters,

7
Fai

143

Questo tipo di domande può essere risolto con una mappa di Karnaugh :

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

da cui si deduce che è necessario un gruppo per la prima riga e due gruppi per la prima colonna, ottenendo la soluzione ottimale di poligenelubrificanti:

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

10
@Justin, La mappa di Karnaugh ha ridotto il numero di operazioni logiche da 3 AND e 2 OR a 2 AND e 2 OR. @ Jack, grazie per avermi ricordato dell'esistenza della Karnaugh Map.
Tachy,

14
+1 per qualcosa di nuovo. Le mie prossime specifiche funzionali includeranno una K-map, che ne abbia bisogno o meno.
Justin R.,

2
Forse la scarsa leggibilità può essere compensata da (1) la tabella appropriata in commento e (2) il test unitario appropriato ... +1 per qualcosa di utile appreso a scuola.
Moala,

140

La leggibilità dovrebbe essere l'obiettivo. Qualcuno che legge il codice deve capire immediatamente il tuo intento. Quindi ecco la mia soluzione.

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

return howManyBooleansAreTrue >= 2;

21
Concordo con la premessa, ma (a && b) || (b && c) || (a && c) è molto più leggibile della tua soluzione IMHO.
Adrian Grigore,

62
Hmm, ora ho bisogno di una versione "due su QUATTRO booleani" ... la versione di Danatel è molto più semplice ora.
Arafangion,

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

5
@retronym: Hmm, no. Il modo Java funziona perfettamente in Scala ed è sia più leggibile che più efficiente.
Seun Osewa,

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

Spiegazione:

Se a==b, allora entrambi sono veri o entrambi sono falsi. Se entrambi sono veri, abbiamo trovato i nostri due veri booleani e possiamo restituire vero (ritornando a). Se entrambi sono falsi, non possono esserci due veri booleani anche se cè vero, quindi restituiamo false (ritornando a). Questa è la (a==b) ? aparte. Che dire : c? Bene, se a==bè falso, allora esattamente uno ao bdeve essere vero, quindi abbiamo trovato il primo vero booleano, e l'unica cosa che rimane è se cè anche vero, quindi torniamo ccome risposta.


8
c non è mai nemmeno testato ... geniale!
CurtainDog,

Usa una relazione transitiva di uguaglianza e il fatto che un booleano sia vero o falso +1
Christophe Roussy,

3
Così elegante! Ho dovuto controllare con carta e penna per crederci :) Complimenti, signore!
Adrian,

3
Penso a questo come "se ae bd'accordo, hanno il voto della maggioranza, quindi andate con qualunque cosa, altrimenti non sono d'accordo, così cè il voto decisivo"
Ben Millwood,

34

Non è necessario utilizzare le forme di corto circuito degli operatori.

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

Questo esegue lo stesso numero di operazioni logiche della tua versione, tuttavia è completamente senza rami.


11
Perché dovresti forzare 5 valutazioni quando 1 potrebbe fare? In realtà non esegue lo stesso numero di operazioni logiche. In effetti, avrebbe sempre funzionato di più.
Mark Peters,

2
Penso che mescolare l'aritmetica binaria e l'aritmetica booleana sia una cattiva idea. È come guidare le viti nel muro con una chiave inglese. La cosa peggiore è che hanno una semantica diversa.
Peter Tillemans,

12
@Mark - potrebbe essere più veloce ... a seconda dell'effetto di una previsione del ramo errata sulla pipeline della CPU. Tuttavia, è meglio lasciare tali micro-ottimizzazioni al compilatore JIT.
Stephen C,

4
Va bene fare qualcosa del genere in Java (o in qualsiasi altra lingua) ... con un paio di avvertenze: 1) deve essere più veloce (in questo caso, credo di si, vedi la mia seconda risposta) 2) preferibile significativamente più veloce (non sono sicuro che lo sia), 3) soprattutto documentato poiché è "dispari". Finché serve a uno scopo ed è documentato, va bene "infrangere le regole" quando ha senso.
TofuBeer,

11
@Peter Tillemans Non c'è mescolamento con operatori binari, in Java si tratta di operatori booleani.
Starblue,

27

Ecco un approccio generale basato sui test. Non "efficiente" come la maggior parte delle soluzioni finora offerte, ma chiaro, testato, funzionante e generalizzato.

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
Caspita, non ho mai visto un metodo completamente testato prima di vederlo.
Rotsor,

51
Personalmente trovo questo codice orribile, per tanti motivi. Non ho intenzione di sottovalutare, ma se mai lo vedessi nel codice di produzione, imprecerei. Un'operazione booleana estremamente semplice non deve essere complicata in questo modo.
CaptainCasey,

10
Sarei molto interessato a conoscere i tuoi motivi, @CaptainCasey. Penso che questo sia un codice abbastanza buono. C'è una bella funzione generalizzata che è facile da capire, facile da verificare e una funzione specifica che ne approfitta, anche facile da capire e da verificare. Nel mondo reale, li renderei pubblici e li metterei in un'altra classe; a parte questo, sarei felice di mettere in produzione questo codice. Oh, sì, rinominerei countBooleans () in countTrue ().
Carl Manaster,

5
se non si tratta di prestazioni, questa soluzione sembra quasi perfetta per me: molto facile da leggere ed estensibile. Questo è esattamente ciò per cui sono fatti var-args.
atamanroman il

7
Che diavolo, gente? Questo è un codice chiaro e ben testato, e l'unica ragione per cui sembra molto è perché include i test. A +++, voterebbe di nuovo.
Christoffer Hammarström,

24

Riassumilo. Si chiama algebra booleana per un motivo:

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

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

Se guardi le tabelle della verità lì, puoi vedere che la moltiplicazione è booleana e, e semplicemente l'aggiunta è xor.

Per rispondere alla tua domanda:

return (a + b + c) >= 2

2
Questa è la soluzione più elegante, secondo me.
Torbjørn Kristoffersen,

9
Errore da
principiante

13
Solo che il tag sul post dice "Java", e non puoi scrivere "a + b + c" quando sono definiti come booleani in Java.
Jay,

Per lavorare in Java, dovrebbe essere return ((a?1:0) + (b?1:0) + (c?1:0)) >= 2.
David R Tribble,

Duh, l'ho votato perché pensavo fosse una domanda C ++ ... perché sto leggendo le domande java? : /
Carlo Wood

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

15

Dipende davvero da cosa intendi per "migliorato":

Più chiaro?

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

Più generico?

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

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

        if(count > x) return true;
    }

    return false;
}

Più scalabile?

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

Più veloce?

// Only profiling can answer this.

Quale è "migliorato" dipende fortemente dalla situazione.


14

Ecco un'altra implementazione che utilizza map / ridurre. Ciò si adatta bene a miliardi di booleani © in un ambiente distribuito. Utilizzare MongoDB:

Creazione di un database valuesdi booleani:

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

Creazione della mappa, riduzione delle funzioni:

Modifica : mi piace la risposta di CurtainDog sull'applicare / ridurre la mappa agli elenchi generici, quindi ecco una funzione della mappa che accetta un callback che determina se un valore deve essere contato o meno.

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

Esecuzione della mappa / riduzione:

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: tanto quanto adoro la M / R e l'esposizione che Google ha recentemente dato ad essa (anche se non è la vera M / R da FP), tenderei a chiamare una stronzata sulla tua risposta. Esistono miliardi e miliardi di righe di codice che eseguono "cose" del mondo reale in cui non è utilizzata una sola riga di mappa / riduzione. Qualcuno che risponde a una domanda del genere con questo è sicuramente segnalato nel mio libro come: "provare a suonare la smartie" . Per non parlare del fatto che la maggior parte degli intervistatori non sarebbe in grado di dire se stai cercando di farli arrabbiare o no perché in realtà non hanno mai scritto un singolo programma usando M / R nella loro carriera.
SyntaxT3rr0r

2
@Syntax - Tutti hanno diritto alla loro opinione. La mia risposta è solo un altro approccio per esaminare il problema. Certo, sembra esagerato per 3 valori booleani, ma ciò non significa che sto cercando di essere i pantaloni intelligenti qui. Questo è un approccio comune alla risoluzione dei problemi che tutti usano: suddividere il problema in piccoli pezzi. Ecco come funziona l'induzione matematica, è così che funzionano la maggior parte degli algoritmi ricorsivi, ed è così che le persone risolvono i problemi in generale.
Anurag,

13

Prendendo le risposte (finora) qui:

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

e eseguendoli attraverso il decompilatore (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
}

Puoi vedere che?: Quelli sono leggermente migliori della versione fissa del tuo originale. Quello che è il migliore è quello che evita del tutto la ramificazione. Ciò è positivo dal punto di vista del minor numero di istruzioni (nella maggior parte dei casi) e migliore per le parti di previsione del ramo della CPU, poiché un'ipotesi errata nella previsione del ramo può causare lo stallo della CPU.

Direi che quello più efficiente è quello di Moonshadow in generale. Utilizza in media il minor numero di istruzioni e riduce la possibilità di blocchi della pipeline nella CPU.

Per essere sicuri al 100% dovresti scoprire il costo (nei cicli della CPU) per ogni istruzione, che purtroppo non è prontamente disponibile (dovresti cercare l'origine dell'hotspot e quindi le specifiche dei fornitori di CPU per il tempo preso per ogni istruzione generata).

Vedere la risposta aggiornata di Rotsor per un'analisi di runtime del codice.


5
Stai solo guardando il bytecode. Per quanto ne sai, JIT prenderà una versione con rami nel bytecode e la trasformerà in una versione senza rami nel codice nativo. Ma si tende a pensare che un minor numero di rami nel bytecode sarebbe meglio.
David Conrad,

13

Un altro esempio di codice diretto:

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

Non è il codice più succinto, ovviamente.

appendice

Un'altra versione (leggermente ottimizzata) di questo:

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

Questo potrebbe essere leggermente più veloce, supponendo che il confronto con 0 userà un codice più veloce (o forse meno) rispetto al confronto con 2.


+1 @Loadmaster, mi dispiace ma ti sbagli! Questa è la risposta più concisa qui. (cioè brevemente E chiaramente espresso);)
Ash

Micro-ottimizzazione: ++nè più veloce di n++ perché devi crearne un'altra copia prima di fare l'incremento .
M. Mimpen,

@ M.Mimpen: solo per oggetti di classe. Per i tipi primitivi (come nsopra), qualsiasi compilatore decente compilerà ogni ++operazione in un'unica istruzione CPU, sia pre che post.
David R Tribble,

12

Ancora un altro modo per farlo, ma non molto buono:

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

I Booleanvalori di hashcode sono fissi a 1231 per true e 1237 per false, quindi avrebbe potuto essere utilizzato allo stesso modo<= 3699


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

12

La serie più ovvia di miglioramenti sono:

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

e poi

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

Ma questi miglioramenti sono minori.


10

Non mi piace il ternario ( return a ? (b || c) : (b && c);dalla risposta in alto) e non credo di aver visto nessuno menzionarlo. È scritto così:

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

8

In Clojure :

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

Uso:

(at-least 2 true false true)

2
+1 La grande versione generica mostra il potere dei Lisps. Grazie,
dsmith il

6

Non credo di aver ancora visto questa soluzione:

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

Il suo vantaggio è che una volta raggiunto il numero che stai cercando, si rompe. Quindi se questo fosse "almeno 2 su 1.000.000 di valori sono veri" dove i primi due sono effettivamente veri, allora dovrebbe andare più veloce di alcune delle soluzioni più "normali".


Probabilmente dovrebbe essere: if (++ counter == howMany) invece di incrementare e quindi controllare separatamente.
Joe Enos,

2
O ancora più breve: if (b && (++ counter == howMany))
Joe Enos,

1
Lo farei, boolean ... boolValuesè più facile da chiamare, ma prende ancora un array
Stephen

Non sono aggiornato sul mio Java - non sapevo che esistesse. Una specie di strana sintassi, ma è utile - ogni tanto lo faccio in C # (parola chiave params), e rende le cose più belle da chiamare. Oppure, non conosco Java, ma in .NET, gli array e tutte le raccolte implementano IEnumerable <T>, quindi probabilmente userò qualunque equivalente di Java.
Joe Enos,

Come si confronta la prestazione di questo con l'esempio 2of3? restituire un? (b || c): (b && c);
Iain Sproat,

6

Possiamo convertire i bool in numeri interi ed eseguire questo semplice controllo:

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

6

Poiché non è stato specificato come migliorare il codice, cercherò di migliorare il codice rendendolo più divertente. Ecco la mia soluzione:

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

Nel caso qualcuno si stia chiedendo se questo codice funziona, ecco una semplificazione usando la stessa logica:

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; 

}

Questo può essere ridotto ulteriormente a quanto segue:

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

Ma ora non è più divertente.


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

Troppi modi per farlo ...


3
Assomiglia di più a C #. Questo dovrebbe essere menzionato come tale nella risposta poiché la domanda è indirizzata a Java :)
BalusC

5

Soluzione AC.

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

o potresti preferire:

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

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

Non ho visto la risposta di Suvega prima di posare questo, praticamente la stessa cosa.
Kevin,

Funziona davvero? Presumo che questo sia PHP, ma non ho accesso ad esso, ma ti chiederò semplicemente: cosa succede se $ a è 0?
Mark Edgar,

@Mark In realtà non funziona se $ a è 0. Questa è stata una svista. Grazie per la segnalazione. :)
Kevin,

4

Il modo più semplice (IMO) che non è confuso e facile da leggere:

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

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

Funzionalmente, è lo stesso. Sintatticamente, rende più semplice la lettura per chi non è abituato all'uso dell'operatore condizionale punto interrogativo. Sono disposto a scommettere che più persone sanno usare gli operatori AND e OR rispetto al numero di persone che sanno usare gli operatori condizionali del punto interrogativo. La domanda originale richiede una "risposta migliorata". La risposta accettata semplifica la risposta, ma solleva una domanda molto interessante di ciò che si considera miglioramento. Programmate per una leggibilità universale o per semplicità? Per me, questo è un miglioramento rispetto alla risposta accettata :)
abelito,

Preferenze personali. Per me è molto più facile capire l'operatore ternario più pulito di questa soluzione.
nico,

1
Ah sì, ho visto questo problema e mi chiedevo perché nessun altro avesse menzionato questa soluzione. Se scrivi la logica del PO come algebra booleana, otterrai A B + A C + B C, che ha cinque operazioni. Dalla proprietà associativa, è possibile scrivere A * (B + C) + B C, che ha quattro operazioni.
Fiume Vivian,

È la stessa della risposta di Jack (19 giugno) che ha (C && (A || B)) || (A && B)appena cambiato i nomi * variabili` ...
user85421

4

Un'interpretazione letterale funzionerà in tutte le principali lingue:

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

Ma probabilmente renderei più facile la lettura delle persone ed espandibile a più di tre, qualcosa che sembra essere dimenticato da molti programmatori:

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

In aggiunta all'eccellente post di TofuBeer di TofuBeer, considera la risposta di pdox di @pdox:

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

Considera anche la sua versione disassemblata data da "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

La risposta di pdox viene compilata con un codice byte inferiore rispetto a una delle risposte precedenti. In che modo i suoi tempi di esecuzione si confrontano con gli altri?

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

Almeno sul mio computer, la risposta di pdox è solo leggermente più veloce della risposta di moonshadow di @moonshadow, rendendo pdox il più veloce in assoluto (sul mio laptop HP / Intel).


3

In Ruby:

[a, b, c].count { |x| x } >= 2

Quale potrebbe essere eseguito in JRuby su JavaVM. ;-)


3

Probabilmente non è alla ricerca di qualcosa di contorto come operatori di confronto bit a bit (non normalmente contorto ma con valori booleani, è estremamente strano usare operatori bit a bit) o ​​qualcosa di molto rotondo come la conversione in int e la loro somma.

Il modo più diretto e naturale per risolverlo è con un'espressione come questa:

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

Mettilo in una funzione se preferisci, ma non è molto complicato. La soluzione è logicamente concisa ed efficiente.


3

In C:

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

In realtà, questa risposta è sbagliata ... dovrebbe essere> = 2, poiché hai bisogno di almeno due veri booleani, non esattamente due.
Paul Wagland,

@ Paul Wagland: grazie per la cattura.
Matt Joiner,

@ergosys: con quale risposta ho risposto due volte?
Matt Joiner,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.