XOR трех значений

Как проще всего выполнить трехстороннее исключающее ИЛИ?

Другими словами, у меня есть три значения, и я хочу, чтобы выражение, оценивающее IFF как истинное, было истинным, только одно из трех значений.

Пока вот что я придумал:

((a ^ b) && (a ^ c) && !(b && c)) || ((b ^ a) && (b ^ c) && !(a && c)) || ((с ^ а) && (с ^ б) && !(а && б))

Есть ли что-то более простое, чтобы сделать то же самое?


Вот доказательство того, что вышеизложенное выполняет задачу:

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

person Josh    schedule 12.08.2010    source источник


Ответы (8)


Это выражение можно использовать ровно для трех терминов:

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

Первая часть равна true тогда и только тогда, когда один или три термина равны true. Вторая часть выражения гарантирует, что не все три равны true.

Обратите внимание, что приведенное выше выражение НЕ обобщается на большее количество терминов. Более общее решение состоит в том, чтобы на самом деле подсчитать, сколько терминов составляет true, что-то вроде этого:

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

return (trueCount == 1); // or some range check expression etc
person polygenelubricants    schedule 12.08.2010
comment
Отлично, но общее решение не замыкает. - person Ani; 28.08.2010
comment
a ? 1 : 0 можно упростить до !!a - person kaspersky; 26.05.2014
comment
@gg.kaspersky, только в JavaScript, C и языках, которые имеют проверку на истинность/ложность через оператор !. Например, это не будет работать в Java или C#. - person Drew Noakes; 03.09.2014
comment
@DrewNoakes, когда я делал комментарий, я почему-то думал, что вопрос был специфичен для C. Действительно, некоторые синтаксические конструкции допустимы только в определенных языках. - person kaspersky; 03.09.2014

a^b^c равен только 1, если нечетное количество переменных равно 1 (две '1' отменяют друг друга). Так что вам просто нужно проверить случай «все три равны 1»:

result = (a^b^c) && !(a&&b&&c)
person Aaron Digulla    schedule 12.08.2010

Другая возможность:

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

который оказывается на 9 символов короче принятого ответа :)

person Timwi    schedule 25.08.2010
comment
Немного рад, что это не принятый ответ из соображений удобочитаемости, но, тем не менее, мне пришлось проголосовать, так как краткость все еще имеет для меня странную привлекательность. В университете в классе C у нас был конкурс на написание самого короткого исходного кода, который генерирует песню, похожую на Heidis Kuken, и я был в ТОП-5 в конкурсе. - person Vlasec; 23.11.2019

Вы также можете попробовать (в C):

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

person kaspersky    schedule 20.05.2014
comment
Это действительно только на некоторых языках [например. javascript], и это не упрощение. В языке, который автоматически преобразует логическое значение в число для +, если a, b и c уже являются логическими значениями, вам также не нужно двойное отрицание !!: эквивалентно будет только a+b+c===1. - person blgt; 20.06.2014
comment
@blgt, моя ошибка, я должен указать, что я имел в виду ответ для языка C. - person kaspersky; 20.06.2014
comment
Использование !! для гарантии того, что C true равно 1, на самом деле довольно изящно. Немного параноидальный в контексте вопроса логической логики. Если остальная часть вашего кода написана хорошо, a+b+c==1 должно быть достаточно - person blgt; 20.06.2014

Вот общая реализация, которая быстро дает сбой, когда обнаруживается, что более одного bool равно true.

Использование:

XOR(a, b, c);

Код:

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;
}
person Ani    schedule 28.08.2010

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

$ правда

$ ложь

Потому что я могу.

person nurettin    schedule 22.11.2012

Еще лучше на Python:

result = (1 if a else 0)+(1 if b else 0)+(1 if c else 0) == 1

Это также можно использовать в операторах if!

Это спасло мой день для взаимоисключающих аргументов CLI через Click (все ненавидят клик)

person Pier Carlo Cadoppi    schedule 19.05.2019

person    schedule
comment
Я люблю это. Очень просто и понятно. - person Aaron D; 20.08.2010
comment
Самый простой и должен быть понятен при чтении кода (ну я бы добавил немного пробелов) - person gerardw; 20.08.2014