2010-08-12 11 views
28

¿Cuál es la forma más sencilla de hacer un O exclusivo de tres vías?XOR de tres valores

En otras palabras, tengo tres valores, y quiero una declaración que evalúe como verdadero IFF solo uno de los tres valores es verdadero.

Hasta ahora, esto es lo que he llegado con:

(! (A^b) & & (a^c) & & (b & & c)) || ((b^a) & & (b^c) & &! (a & & c)) || ((C^a) & & (c^b) & &! (Un & & b))

¿Hay algo más sencillo que hacer la misma cosa?


Aquí está la prueba de que el anterior logra la tarea:

a = true; b = true; c = true 
((a^b) && (a^c) && !(b && c)) || ((b^a) && (b^c) && !(a && c)) || ((c^a) && (c^b) && !(a && b)) 
=> false 

a = true; b = true; c = false 
((a^b) && (a^c) && !(b && c)) || ((b^a) && (b^c) && !(a && c)) || ((c^a) && (c^b) && !(a && b)) 
=> false 

a = true; b = false; c = true 
((a^b) && (a^c) && !(b && c)) || ((b^a) && (b^c) && !(a && c)) || ((c^a) && (c^b) && !(a && b)) 
=> false 

a = true; b = false; c = false 
((a^b) && (a^c) && !(b && c)) || ((b^a) && (b^c) && !(a && c)) || ((c^a) && (c^b) && !(a && b)) 
=> true 

a = false; b = true; c = true 
((a^b) && (a^c) && !(b && c)) || ((b^a) && (b^c) && !(a && c)) || ((c^a) && (c^b) && !(a && b)) 
=> false 

a = false; b = true; c = false 
((a^b) && (a^c) && !(b && c)) || ((b^a) && (b^c) && !(a && c)) || ((c^a) && (c^b) && !(a && b)) 
=> true 

a = false; b = false; c = true 
((a^b) && (a^c) && !(b && c)) || ((b^a) && (b^c) && !(a && c)) || ((c^a) && (c^b) && !(a && b)) 
=> true 

a = false; b = false; c = false 
((a^b) && (a^c) && !(b && c)) || ((b^a) && (b^c) && !(a && c)) || ((c^a) && (c^b) && !(a && b)) 
=> false 

Respuesta

34

Para exactamente tres términos, puede utilizar esta expresión:

(a^b^c) && !(a && b && c) 

La primera parte es true si y sólo si uno o tres de los términos son true. La segunda parte de la expresión asegura que no todos los tres son true.

Tenga en cuenta que la expresión anterior hace NOT generalice a más términos. Una solución más general es en realidad recuento cuántos términos son true, así que algo como esto:

int trueCount = 
    (a ? 1 : 0) + 
    (b ? 1 : 0) + 
    (c ? 1 : 0) + 
    ... // more terms as necessary 

return (trueCount == 1); // or some range check expression etc 
+0

Grande, pero la solución general no hace corto circuito. – Ani

+0

'a? 1: 0' se puede simplificar a '!! a' – kaspersky

+0

@ gg.kaspersky, solo en JavaScript, C, e idiomas que tienen pruebas de verdad/falsy mediante el operador'! '. Por ejemplo, esto no funcionaría en Java o C#. –

9

a^b^c sólo es 1 si un número impar de variables es 1 (dos '1' se anulan entre sí). Por lo que sólo necesita comprobar en el caso "los tres son 1":

result = (a^b^c) && !(a&&b&&c) 
9
bool result = (a?1:0)+(b?1:0)+(c?1:0) == 1; 
+1

Lo amo. Muy simple y comprensible. –

+0

El más simple y comprensible al leer el código (bueno, agregaría algunos espacios) – gerardw

5

Otra posibilidad:

a ? !b && !c : b^c 

que pasa a ser de 9 caracteres más corta que la respuesta aceptada :)

1

Aquí hay una implementación general que falla rápidamente cuando hay más de un bool es true.

Uso:

XOR(a, b, c); 

Código:

public static bool XOR(params bool[] bools) 
{ 
    return bools.Where(b => b).AssertCount(1); 
} 

public static bool AssertCount<T>(this IEnumerable<T> source, int countToAssert) 
{ 
    int count = 0; 
    foreach (var t in source) 
    { 
     if (++count > countToAssert) return false; 
    } 

    return count == countToAssert; 
} 
1
f= lambda{ |a| [false, false, true].permutation.to_a.uniq.include? a } 
p f.call([false, true, false]) 
p f.call([false, true, true]) 

$ verdadera

$ falsa

Porque puedo.

2

También puede probar (en C):

!!a + !!b + !!c == 1

+0

Esto solo es válido en algunos idiomas [ej. javascript], y no es una simplificación. En un lenguaje que auto-convierte boolean a number para '+', si 'a',' b', y 'c' ya son booleanos, tampoco necesita la doble negación' !! ': simplemente' a + b + c === 1' será equivalente. – blgt

+1

@blgt, error mío, debería especificar que quería ser una respuesta para el lenguaje C. – kaspersky

+0

El uso de '!!' para garantizar un C verdadero es '1' en realidad es bastante limpio. Aunque un poco paranoico en el contexto de una pregunta lógica booleana. Si el resto de tu código está bien escrito, "a + b + c == 1" debería ser suficiente – blgt

Cuestiones relacionadas