Lógica para probar que 3 de 4 son verdaderos


163

Quiero devolver Truesi y solo si 3 de los 4 valores booleanos son verdaderos.

Lo más cerca que he llegado es (x ^ y) ^ (a ^ b):

¿Qué tengo que hacer?


10
Hmm, la única forma en que puedo pensar sin una fórmula matemática es usar count. ¡Buena pregunta! :)
Soy Cavic

10
Su idea no es mala, pero debe tomar las negaciones: not a ^ not b ^ not c ^ not des verdadera cuando exactamente uno de los valores negados es verdadero. Esto significa que, a partir de los valores originales, exactamente uno era falso.
Ingo

23
¿Cuál es su problema real detrás de estos detalles?
Wolf

55
@Ingo no a ^ no b ^ no c ^ no d devuelve verdadero donde solo uno es falso Y donde 3 son falsos.
NameSpace

9
La solución obvia sin conteo es (!a&&b&&c&&d) || (a&&!b&&c&&d) || (a&&b&&!c&&d) || (a&&b&&c&&!d).
Jason C

Respuestas:


248

Sugiero escribir el código de una manera que indique lo que quieres decir. Si desea que 3 valores sean verdaderos, me parece natural que el valor 3 aparezca en algún lugar.

Por ejemplo, en C++:

if ((int)a + (int)b + (int)c + (int)d == 3)
    ...

Esto está bien definido en C++: standard (§4.7/4)indica que convertir boola intda los valores esperados 0 o 1.

En Java y C #, puede usar la siguiente construcción:

if ((a?1:0) + (b?1:0) + (c?1:0) + (d?1:0) == 3)
    ...

23
Esta es una buena respuesta. Esto parece un caso de esa cosa X / Y. "Él quiere hacer X usando Y, pero no sabe cómo hacer Y. En lugar de preguntar X, pregunta Y". A menos que esté diseñando un circuito lógico o algo así (y luego estaría en el sitio equivocado), la mejor manera de hacerlo es de una manera que sea legible .
Nada

2
@NothingsImpossible No hay nada XY sobre la pregunta. Es una pregunta clara y directa sobre la resolución de un problema razonablemente común en la programación. La Y es irrelevante.
Ярослав Рахматуллин

¡Gracias! Esto es realmente lo que quería hacer, pero mi idea era tan torpe que busqué la lógica booleana.
Simon Kuang

3
if (!!a + !!b + !!c + !!d == 3)es más fácil de escribir, aunque no sé si los compiladores optimizan esto o no
phuclv

2
Tenga en cuenta que en c ++ la conversión de bool a int no es necesaria.
PlasmaHH

90

# 1: ¿Usando una ramificación?: 3 o 4 operaciones

A ^ B ? C & D : ( C ^ D ) & A

# 2 no ramificado, 7 operaciones

(A ^ B ^ C ^ D) & ((A & B) | (C & D))

Cuando solía perfilar todo, descubrí que las soluciones no ramificadas eran un poco más rápidas de operación por operación, ya que la CPU podía predecir mejor la ruta del código y ejecutar más operaciones en conjunto. Sin embargo, aquí hay un 50% menos de trabajo en la declaración de ramificación.


18
+1 - mientras que las otras respuestas son mejores para la mayoría de los lenguajes de programación, su # 2 es la mejor respuesta en lógica booleana pura.
Brilliand


68

Si esto hubiera sido Python, habría escrito

if [a, b, c, d].count(True) == 3:

O

if [a, b, c, d].count(False) == 1:

O

if [a, b, c, d].count(False) == True:
# In Python True == 1 and False == 0

O

print [a, b, c, d].count(0) == 1

O

print [a, b, c, d].count(1) == 3

O

if a + b + c + d == 3:

O

if sum([a, b, c, d]) == 3:

Todo esto funciona, ya que los booleanos son subclases de enteros en Python.

if len(filter(bool, [a, b, c, d])) == 3:

O, inspirado en este ingenioso truco ,

data = iter([a, b, c, d])
if not all(data) and all(data):

17
+1 Esto resuelve el problema traduciéndolo correctamente a Python.
Wolf

Esto es un poco peligroso porque las personas pueden devolver cualquier número entero distinto de cero en un contexto booleano en Python. El viejo truco C funciona en python también: a=5;not not a == 1. La desventaja de no tener un tipo booleano real.
Voo

@Voo También tenemos bool:)
thefourtheye

@thefourtheye Ah sí, cierto, mucho mejor que el truco / hack de doble negación.
Voo

1
O ... o ... o ... Debería haber una, y preferiblemente solo una, forma obvia de hacerlo. : - / :-)
rz.

53

Forma normal larga pero muy simple (disyuntiva):

 (~a & b & c & d) | (a & ~b & c & d) | (a & b & ~c & d) | (a & b & c & ~d)

Puede simplificarse, pero eso requiere más reflexión: P


2
@Ben que solo te da varias formas normales, que ya está en (DNF).
Riking

8
¿Qué tal (a & b & (c ^ d)) | ((a ^ b) & c & d)?
user253751

2
Sí, @immibis, según Wolfram Alpha, su DNF es la fórmula que he escrito, por lo que es la misma función booleana.
Gastón Bengolea

2
+1 porque creo que alguien que lea el código entenderá lo que se intenta más rápido que con otras respuestas.
Boluc Papuccuoglu


22

Si desea utilizar esta lógica en un lenguaje de programación, mi sugerencia es

bool test(bool a, bool b, bool c, bool d){
    int n1 = a ? 1 : 0;
    int n2 = b ? 1 : 0;
    int n3 = c ? 1 : 0;
    int n4 = d ? 1 : 0;

    return n1 + n2 + n3 + n4 == 3;
}

O si lo desea, puede poner todo esto en una sola línea:

return (a ? 1 : 0) + (b ? 1 : 0) + (C ? 1 : 0) + (d ? 1 : 0) == 3;

También puede generalizar este problema a n of m :

bool test(bool *values, int n, int m){
    int sum = 0;
    for(int i = 0; i < m; i += 1){
        sum += values[i] ? 1 : 0;
    }
    return sum == n;
}

12
Gáname a eso. La legibilidad triunfa sobre la inteligencia, siempre. +1
MikeTheLiar

20

Esta respuesta depende del sistema de representación, pero si 0 es el único valor interpretado como falso y not(false)siempre devuelve el mismo valor numérico, entonces not(a) + not(b) + not(c) + not(d) = not(0)debería funcionar.


18

Teniendo en cuenta que SO si para preguntas de programación, en lugar de simples problemas lógicos, la respuesta obviamente depende de la elección de un lenguaje de programación. Algunos idiomas admiten funciones que son poco comunes a otros.

Por ejemplo, en C ++ puede probar sus condiciones con:

(a + b + c + d) == 3

Esta debería ser la forma más rápida de hacer la verificación en los idiomas que admiten la conversión automática (de bajo nivel) de tipos booleanos a enteros. Pero, de nuevo, no hay una respuesta general para ese problema.


2
Esta es la respuesta que iba a publicar. Sin embargo, una cosa para agregar, dependiendo del lenguaje de programación utilizado, la respuesta que desea sería -3. En VB, verdadero = -1.
Tom Collins


11
((a xor b) xor (c xor d)) and ((a or b) and (c or d))

La expresión de puño busca 1 o 3 truede 4. La segunda elimina 0 o 1 (y a veces 2) truede 4.


11

Java 8, filtre los valores falsos y cuente los valores verdaderos restantes:

public static long count(Boolean... values) {
    return Arrays.stream(values).filter(t -> t).count();
}

Entonces puede usarlo de la siguiente manera:

if (3 == count(a, b, c, d)) {
    System.out.println("There... are... THREE... lights!");
}

Generaliza fácilmente a la comprobación de nde martículos de ser cierto.


11

Para verificar que al menos ntodos Booleanson verdaderos, (n debe ser menor o igual que el número total de Boolean: p)

if (((a ? 1:0) + (b ? 1:0 ) + (c ? 1:0) + (d ? 1:0 )) >= n) {
    // do the rest
}

Editar : después del comentario de @ Cruncher

Para verificar 3 booleande 4

if (((a ? 1:0) + (b ? 1:0 ) + (c ? 1:0) + (d ? 1:0 )) == 3) {
    // do the rest
}

Otro :

((c & d) & (a ^ b)) | ((a & b) & (c ^ d))( Detalles )


OP quiere exactamente n, no al menos n. Pero ese es un cambio fácil de esta solución
Cruncher

2
@Wolf esa pregunta pertenece a StackUnderflow.com: p
No es un error

10

Aquí hay una manera de resolverlo en C # con LINQ:

bool threeTrue = new[] { a, b, x, y }.Count(x => x) == 3;

10

Esa es la función booleana simétrica S₃(4). Una función booleana simétrica es una función booleana que solo depende de la cantidad de entradas establecidas, pero no depende de las entradas que sean. Knuth menciona funciones de este tipo en la sección 7.1.2 del Volumen 4 de The Art of Computer Programming.

S₃(4) se puede calcular con 7 operaciones de la siguiente manera:

(x && y && (a || b)) ^ ((x || y) && a && b)

Knuth muestra que esto es óptimo, lo que significa que no puede hacerlo en menos de 7 operaciones utilizando los operadores normales: &&, || , ^, <,y >.

Sin embargo, si desea usar esto en un lenguaje que use 1verdadero y 0falso, también puede usar la suma fácilmente:

x + y + a + b == 3

lo que deja tu intención bastante clara.


9
(a && b && (c xor d)) || (c && d && (a xor b))

Desde un punto de vista de lógica pura, esto es lo que se me ocurrió.

Según el principio del palomar, si exactamente 3 son verdaderas, entonces ayb son verdaderas, o cyd son verdaderas. Entonces es solo cuestión de combinar cada uno de esos casos con exactamente uno de los otros 2.

Tabla de verdad de Wolfram


Esto es equivalente a la segunda solución de NameSpace.
Brilliand

@Brilliand Me parece diferente. Sus xors todos juntos para obtener los 3 o 1, luego excluye los que tienen 1 al requerir al menos uno de 2 grupos distintos. (resumido como 1 o 3 y al menos 2). La mía requiere tanto de uno de los distintos grupos, y luego exactamente uno del otro grupo.
Cruncher

Si quisiste decir equivalente en el sentido de que mine <=> hisno sé qué decir, ya que esto sería de esperar.
Cruncher

Supongo que quise decir que esta respuesta es buena exactamente de la misma manera que la segunda solución de NameSpace es buena, sin agregar nada nuevo que la respuesta de NameSpace (anterior) no cubrió. Bueno, voy a votar de todos modos.
Brilliand

8

Si utiliza una herramienta de visualización lógica como Karnaugh Maps, verá que este es un problema en el que no puede evitar un término lógico completo si desea escribirlo en una línea if (...). Lopina ya lo mostró, no es posible escribirlo de manera más simple. Puede factorizar un poco, pero seguirá siendo difícil de leer para usted y para la máquina.

Las soluciones de conteo no son malas y muestran lo que realmente buscas. La forma de contar eficientemente depende de su lenguaje de programación. Las soluciones de matriz con Python o LinQ son agradables de ver, pero cuidado, esto es LENTO. Wolf's (a + b + x + y) == 3 funcionará bien y rápido, pero solo si su idioma equivale a "verdadero" con 1. Si "verdadero" está representado por -1, deberá probar para -3: )

Si su idioma usa booleanos verdaderos, puede intentar programarlo explícitamente (¡uso! = Como prueba XOR):

if (a)
{
    if (b)
        return (x != y);    // a,b=true, so either x or y must be true
    else
        return (x && y);     // a=true, b=false, so x AND y must be true
}
else
{
    if (b)
        return (x && y);    // a=false, b=true, so x and y must be true
    else
        return false;       // a,b false, can't get 3 of 4
}

"x! = y" funciona solo si x, y son de tipo booleano. Si son de otro tipo donde 0 es falso y todo lo demás es verdadero, esto puede fallar. Luego use un XOR booleano, o ((bool) x! = (Bool) y), o escriba "if (x) return (y == false) else return (y == true);", que es un poco más trabajar para la computadora

Si su lenguaje de programación proporciona el operador ternary?: Puede acortarlo a

if (a)
    return b ? (x != y) : (x && y);
else
    return b ? (x && y) : false;

lo que mantiene un poco de legibilidad, o lo corta agresivamente

return a ? (b ? (x != y) : (x && y)) : (b ? (x && y) : false);

Este código hace exactamente tres pruebas lógicas (estado de a, estado de b, comparación de x e y) y debería ser más rápido que la mayoría de las otras respuestas aquí. Pero necesitas comentarlo, o no lo entenderás después de 3 meses :)


8

Hay muchas buenas respuestas aquí; Aquí hay una formulación alternativa que nadie más ha publicado todavía:

 a ? (b ? (c ^ d) : (c && d)) : (b && c && d)

Gracias por su respuesta, pero ¿puede agregar algún comentario sobre cómo funciona? Gracias.
Deanna

(Perdón por molestarlo, me lo dieron como una auditoría de revisión. Al menos lo aprobé ... :))
Deanna

7

Similar a la primera respuesta, pero Java puro:

int t(boolean b) {
    return (b) ? 1 : 0;
}

if (t(x) + t(y) + t(a) + t(b) == 3) return true;
return false;

Prefiero contarlos como enteros porque hace que el código sea más legible.


7

En Python , para ver cuántos elementos iterables son verdaderos, use sum(es bastante sencillo):

Preparar

import itertools

arrays = list(itertools.product(*[[True, False]]*4))

Prueba real

for array in arrays:
    print(array, sum(array)==3)

Salida

(True, True, True, True) False
(True, True, True, False) True
(True, True, False, True) True
(True, True, False, False) False
(True, False, True, True) True
(True, False, True, False) False
(True, False, False, True) False
(True, False, False, False) False
(False, True, True, True) True
(False, True, True, False) False
(False, True, False, True) False
(False, True, False, False) False
(False, False, True, True) False
(False, False, True, False) False
(False, False, False, True) False
(False, False, False, False) False

5

Si buscas la solución en el papel (sin programación), entonces los mapas K y los algoritmos Quine-McCluskey son lo que buscas, te ayudan a minimizar tu función booleana.

En su caso, el resultado es

y = (x̄3 ^ x2 ^ x1 ^ x0) ∨ (x3 ^ x̄2 ^ x1 ^ x0) ∨ (x3 ^ x2 ^ x̄1 ^ x0) ∨ (x3 ^ x2 ^ x1 ^ x̄0)

Si desea hacer esto programáticamente, una cantidad no fija de variables y un "umbral" personalizado, simplemente iterar a través de una lista de valores booleanos y contar las ocurrencias de "verdadero" es bastante simple y directo.


1
¿Qué significa la barra de arriba? Noto que se está moviendo hacia abajo en la lista.
NameSpace

3
@NameSpace Es una de las muchas anotaciones de la OMI que la gente usa para expresar "no".

5

Quiero devolver verdadero si y solo si 3 de los 4 valores booleanos son verdaderos.

Dados los 4 valores booleanos, a, b, x, y, esta tarea se traduce en la siguiente instrucción C:

return (a+b+x+y) == 3;

1
Buena trampa Esto supone trueigual a 1. Esto no es cierto (sin juego de palabras) en todos los idiomas / casos. blogs.msdn.com/b/oldnewthing/archive/2004/12/22/329884.aspx
JensG

@JensG Tienes razón: hago explícita esta suposición. Thx :)
Wolf

4
((a^b)^(x^y))&((a|b)&(x|y))

es lo que quieres Básicamente tomé su código y agregué verificar si realmente 3 son verdaderos y no 3 son falsos.


4

¿Una pregunta de programación sin una respuesta que implique recursividad? ¡Inconcebible!

Hay suficientes respuestas "exactamente 3 de 4 verdaderas", pero aquí hay una versión generalizada (Java) para "exactamente m de n verdaderas" (de lo contrario, la recursión realmente no vale la pena) solo porque puede:

public static boolean containsTrues(boolean[] someBooleans,
    int anIndex, int truesExpected, int truesFoundSoFar) {
  if (anIndex >= someBooleans.length) {
    return truesExpected == truesFoundSoFar; // reached end
  }
  int falsesExpected = someBooleans.length - truesExpected;
  boolean currentBoolean = someBooleans[anIndex];
  int truesFound = truesFoundSoFar + (currentBoolean ? 1 : 0);
  if (truesFound > truesExpected) {
    return false;
  }
  if (anIndex - truesFound > falsesExpected) {
    return false; // too many falses
  }
  return containsTrues(someBooleans, anIndex + 1, truesExpected,
      truesFound);
}

Esto podría llamarse con algo como:

 boolean[] booleans = { true, false, true, true, false, true, true, false };
 containsTrues(booleans, 0, 5, 0);

que debería regresar true(porque 5 de 8 valores eran verdaderos, como se esperaba). No estoy muy contento con las palabras "verdaderas" y "falsas", pero no puedo pensar en un nombre mejor en este momento ... Tenga en cuenta que la recursión se detiene cuando se han encontrado demasiados true o demasiados falsevalores.


@ FélixSaparelli: No estoy seguro de que la "verdad" se aplique aquí ... parecería que estás contento con solo uno true. Quizás algo así containsNumberOfTrueValues(). Como acotación al margen: nombres de Smalltalk sería mucho más adecuado para esto, sin embargo: doesArray: someBooleans startingAt: anIndex containNumberOfTrueValues: anExpectedNumber foundSofar: aNumberFoundSoFar. Probablemente demasiado tiempo para los gustos de algunos desarrolladores de Java, pero Smalltalkers nunca tienen miedo de nombrar adecuadamente ;-)
Amos M. Carpenter

Eso fue sobre todo humorístico. Y containsTruthsignifica "contiene una cantidad no revelada de verdad", literalmente, así que creo que está bastante bien.
Félix Saparelli

3

Dado que la legibilidad es una gran preocupación, podría usar una llamada de función descriptiva (envolviendo cualquiera de las implementaciones sugeridas). Si este cálculo debe hacerse en varios lugares, una llamada a la función es la mejor manera de lograr la reutilización y deja en claro exactamente lo que está haciendo.

bool exactly_three_true_from(bool cond1, bool cond2, bool cond3, bool cond4)
{
    //...
}

3

En PHP, hacerlo más dinámico (en caso de que cambie el número de condiciones, etc.):

$min = 6;
$total = 10;

// create our boolean array values
$arr = array_map(function($a){return mt_rand(0,1)>0;},range(1,$total));

// the 'check'
$arrbools = array_map(function($a){return (int)$a;},$arr);
$conditionMet = array_sum($arrbools)>=$min;

echo $conditionMet ? "Passed" : "Failed";

2
(((a AND b) OR (x AND y)) AND ((a XOR b) OR (x XOR y)))

Si bien podría demostrar que esta es una buena solución, la respuesta de Sam Hocevar es fácil de escribir y comprender más tarde. En mi libro eso lo hace mejor.


1

Aquí hay un código C # que acabo de escribir porque me has inspirado:

Toma cualquier cantidad de argumentos y le dirá si n de ellos son verdaderos.

    static bool boolTester(int n, params bool[] values)
    {
        int sum = 0;           

        for (int i = 0; i < values.Length; i++)
        {
            if (values[i] == true)
            {
                sum += 1;
            }                
        }
        if( sum == n)
        {
            return true;
        }            
        return false;                
    }

y lo llamas así:

        bool a = true;
        bool b = true;
        bool c = true;
        bool d = false;            

        bool test = false;
        test = boolTester(3, a, b, c, d);

Entonces ahora puede probar 7/9 o 15/100 como lo hará.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.