web-dev-qa-db-fra.com

Comment déterminer si * exactement * un booléen est vrai, sans conversion de type?

Étant donné une liste arbitraire de booléens, quel est le moyen le plus élégant de déterminer si exactement l'un d'entre eux est vrai?

Le hack le plus évident est la conversion de type: convertissez-les en 0 pour false et 1 pour true, puis en les additionnant et en retournant sum == 1.

J'aimerais savoir s'il existe un moyen de faire cela sans les convertir en entiers, en utilisant une logique booléenne .

(Cela semble être trivial, idk, longue semaine)

Edit: Au cas où ce n’était pas évident, c’est plutôt une question de code-golf/théorique. Je ne me soucie pas d'utiliser le type de conversion/int addition dans le code PROD, je suis simplement intéressé par la possibilité de le faire sans cela.

Edit2: Désolé, c'est une longue semaine et je ne m'explique pas bien. Laissez-moi essayer ceci:

Dans la logique booléenne, ANDing une collection de booléens est vraie si tous les booléens sont vrais, ORing la collection est vraie si au moins l'un d'entre eux est vrai. Existe-t-il une construction logique qui sera vraie si exactement un booléen est vrai? XOR s’agit-il d’une collection de deux booléens, par exemple, mais pas plus que cela et cela tombe.

21
SCdF

Avec une logique booléenne simple, il peut ne pas être possible de réaliser ce que vous voulez. Parce que ce que vous demandez, c'est une évaluation de la vérité non seulement basée sur les valeurs de la vérité, mais aussi sur des informations supplémentaires (compter dans ce cas). Mais l'évaluation booléenne est une logique binaire, elle ne peut dépendre que des opérandes eux-mêmes. Et il n’ya aucun moyen de procéder à une ingénierie inverse pour trouver les opérandes avec une valeur de vérité car il peut y avoir quatre combinaisons d’opérandes possibles mais deux résultats seulement. Étant donné un faux, pouvez-vous dire si c'est à cause de F ^ F ou T ^ T dans votre cas, afin que la prochaine évaluation puisse être déterminée sur cette base?.

6
Shiva Kumar

Vous pouvez réellement accomplir cela en utilisant uniquement la logique booléenne, bien que cela ne présente peut-être aucune valeur pratique dans votre exemple. La version booléenne est beaucoup plus complexe que de simplement compter le nombre de valeurs vraies. 

Quoi qu'il en soit, pour satisfaire la curiosité intellectuelle, voici. Premièrement, l’idée d’utiliser une série de XOR est bonne, mais elle ne nous mène qu’à mi-chemin. Pour deux variables quelconques x et y

x y

est vrai chaque fois que l'un d'entre eux est vrai. Toutefois, cela ne continue pas d’être vrai si vous ajoutez une troisième variable z ,

x y z

La première partie, x y , est toujours vraie si exactement l'un des x et y est vrai. Si x ou y est vrai, alors z doit être faux pour que toute l'expression soit vraie, ce que nous voulons. Mais considérons ce qui se passe si les deux x et y sont vrais. Alors x y est faux, mais l'expression entière peut devenir vraie si z est également vraie. Donc, soit une variable, soit les trois doivent être vraies. En général, si vous avez une déclaration qui est une chaîne de XOR, ce sera vrai si un nombre impair de variables sont vraies.

Comme l’un est un nombre impair, cela pourrait s’avérer utile. Bien sûr, vérifier un nombre impair de vérités ne suffit pas. Nous devons également veiller à ce qu’une seule variable soit vraie. Cela peut être fait par paires en prenant toutes les paires de deux variables et en vérifiant qu'elles ne sont pas toutes les deux vraies. Prises ensemble, ces deux conditions permettent d’assurer exactement une si les variables sont vraies. 

Vous trouverez ci-dessous un petit script Python pour illustrer cette approche. 

from itertools import product

print("x|y|z|only_one_is_true")
print("======================")
for x, y, z in product([True, False], repeat=3):
    uneven_number_is_true = x ^ y ^ z
    max_one_is_true = (not (x and y)) and (not (x and z)) and (not (y and z))
    only_one_is_true = uneven_number_is_true and max_one_is_true
    print(int(x), int(y), int(z), only_one_is_true)

Et voici la sortie.

 x | y | z | only_one_is_true 
 ====================== 
 1 1 1 Faux 
 1 1 0 Faux 
 1 0 1 Faux 
 1 0 0 Vrai 
 0 1 1 Faux 
 0 1 0 Vrai 
 0 0 1 Vrai 
 0 0 0 Faux 
7
Anders Johannsen

Après votre clarification, la voici sans entiers.

 bool IsExactlyOneBooleanTrue( bool *boolAry, int size )
    {
      bool areAnyTrue = false;
      bool areTwoTrue = false;
      for(int i = 0; (!areTwoTrue) && (i < size); i++) {
        areTwoTrue = (areAnyTrue && boolAry[i]);
        areAnyTrue |= boolAry[i];
      }
      return ((areAnyTrue) && (!areTwoTrue));
    }
3
c.fogelklou

Cela peut être fait très bien avec la récursivité, par exemple. à Haskell

-- there isn't exactly one true element in the empty list
oneTrue [] = False 
-- if the list starts with False, discard it
oneTrue (False : xs) = oneTrue xs
-- if the list starts with True, all other elements must be False
oneTrue (True : xs) = not (or xs)
2
Alexey Romanov

En raison du grand nombre de lectures, voici un nettoyage rapide et des informations supplémentaires.

Option 1:

Demandez si seule la première variable est vraie, ou seulement la seconde, ..., ou seulement la nième variable.

x1 & !x2 & ... & !xn |
!x1 & x2 & ... & !xn |
...
!x1 & !x2 & ... & xn

Cette approche évolue de O (n ^ 2) , l'évaluation s'arrête après la découverte du premier résultat positif. Par conséquent, préféré si il est probable qu'il y ait une correspondance positive.

Option 2:

Demandez s'il y a au moins une variable vraie au total. De plus, vérifiez chaque paire pour contenir au plus une vraie variable (réponse de Anders Johannsen)

(x1 | x2 | ... | xn) &
(!x1 | !x2) &
...
(!x1 | !xn) &
(!x2 | !x3) &
...
(!x2 | !xn) &
...

Cette option est également mise à l'échelle dans O (n ^ 2) en raison du nombre de paires possibles. Une évaluation paresseuse arrête la formule après le premier exemple de compteur. Par conséquent, il est préférable si sa correspondance probable est négative.

(Option 3):

Cette option implique une soustraction et est donc non une réponse valide pour le paramètre restreint. Néanmoins, il explique que la mise en boucle des valeurs peut ne pas être la solution la plus avantageuse dans un environnement sans restriction.

Traite x1 ... xn comme un nombre binaire x. Soustrayez un, puis ET les résultats. La sortie est zéro <=> x1 ... xn contient au plus une valeur vraie. (ancien algorithme de "vérification de la puissance de deux")

x    00010000
x-1  00001111
AND  00000000

Si les bits sont déjà stockés dans un tel tableau, cela pourrait être avantageux par rapport à la mise en boucle. Cependant, gardez à l'esprit que cela tue la lisibilité et est limité par la longueur de carte disponible.

Une dernière remarque pour sensibiliser: il existe maintenant un échange de pile appelé informatique qui est exactement destiné à ce type de questions algorithmiques

1
Tobias Schiele

Personne n'a mentionné que cette "opération" recherchée est un raccourci semblable à booléen AND et OR dans la plupart des langues. Voici une implémentation en Java:

public static boolean exactlyOneOf(boolean... inputs) {
    boolean foundAtLeastOne = false;
    for (boolean bool : inputs) {
        if (bool) {
            if (foundAtLeastOne) {
                // found a second one that's also true, shortcut like && and ||
                return false;
            }
            foundAtLeastOne = true;
        }
    }
    // we're happy if we found one, but if none found that's less than one
    return foundAtLeastOne;
}
1
TWiStErRob

Bien sûr, vous pourriez faire quelque chose comme ça (pseudocode, puisque vous n'avez pas parlé de langage):

found = false;
alreadyFound = false;
for (boolean in booleans):
    if (boolean):
        found = true;
        if (alreadyFound):
            found = false;
            break;
        else:
            alreadyFound = true;
return found;
1
Eric Galluzzo

Une façon de le faire est d'exécuter la variable AND par paire, puis de vérifier si l'une des comparaisons par paire est devenue vraie avec la chaîne OR chaînée. En python, je le mettrais en œuvre en utilisant

from itertools import combinations

def one_true(bools):
    pairwise_comp = [comb[0] and comb[1] for comb in combinations(bools, 2)]
    return not any(pairwise_comp)

Cette approche se généralise facilement aux listes de longueur arbitraire, bien que pour les listes très longues, le nombre de paires possibles augmente très rapidement.

0
Neuneck

Ce script python fait bien son travail. Voici le one-liner utilisé: 

((x ∨ (y ∨ z)) (¬ (x ∧ y) (¬ (z ∧ x) ¬ (y ∧ z))))

0
Luis E. Vela

Comment voulez-vous compter combien sont vrais sans, vous savez, compter ? Bien sûr, vous pourriez faire quelque chose de compliqué comme (syntaxe C, mon Python est horrible):

for(i = 0; i < last && !booleans[i]; i++)
     ;
if(i == last)
     return 0;  /* No true one found */
/* We have a true one, check there isn't another */
for(i++; i < last && !booleans[i]; i++)
     ;
if(i == last)
     return 1; /* No more true ones */
else
     return 0; /* Found another true */ 

Je suis sûr que vous conviendrez que la victoire (le cas échéant) est faible et que la lisibilité est mauvaise.

0
vonbrand

OK, un autre essai. Appelez les différents booléens b[i] et appelez une tranche d'entre eux (une plage du tableau) b[i .. j]. Définissez les fonctions none(b[i .. j]) et just_one(b[i .. j]) (vous pouvez remplacer les définitions récursives pour obtenir des formules explicites si nécessaire). Nous avons, en utilisant la notation C pour les opérations logiques (&& est et, || est ou, ^ pour xor (pas vraiment en C), ! n'est pas):

none(b[i .. i + 1]) ~~> !b[i] && !b[i + 1]
just_one(b[i .. i + 1]) ~~> b[i] ^ b[i + 1]

Et puis récursivement:

none(b[i .. j + 1]) ~~> none(b[i .. j]) && !b[j + 1]
just_one(b[i .. j + 1] ~~> (just_one(b[i .. j]) && !b[j + 1]) ^ (none(b[i .. j]) && b[j + 1])

Et vous êtes intéressé par just_one(b[1 .. n]).

Les expressions deviendront horribles.

S'amuser!

0
vonbrand

Ce n'est pas possible sans bouclage. Vérifiez la cardinalité BitSet () dans l'implémentation Java. http://fuseyism.com/classpath/doc/Java/util/BitSet-source.html

0
TarekZ

booleanList.Where (y => y) .Count () == 1;

0
J.T. Taylor