xor с 3 значениями - PullRequest
       14

xor с 3 значениями

24 голосов
/ 03 июня 2011

Мне нужно сделать условное выражение xor между 3 значениями, т. Е. Мне нужно, чтобы одно из трех значений было истинным, но не более одного и ни одного.

Я думал, что мог бы использовать для этого оператор xor ^, но он не работает должным образом.

Я ожидал, что это вернет false, но это не так. (правда ^ правда ^ правда)

все остальные комбинации работают так, как я ожидал.

При просмотре документов для оператора xor они говорят только о сравнении двух значений, и я ничего не могу найти при этом для трех или более значений в Интернете.

Может кто-нибудь пролить свет или предложить простой способ сделать это?

Ответы [ 10 ]

16 голосов
/ 03 июня 2011

Одним из способов будет преобразование логических значений в целое число , добавление результатов и сравнение с 1.

11 голосов
/ 03 июня 2011

Поскольку я не могу получить достаточно Linq, как насчет:

new[] { a, b, c }.Count(v => v) == 1

10 голосов
/ 03 июня 2011

((true ^ true) ^ true) вернет true, а это не то, что вы ожидаете от true ^ true ^ true.

Чтобы убедиться, что вы получите желаемый результат (только одно значение будет истинным), выполните следующие действия:

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

В качестве альтернативы, основываясь на ответе @jcomeau_ictx, вы можете сделать следующее:

if( Convert.ToInt32(a) + Convert.ToInt32(b) + Convert.ToInt32(c) == 1 )

Или вы можете создать функцию:

public bool TernaryXor(bool a, bool b, bool c)
{
    //return ((a && !b && !c) || (!a && b && !c) || (!a && !b && c));

    // taking into account Jim Mischel's comment, a faster solution would be:
    return (!a && (b ^ c)) || (a && !(b || c));
}

РЕДАКТИРОВАТЬ: Возможно, вы захотите назвать функцию TernaryXor, чтобы она была более ясной в отношении результата функции.

4 голосов
/ 03 июня 2011

Это сложно, конечно.Учитывая, что вы хотите:

a b c rslt
0 0 0  0
0 0 1  1
0 1 0  1
0 1 1  0
1 0 0  1
1 0 1  0
1 1 0  0
1 1 1  0

Это сделает это:

rslt = (!a & (b ^ c)) || (a & !(b | c));

Первая часть обрабатывает четыре случая, где a равен 0. Вторая часть, где aне 0.

Более простой способ взглянуть на это так:

rslt = (a | b | c) & !((a & b) | (a & c) | (b & c))

То есть, одно из трех должно быть истинным, но никакие два (или более) не могут быть истинными.

Кажется, должен быть способ еще больше упростить, но это не приходит в голову.Может быть, мне нужно больше кофеина.

РЕДАКТИРОВАТЬ

Я думаю, что это то решение, которое я искал сегодня утром:

rslt = a ? !(b | c) : (b ^ c);

Теперь,что касается того, почему я использовал | вместо ||:

Это сочетание проблемы стиля и старого уклона против ветвления (старые привычки сильно умирают).!(b | c) генерирует этот код IL:

ldarg.1
ldarg.2
or
ldc.i4.0
ceq
stloc.0

В этом коде нет ветвей.Если я использую ||, как в !(b ||c), он генерирует:

  ldarg.1
  brfalse.s IL_009B
  ldarg.2
  br.s IL_009C
IL_009B:
  ldc.i4.1
IL_009C:
  stloc.0

, который имеет две ветви.Я не знаю, отразит ли это код, созданный JIT, но я подозреваю, что так и будет.Таким образом, один бит кода - это 6 инструкций, которые всегда выполняются.Другой 6 инструкций, из которых иногда только 4 выполняются.Но штрафы за ветвление вполне могут съесть любую выгоду от невыполнения двух инструкций.

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

Но я говорю вам, что это, безусловно, раньше!На 8086, где ветвление было очень дорогим, разница между (b | c) и (b || c) составляла огромная .

Наконец, использование |, как вы заметили, заставляет оцениватьвсе выражение.Мой оригинальный код говорит, по сути, «если это выражение истинно или , то это выражение истинно ».Использование && и || превращает его в набор условных выражений и, на мой взгляд, труднее читать.

Итак: старое предубеждение, основанное на наиболее вероятных устаревших соображениях производительности.Но безвреден.

Однако нужно быть осторожным, чтобы не писать что-то вроде (b() | c()), если обе функции не должны быть оценены.

4 голосов
/ 03 июня 2011

это коротко! (A && b && c) && (a ^ b ^ c)

3 голосов
/ 21 июня 2016

решение простое это

a = true; b = false; c = true

(a ^ b) || (a ^ c)
2 голосов
/ 06 февраля 2015

Наткнулся на это, пока я шел по неверному пути, пытаясь решить свою собственную проблему (XOR для 4 значений);решил, что LINQ - самый простой способ справиться с этой проблемой для N-случая;

bool OneAndOnlyOneIsTrue(IEnumerable<bool> conditions)
{
    return conditions.Count(c => c) == 1;
}

Вызывается как;

bool OneLuckyGuy(Person p1, Person p2, Person p3, Person p4)
{
     return OneAndOnlyOneIsTrue(new [] {         
         p1.IsAMan(),
         p2.IsAMan(),
         p3.IsAMan(),
         p4.IsAMan()
     });
}

По инструкции, каждый раз проверяется каждое условие, поэтомуФункция - это O (n), но она гибкая и чертовски сайт более читабелен, чем побитовая альтернатива.; -)

2 голосов
/ 03 июня 2011

XOR является бинарным оператором и не работает более чем с двумя операндами.Учитывая порядок операций, когда вы смотрите на: (false ^ false ^ false) вы действительно получаете `((false ^ false) ^ false).

Имея это в виду, с точки зрения оценки того, собирается ли этоработать для вас, может быть полезно создать таблицу истинности для желаемой операции.В приведенном выше примере вы смотрите на:

(Op1 XOR Op2) XOR Op3 = Result    
 F       F        F     F
 F       F        T     T 
 F       T        F     T 
 F       T        T     F
 T       F        F     T
 T       F        T     F
 T       T        F     F
 T       T        T     T

, что делает XOR проблемой в случае, когда все операнды истинны, и может потребовать некоторой специальной обработки, скажем, путем добавления AND not (Op1 ANDOp2 и Op3).

2 голосов
/ 03 июня 2011

Используя XOR, if (false^false^false) должно вернуть false.Это работает, как XOR предполагается работать. Exclusive Or возвращает true, если один и только один из элементов имеет значение true.Если все они ложны, он вернет ложь.

Если вы хотите сделать это с 3 значениями, скажем, a, b и c, выражение должно быть примерно таким: (a или b или c) и ~(a и b) и ~ (a и c) и ~ (b и c)

вот более правильный формат: (avbvc) * ~ (a * b) * ~ (a * c) * ~(б * в)

2 голосов
/ 03 июня 2011

XOR - бинарный оператор, он может использоваться только для 2 значений одновременно.Поэтому, когда вы делаете (true XOR true XOR true), это на самом деле ((true XOR true) XOR true) ...

Внутренний (true XOR true) разрешается до false, потому что они одинаковы.С этим разрешением, остаток составляет (false XOR true), который разрешается до true.

Звучит так, будто вы пытаетесь быть умным или сверхэффективным в отношении соответствия вашим условиям.Возможно, мне понадобится несколько минут, чтобы разобраться, а затем написать таблицу истинности или тест, чтобы убедиться, что он правильно обрабатывает все возможные комбинации.

Гораздо проще просто посчитать, сколько из них true и делай if (countTrues == 1).Причина этого в том, что у него нет значительных накладных расходов, и он действительно читабелен и понятен по сравнению с решением, использующим только сдвоенный бит (здесь есть пара других ответов, демонстрирующих это).

...