xor avec 3 valeurs

j'ai besoin de faire un xor conditionnelle entre 3 valeurs, c'est à dire j'ai besoin de l'une des trois valeurs pour être vrai, mais pas plus d'un et pas aucun.

j'ai pensé que je pourrais utiliser l'opérateur xor ^ pour ceci mais cela ne fonctionne pas comme prévu.

Je m'attendais à ce que cela retourne false mais ce n'est pas le cas. (vrai ^ vrai ^ true)

toutes les autres combinaisons semblent fonctionner comme je m'y attendais.

en regardant les docs pour l'opérateur xor ils ne parlent que de comparer 2 valeurs et je ne trouve rien sur le fait de faire cela pour 3 valeurs ou plus en ligne.

quelqu'un peut-il nous éclairer ou Suggérer une façon simple de faire cela?

24
demandé sur sianabanana 2011-06-03 18:19:40

10 réponses

((true ^ true) ^ true) retour true, qui n'est pas ce que vous attendez d' true ^ true ^ true.

Pour vous assurer que vous obtenez le résultat que vous voulez (une seule valeur pour être vrai) procédez de la manière suivante:

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

alternativement, basé sur la réponse de @jcomeau_ictx, vous pouvez faire ce qui suit:

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

Ou, vous pouvez créer une fonction:

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));
}

EDIT: Vous pouvez nommer la fonction TernaryXor pour qu'il soit plus clair quant au résultat de la fonction.

10
répondu Chris 2011-06-03 19:32:46

une façon serait de convertir le valeurs booléennes à un entier, ajouter les résultats, et comparer à 1.

16
répondu jcomeau_ictx 2011-06-03 14:29:25

Parce que je ne peux pas obtenir assez de Linq, comment sur:

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

10
répondu Ray 2011-06-03 14:35:31

c'est court !(a&&b & & c) & & & (A^b^C)--1-->

4
répondu Kuba 2011-06-03 14:37:03

C'est une question difficile, pour sûr. Compte tenu de ce que vous voulez:

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

Cela fera:

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

la première partie traite les quatre cas où a est 0. La deuxième partie, où a n'est pas 0.

Voici une façon plus simple de le regarder:

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

C'est, l'un des trois doit être vrai, mais pas les deux (ou plus) peut être vrai.

Il semble qu'il devrait y avoir un moyen de simplifier davantage, mais il ne vient pas pour esprit. J'ai peut-être besoin de plus de caféine.

EDIT

je pense que c'est la solution que je cherchais ce matin:

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

Maintenant, pourquoi j'ai utilisé | au lieu de ||:

c'est une combinaison d'un problème de style et d'un vieux préjugé contre la ramification (les vieilles habitudes meurent difficilement). !(b | c) génère ce code IL:

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

il n'y a pas de branches dans ce code. Si j'utilise ||, en !(b ||c), il génère:

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

Qui a deux branches. Je ne sais pas si le code produit par JIT sera le même, mais je le soupçonne. Donc un peu de code est 6 instructions qui sont toujours exécutées. L'autre est de 6 instructions dont parfois seulement 4 sont exécutées. Mais les pénalités de branchement pourraient très bien engloutir n'importe quels gains de ne pas exécuter deux des instructions.

je me rends compte que les CPU modernes sont beaucoup mieux à brancher que le 8086 était, et il pourrait n'y avoir aucune différence détectable dans l'exécution de ces deux extraits de code. Même s'il y en avait, il est peu probable que cela fasse une différence significative dans le fonctionnement global des programmes que j'écris habituellement.

mais je vous dis que c'était certainement le cas! Sur le 8086, où la ramification était très chère, la différence entre (b | c) et (b || c)énorme.

enfin, en utilisant |, comme vous l'avez remarqué, les forces de l'évaluation de l'expression entière. Mon le code original dit, en effet, " si cette expression est true ou que l'expression est true."En utilisant && et || il se transforme en un tas de conditions et est, dans mon cerveau, plus difficile à lire.

So: un vieux préjugé fondé sur des considérations de rendement fort probablement dépassées. Mais inoffensif.

il faut faire attention, cependant, de ne pas écrire quelque chose comme (b() | c()) sauf si les deux fonctions doivent être évaluées.

4
répondu Jim Mischel 2011-06-03 23:09:06

Utilisation de la XOR, if (false^false^false) devrait revenir false. La façon dont XOR est supposé fonctionner fonctionne. Exclusif Ou renvoie true si un seul des éléments est true. S'ils sont tous faux, il retournera faux.

Si vous voulez le faire avec 3 valeurs, disons a,b et c, l'expression doit être quelque chose comme: (a, b ou c) et ~(a et b) et ~(a et c) et ~(b et c)

heres un plus bon format: (a v b v c) * ~(a * b) * ~(a * c) * ~(b * c)

2
répondu Mertis 2011-06-03 14:25:56

XOR est un opérateur binaire et ne fonctionne pas sur plus de deux opérandes. Compte tenu de l'ordre des opérations, lorsque vous regardez: (false ^ false ^ false) vous êtes vraiment `((faux ^ false) ^ false).

compte tenu de ce qui mindm, en termes d'évaluer si cela va fonctionner pour vous, il peut être utile de construire vous-même une table de vérité pour votre opération souhaitée. Dans le cas de l'exemple ci-dessus, vous regardez:

(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
2
répondu Greg 2011-06-03 14:31:49

XOR est un opérateur binaire, il ne peut être utilisé que sur 2 valeurs à la fois. Ainsi, lorsque vous vous (true XOR true XOR true), en fait ((true XOR true) XOR true)...

L'intérieur (true XOR true) décide de false parce qu'ils sont les mêmes. Avec cela résolu, le reste est (false XOR true), qui se résout à true.

on dirait que vous essayez d'être intelligent ou super-efficace pour s'adapter à vos conditions. Cela me prendrait probablement plusieurs minutes pour trouver et ensuite écrire une table de vérité ou de test pour assurez-vous qu'il traite toutes les combinaisons possibles correctement.

C'est tellement plus simple de compter combien d'entre eux sont true et ne if (countTrues == 1). La raison pour cela est qu'il n'a pas de frais généraux significatifs, et il est effectivement lisible et compréhensible par rapport à une solution utilisant seulement bit-twiddling (Il ya un couple d'autres réponses ici qui le démontrent).

2
répondu Joel B Fant 2011-06-03 14:45:44

Je suis tombé là-dessus alors que je me suis trompé de chemin en essayant de résoudre mon propre problème (XOR à travers 4 valeurs); j'ai décidé que LINQ est la façon la plus simple de gérer ce problème pour un n-case;

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

invoqué comme;

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

Instruction-wise, cela va vérifier chaque condition une fois, donc la fonction est O(n), mais c'est flexible et un site damn plus lisible que l'alternative bitwise. ; -)

2
répondu RJ Lohan 2015-02-05 22:50:04

la solution simple c'est

a = true; b = false; c = true

(a ^ b) || (a ^ c)
2
répondu Dhymas Josue Acuña Rocha 2016-06-20 21:53:24