XOR de tres valores


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

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

Hasta ahora, esto es lo que se me ha ocurrido:

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

¿Hay algo más sencillo para hacer lo mismo?


Aquí está la prueba de que lo anterior cumple 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
Author: polygenelubricants, 2010-08-12

7 answers

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

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

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

Tenga en cuenta que la expresión anterior hace NO generalizar a más términos. Una solución más general es en realidad contar 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
 38
Author: polygenelubricants,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-12 10:09:20
bool result = (a?1:0)+(b?1:0)+(c?1:0) == 1;
 10
Author: Hemant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-12 09:54:51

a^b^c es solo 1 si un número desigual de variables es 1 (dos '1' se anularían entre sí). Así que solo tiene que comprobar el caso "los tres son 1":

result = (a^b^c) && !(a&&b&&c)
 9
Author: Aaron Digulla,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-12 09:53:35

Otra posibilidad:

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

Que resulta ser 9 caracteres más cortos que la respuesta aceptada:)

 5
Author: Timwi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-25 12:41:11

También puedes probar (en C):

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

 2
Author: kaspersky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-20 14:18:32

Aquí hay una implementación general que falla rápidamente cuando se encuentra que 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
Author: Ani,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-28 09:37:59
f= lambda{ |a| [false, false, true].permutation.to_a.uniq.include? a }
p f.call([false, true, false])
p f.call([false, true, true])

True verdadero

False false

Porque puedo.

 1
Author: nurettin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-22 09:11:09