Compruebe si al menos dos de cada tres booleanos son verdaderos

579

Un entrevistador me hizo esta pregunta recientemente: dadas tres variables booleanas, a, byc, devuelven verdadero si al menos dos de los tres son verdaderos.

Mi solución sigue:

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

Dijo que esto se puede mejorar aún más, pero ¿cómo?

usuario282886
fuente
170
En línea la declaración de devolución.
Finglas
45
Suena como una entrevista de "quién tiene el coeficiente intelectual más alto". Yo fallaría
Chris Dutrow
79
atLeastTwo(iWantYou, iNeedYou, imEverGonnaLoveYou)
Andrew Grimm
92
¿Por qué la gente vota las preguntas más triviales?
BlueRaja - Danny Pflughoeft
46
Las preguntas que son generales y fáciles de entender obtienen muchos votos positivos. Preguntas que son muy específicas y técnicas no.
Jay

Respuestas:

820

En lugar de escribir:

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

Escribir:

return someExpression;

En cuanto a la expresión en sí, algo como esto:

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

o esto (lo que sea más fácil de entender):

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

Prueba ay bexactamente una vez, y ccomo máximo una vez.

Referencias

lubricantes poligénel
fuente
144
+1: solución encantadora para el rompecabezas, pero espero que no veamos algo así en el mundo real :)
Juliet
124
@Juliet: No sé, creo que si esto fuera un requisito del mundo real (con nombres de variables reales) se leería bastante bien. Considera return hasGoodAttendance ? (passedCoursework || passed Exam) : (passedCoursework && passedExam), eso me parece bien.
Andrzej Doyle
18
No creo que se vea mal , pero si se entiende que el requisito en el dominio es "al menos dos", creo que sería más fácil de leer atLeastTwo(hasgoodAttendance, passedCoursework, passedExam). La idea de "al menos 2 bools son verdaderas" es lo suficientemente genérica como para merecer su propia función.
Ken
17
@Lese: Pedir el código más micro-optimizado en las entrevistas cara a cara no es práctico, y me atrevo a decir que es inútil. Las microoptimizaciones, cuando son impulsadas por la necesidad, se guían por resultados de perfiles de tiempo de ejecución, no por instintos humanos (que se sabe que son terribles). Ciertamente, puede preguntar a los entrevistados el proceso mediante el cual optimizaría esto aún más; eso es más importante que el resultado en sí.
Polygenelubricants
17
El operador ternario es un idioma común que deberías poder leer. Si no puede leerlo, debe estudiarlo hasta que pueda. El uso del operador ternario no es algo que considero "inteligente" en el sentido despectivo. Pero sí, lo pondría como el cuerpo de una llamada a un método si está usando comúnmente la lógica de "al menos dos".
Stephen P
494

Solo por el uso de XOR para responder a un problema relativamente sencillo ...

return a ^ b ? c : a
Tim Stone
fuente
160
Wow, solución genial. Pero para mí su versión invertida es más fácil de entender: a == b? a: c
Rotsor
55
a ^ b? c: a ^ b? c: a ^ b? c: a
alexanderpas
44
Yay ... XOR tiene tan mala prensa y rara vez tienes la oportunidad de usarla.
EightyOne Unite
19
@ Stimul8d quizás porque, para booleanos, es lo mismo que! = Pero menos legible? Pensar que eso fue un momento eureka para mí ...
Tikhon Jelvis
2
Prefiero la forma puramente binaria: return ((a ^ b) & c) | (a y b). No tiene ramificaciones (más rápido) y es fácil de leer: (aob es verdadero yc es verdadero) o (a y b son verdaderos). Tenga en cuenta que (a | b) y (a ^ b) ambos funcionan con esta fórmula.
flanglet
217

¿Por qué no implementarlo literalmente? :)

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

En C podrías simplemente escribir a+b+c >= 2(o !!a+!!b+!!c >= 2para estar muy seguro).

En respuesta a la comparación de TofuBeer del código de bytes de Java, aquí hay una prueba de rendimiento simple:

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

Esto imprime lo siguiente en mi máquina (ejecutando Ubuntu en Intel Core 2 + sun java 1.6.0_15-b03 con HotSpot Server VM (14.1-b02, modo mixto)):

Primera y segunda iteraciones:

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

Iteraciones posteriores:

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

Me pregunto, ¿qué podría hacer Java VM que degrada el rendimiento con el tiempo para el caso (a + b + c> = 2).

Y esto es lo que sucede si ejecuto Java con un -clientconmutador 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

Misterio...

Y si lo ejecuto en GNU Java Interpreter , se vuelve casi 100 veces más lento, pero la a&&b || b&&c || a&&cversión gana.

Resultados de Tofubeer con el último código que ejecuta 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

Resultados de Paul Wagland con una 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
Rotsor
fuente
44
a+b+c >= 2: esto no funciona con negativos, ¿verdad? Puede que tenga que hacer la !!acosa, no estoy seguro.
Polygenelubricants
8
<s> -1. Nunca debe hacer eso por C. No sabe cuál es el valor de verdadero (podría ser fácilmente -1). </s> En realidad, supongo que C99 incluye en su estándar que verdadero se define como 1. Pero Todavía no haría esto.
Mark Peters
1
¿Es eso posible si su entrada es resultado de operaciones booleanas? ¿Y eso es posible para el tipo "bool" en C ++?
Rotsor
2
@Rotsor: Nadie dijo que la entrada tiene que ser el resultado de operaciones booleanas. Incluso sin negativos estás jugando con fuego, como si lo definieras como 2 tu condición tendría falsos positivos. Pero eso no me importa tanto como me disgusta la idea de mezclar booleanos en aritmética. Su solución Java es clara porque no se basa en conversiones matizadas de tipo booleano a entero.
Mark Peters
77
Tenga cuidado con los microbenchmarks: java.sun.com/docs/hotspot/HotSpotFAQ.html#benchmarking_simple
BalusC
143

Este tipo de preguntas se pueden resolver con un mapa de Karnaugh :

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

de lo cual deduce que necesita un grupo para la primera fila y dos grupos para la primera columna, obteniendo la solución óptima de poligenelubricantes:

(C && (A || B)) || (A && B)  <---- first row
       ^
       |
   first column without third case
Jack
fuente
10
@Justin, el mapa de Karnaugh redujo el número de operaciones lógicas de 3 AND y 2 OR a 2 AND y 2 OR. @ Jack, gracias por recordarme la existencia del mapa de Karnaugh.
Tachy
14
+1 por algo nuevo. Mi próxima especificación funcional incluirá un K-map, ya sea que lo necesite o no.
Justin R.
2
Quizás la escasa legibilidad se pueda compensar con (1) la tabla apropiada en el comentario y (2) la prueba de unidad apropiada ... +1 por algo útil aprendido en la escuela.
moala
140

La legibilidad debe ser el objetivo. Alguien que lea el código debe comprender su intención de inmediato. Entonces aquí está mi solución.

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

return howManyBooleansAreTrue >= 2;
danatel
fuente
21
Estoy de acuerdo con la premisa, pero (a && b) || (b && c) || (a && c) es mucho más legible que su solución en mi humilde opinión.
Adrian Grigore
6262
Hmm, ahora necesito una versión de "dos de CUATRO booleanos" ... la versión de Danatel es mucho más fácil ahora.
Arafangion
66
O en Scala:Seq(true, true, false).map(if (_) 1 else 0).sum >= 2
retronym
55
@retrónimo: Hmm, no. La forma Java funciona bien en Scala y es más legible y más eficiente.
Seun Osewa
134
return (a==b) ? a : c;

Explicación:

Si a==b, entonces ambos son verdaderos o ambos son falsos. Si ambos son verdaderos, hemos encontrado nuestros dos booleanos verdaderos, y podemos devolver verdadero (al regresar a). Si ambos son falsos, no puede haber dos booleanos verdaderos, incluso si ces verdadero, por lo que devolvemos falso (al regresar a). Esa es la (a==b) ? aparte ¿Qué hay de : c? Bueno, si a==bes falso, entonces exactamente uno de ao bdebe ser verdadero, por lo que hemos encontrado el primer booleano verdadero, y lo único que importa es si ctambién es verdadero, por lo que volvemos ccomo la respuesta.

Boann
fuente
8
c ni siquiera se prueba ... ¡genial!
CurtainDog
Utiliza una relación transitiva de igualdad y el hecho de que un booleano es verdadero o falso +1
Christophe Roussy
3
¡Muy elegante! Tuve que consultar con lápiz y papel para creerlo :) Felicitaciones señor!
Adrian
3
Pienso en esto como "si ay está de bacuerdo, tienen el voto mayoritario, así que vaya con lo que sea, de lo contrario, no están de acuerdo, así ces el voto decisivo"
Ben Millwood
34

No necesita usar las formas de cortocircuito de los operadores.

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

Esto realiza el mismo número de operaciones lógicas que su versión, sin embargo, no tiene ramificaciones.

sombra de Luna
fuente
11
¿Por qué querría forzar 5 evaluaciones cuando 1 podría hacer? Realmente no realiza la misma cantidad de operaciones lógicas en verdad. De hecho, siempre funcionaría más.
Mark Peters
2
Creo que mezclar aritmética binaria y aritmética booleana es una mala idea. Es como clavar tornillos en la pared con una llave inglesa. Lo peor de todo es que tienen una semántica diferente.
Peter Tillemans
12
@Mark: puede ser más rápido ... dependiendo del efecto de una predicción de rama incorrecta en la tubería de la CPU. Sin embargo, es mejor dejar tales micro optimizaciones para el compilador JIT.
Stephen C
44
Está bien hacer algo como esto en Java (o en cualquier otro idioma) ... con un par de advertencias: 1) debe ser más rápido (en este caso, creo que es así, vea mi segunda respuesta) 2) preferible significativamente más rápido (no estoy seguro de si lo es), 3) documentado más importante ya que es "extraño". Mientras tenga un propósito y esté documentado, está bien "romper las reglas" cuando tenga sentido.
TofuBeer
11
@ Peter Tillemans No hay mezcla con operadores binarios, en Java estos son operadores booleanos.
Starblue
27

Aquí hay un enfoque general basado en pruebas. No es tan "eficiente" como la mayoría de las soluciones ofrecidas hasta ahora, pero claro, probado, funcionando y generalizado.

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;
    }
}
Carl Manaster
fuente
8
Wow, nunca he visto un método completamente probado antes de ver este.
Rotsor
51
Personalmente, encuentro este código horrible, por muchas razones. No voy a votar en contra, pero si alguna vez viera esto en el código de producción, maldeciría. Una operación booleana extremadamente simple no necesita ser complicada como esta.
CaptainCasey
10
Me interesaría mucho saber tus razones, @CaptainCasey. Creo que este es un código bastante bueno. Hay una buena función generalizada que es fácil de entender, fácil de verificar y una función específica que aprovecha, también fácil de entender y verificar. En el mundo real, los haría públicos y los pondría en otra clase; Aparte de eso, felizmente pondría este código en producción. Oh, sí, cambiaría el nombre de countBooleans () por countTrue ().
Carl Manaster
55
Si no se trata de rendimiento, esta solución me parece casi perfecta: muy fácil de leer y ampliable. Eso es exactamente para lo que están hechos los var-args.
atamanroman
77
¿Qué demonios, gente? Este es un código claro y bien probado, y la única razón por la que parece mucho es porque incluye las pruebas. A +++, volvería a votar de nuevo.
Christoffer Hammarström
24

Añádelo. Se llama álgebra booleana por una razón:

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

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

Si observa las tablas de verdad allí, puede ver que la multiplicación es booleana y, además, la suma es xor.

Para responder tu pregunta:

return (a + b + c) >= 2
memet
fuente
2
Esta es la solución más elegante, en mi opinión.
Torbjørn Kristoffersen
99
Sin embargo, un error de novato, un valor booleano NO es 0, eso no significa que siempre sea 1.
tomdemuyt
13
Excepto que la etiqueta en la publicación dice "Java", y no puedes escribir "a + b + c" cuando se definen como booleanos en Java.
Jay
Para trabajar en Java, tendría que serlo return ((a?1:0) + (b?1:0) + (c?1:0)) >= 2.
David R Tribble
Duh, voté por esto porque pensé que era una pregunta de C ++ ... ¿por qué estoy leyendo preguntas de Java? : /
Carlo Wood
15
boolean atLeastTwo(boolean a, boolean b, boolean c) 
{
  return ((a && b) || (b && c) || (a && c));
}
malu
fuente
15

Realmente depende de lo que quieras decir con "mejorado":

Más claro?

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

¿Mas general?

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

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

        if(count > x) return true;
    }

    return false;
}

¿Más escalable?

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

¿Más rápido?

// Only profiling can answer this.

Cuál es "mejorado" depende en gran medida de la situación.

Kerkeslager
fuente
14

Aquí hay otra implementación usando map / reduce. Esto escala bien a miles de millones de booleanos © en un entorno distribuido. Usando MongoDB:

Crear una base valuesde datos de booleanos:

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

Crear el mapa, reducir funciones:

Editar : Me gusta la respuesta de CurtainDog sobre que map / reduce se aplique a listas genéricas, así que aquí va una función de mapa que toma una devolución de llamada que determina si un valor debe contarse o no.

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

Correr mapa / reducir:

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
fuente
@Anurag: por mucho que me guste M / R y la exposición que Google le dio recientemente (incluso si no es el único M / R verdadero de FP), tendería a llamar a bullsh! T por su respuesta. Hay miles y miles de millones de líneas de código que realizan "cosas" del mundo real [TM] en las que no se utiliza una sola línea de mapa / reducción. Alguien que responde a una pregunta de este tipo definitivamente está marcado en mi libro como: "tratando de jugar al inteligente" . Sin mencionar que la mayoría de los entrevistadores no podrían decir si estás tratando de intimidarlos o no porque en realidad nunca escribieron un solo programa usando M / R en su carrera.
SyntaxT3rr0r
2
@Syntax: todos tienen derecho a su opinión. Mi respuesta es solo un enfoque más de mirar el problema. Claro, suena exagerado para 3 valores booleanos, pero eso no significa que estoy tratando de ser el sabelotodo aquí. Este es un enfoque común para la resolución de problemas que todos usan: divida el problema en pedazos pequeños. Así es como funciona la inducción matemática, así es como funcionan la mayoría de los algoritmos recursivos, y así es como las personas resuelven problemas en general.
Anurag
13

Tomando las respuestas (hasta ahora) aquí:

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

y ejecutarlos a través del descompilador (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
}

Puede ver que los?: Unos son ligeramente mejores que la versión arreglada de su original. El que es el mejor es el que evita la ramificación por completo. Eso es bueno desde el punto de vista de menos instrucciones (en la mayoría de los casos) y mejor para las partes de predicción de bifurcación de la CPU, ya que una suposición errónea en la predicción de bifurcación puede causar el estancamiento de la CPU.

Diría que el más eficiente es el de moonshadow en general. Utiliza la menor cantidad de instrucciones en promedio y reduce la posibilidad de que la tubería se bloquee en la CPU.

Para estar 100% seguro, necesitaría averiguar el costo (en ciclos de CPU) para cada instrucción, que, desafortunadamente, no está disponible fácilmente (tendría que buscar la fuente del punto de acceso y luego las especificaciones de los proveedores de CPU para el momento tomado para cada instrucción generada).

Vea la respuesta actualizada de Rotsor para un análisis en tiempo de ejecución del código.

TofuBeer
fuente
55
Solo estás mirando el código de bytes. Por lo que sabes, el JIT tomará una versión con ramas en el código de bytes y la convertirá en una versión sin ramas en el código nativo. Pero uno tendería a pensar que menos ramas en el código de bytes serían mejores.
David Conrad
13

Otro ejemplo de código directo:

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

No es el código más sucinto, obviamente.

Apéndice

Otra versión (ligeramente optimizada) de esto:

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

Esto podría ejecutarse un poco más rápido, suponiendo que la comparación contra 0 usará un código más rápido (o quizás menos) que la comparación contra 2.

David R Tribble
fuente
+1 @Loadmaster, lo siento pero te equivocas. Esta es la respuesta más sucinta aquí. (es decir, brevemente y claramente expresado);)
Ash
Microoptimización: ++nes más rápido que n++ porque tiene que crear otra copia antes de hacer el incremento .
M. Mimpen
@ M.Mimpen: solo para objetos de clase. Para los tipos primitivos (como los nanteriores), cualquier compilador decente compilará cada ++operación en una sola instrucción de CPU, ya sea pre o post.
David R Tribble
12

Otra forma de hacerlo, pero no muy buena:

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

Los Booleanvalores del código hash se fijan en 1231 para verdadero y 1237 para falso, por lo que igualmente podría haber utilizado<= 3699

barrowc
fuente
1
o (a? 1: 0) + (b? 1: 0) + (c? 1: 0)> = 2
Peter Lawrey
12

Las mejoras más obvias son:

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

y entonces

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

Pero esas mejoras son menores.

TofuBeer
fuente
10

No me gusta el ternario ( return a ? (b || c) : (b && c);de la respuesta principal), y no creo haber visto a nadie mencionarlo. Está escrito así:

boolean atLeastTwo(boolean a, boolean b, boolean c) {
    if (a) {
        return b||c;
    } 
    else {
        return b&&C;
    }
Roman A. Taycher
fuente
8

En Clojure :

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

Uso:

(at-least 2 true false true)
Vagif Verdi
fuente
2
+1 Gran versión genérica muestra el poder de los Lisps. Gracias,
dsmith
6

No creo haber visto esta solución todaví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;
}

Su ventaja es que una vez que alcanza el número que está buscando, se rompe. Entonces, si esto fue "al menos 2 de estos 1,000,000 de valores son verdaderos" donde los dos primeros son verdaderos, entonces debería ser más rápido que algunas de las soluciones más "normales".

Joe Enos
fuente
Probablemente debería ser: if (++ counter == howMany) en lugar de incrementar y luego verificar por separado.
Joe Enos
2
O incluso más corto: if (b && (++ counter == howMany))
Joe Enos
1
Lo haría boolean ... boolValues, es más fácil llamar, pero aún así toma una matriz
Stephen
No estoy actualizado en mi Java, no sabía que existía. Es una sintaxis extraña, pero es útil, de vez en cuando lo haré en C # (palabra clave params), y hace que las cosas sean más agradables de llamar. O bien, no sé acerca de Java, pero en .NET, las matrices y todas las colecciones implementan IEnumerable <T>, por lo que probablemente usaría cualquier equivalente de Java.
Joe Enos
¿Cómo se compara el rendimiento de esto con el ejemplo 2of3? devolver a? (b || c): (b && c);
Iain Sproat
6

Podemos convertir los bools a enteros y realizar esta comprobación fácil:

(int(a) + int(b) + int(c)) >= 2
viñedo
fuente
6

Como no se especificó cómo se debe mejorar el código, me esforzaré por mejorarlo haciéndolo más divertido. Aquí está mi solución:

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

En caso de que alguien se pregunte si este código funciona, aquí hay una simplificación que usa la misma lógica:

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; 

}

Esto se puede reducir a lo siguiente:

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

Pero ahora ya no es gracioso.

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

Demasiadas maneras de hacer esto ...

Duby
fuente
3
Se parece más a C #. Esto debería mencionarse como tal en la respuesta ya que la pregunta está dirigida a Java :)
BalusC
5

Solución de corriente alterna.

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

o puede preferir:

int two(int a, int b, int c) {
  return !!a + !!b + !!c >= 2;
}
Mark Edgar
fuente
4
return 1 << $a << $b << $c >= 1 << 2;
Kevin
fuente
No vi la respuesta de Suvega antes de plantear esto, más o menos lo mismo.
Kevin
¿Esto realmente funciona? Supongo que esto es PHP, pero no tengo acceso a él, pero solo te preguntaré: ¿qué sucede si $ a es 0?
Mark Edgar
@Mark En realidad no funciona si $ a es 0. Eso fue un descuido. Gracias por señalar eso. :)
Kevin
4

La forma más simple (IMO) que no es confusa y fácil de leer:

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

return ( a && ( b || c ) ) || ( b && c );
abelito
fuente
Funcionalmente, es lo mismo. Sintácticamente, hace que sea más fácil de leer para aquellos que no están acostumbrados al uso del operador condicional de signo de interrogación. Estoy dispuesto a apostar que más personas saben cómo usar operadores AND y OR que la cantidad de personas que saben cómo usar operadores condicionales de signo de interrogación. La pregunta original pide una "respuesta mejorada". La respuesta aceptada simplifica la respuesta, pero plantea una pregunta muy interesante sobre qué se considera mejor. ¿Programa para facilitar la lectura universal o para simplificar? Para mí, esta es una mejora con respecto a la respuesta aceptada :)
abelito
Preferencias personales. Para mí es mucho más fácil entender al operador ternario más limpio que esta solución.
nico
1
Ah sí, vi este problema y me preguntaba por qué nadie más mencionó esta solución. Si escribe la lógica del OP como álgebra booleana, obtendrá A B + A C + B C, que tiene cinco operaciones. Mediante la propiedad asociativa, puede escribir A * (B + C) + B C, que tiene cuatro operaciones.
Vivian River
Es lo mismo que la respuesta de Jack (jun 19 °.) (C && (A || B)) || (A && B)Acaba de cambiar los nombres * variable` ...
user85421
4

Una interpretación literal funcionará en todos los idiomas principales:

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

Pero probablemente haría más fácil la lectura de las personas, y ampliable a más de tres, algo que parece ser olvidado por muchos programadores:

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;
}
blakecallens
fuente
4

Como una adición a la excelente publicación de @TofuBeer TofuBeer, considere la respuesta de @pdox pdox:

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

Considere también su versión desmontada como la dada por "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 respuesta de pdox se compila con menos código de byte que cualquiera de las respuestas anteriores. ¿Cómo se compara su tiempo de ejecución con los demás?

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

Al menos en mi computadora, la respuesta de pdox es solo un poco más rápida que la respuesta de @moonshadow moonshadow, lo que hace que pdox sea la más rápida en general (en mi computadora portátil HP / Intel).

Rex Barzee
fuente
3

En rubí:

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

Que podría ejecutarse en JRuby en JavaVM. ;-)

usuario373826
fuente
3

Probablemente no esté buscando nada complicado como operadores de comparación bit a bit (normalmente no es complicado pero con booleanos, es extremadamente extraño usar operadores bit a bit) o ​​algo que sea muy indirecto como convertir a int y resumirlos.

La forma más directa y natural de resolver esto es con una expresión como esta:

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

Póngalo en una función si lo prefiere, pero no es muy complicado. La solución es lógicamente concisa y eficiente.

apestosas472
fuente
3

C ª:

return !!a + !!b + !!c >= 2;
Matt Joiner
fuente
En realidad, esta respuesta es incorrecta ... debería ser> = 2, ya que necesita al menos dos booleanos verdaderos, no exactamente dos.
Paul Wagland
@ Paul Wagland: Gracias por la captura.
Matt Joiner
@ergosys: ¿Con qué respondí dos veces?
Matt Joiner el