Saltar al contenido

XOR con cuatro compuertas NAND, equivalencia verificada

El o exclusivo clásico con cuatro NAND, NAND(NAND(A, NAND(A, B)), NAND(B, NAND(A, B))), comprobado contra A XOR B. La equivalencia se cumple en las cuatro filas; la construcción mecánica del propio motor necesita 5 compuertas.

Una sola compuerta NAND es universal, y el XOR dibujado a mano con cuatro de ellas es la demostración estándar: un NAND calcula NAND(A, B), dos más lo combinan con cada entrada, y un cuarto combina esos dos. El modelo lleva A XOR B como referencia y la fórmula de cuatro NAND como circuito bajo prueba. La lectura del motor de la referencia dice que exclusive_or es verdadero exactamente cuando exactamente uno entre A y B es verdadero; la tabla muestra las dos columnas verdaderas en las filas 1 y 2, Σm(1, 2).

La equivalencia se cumple: exclusive_or y four_nands coinciden en todas las filas. El XOR no es tautología, contraejemplo A = FALSO, B = FALSO; satisfacible, testigo A = FALSO, B = VERDADERO. El simplificador no encontró ninguna ley que acorte la referencia de un operador. Las formas normales son las habituales de la eliminación de XOR, y el mapa de Karnaugh deriva la forma de dos celdas a partir de las celdas 1 y 2. El conversor solo con NAND, trabajando mecánicamente desde la referencia, produce NAND(NAND(A, NAND(B, B)), NAND(NAND(A, A), B)) con 5 compuertas, una más que el diseño a mano; la forma solo con NOR necesita 11.

La página muestra las dos maneras de obtener un circuito NAND: un truco de diseñador, que comparte el primer NAND entre las dos ramas, y una construcción de conversor, que no busca compartir. Las dos se verifican con la misma tabla. Lo que hay que vigilar en la práctica es justamente ese paso de verificación: un circuito afinado a mano es una afirmación, y la comprobación de equivalencia es como se pone a prueba, fila por fila, antes de comprometer el trazado.

exclusive_or := A XOR B · salidafour_nands := NAND(NAND(A, NAND(A, B)), NAND(B, NAND(A, B))) · salida

Comprobaciones exactas

¿Siempre verdadera (tautología)? No Contraejemplo: A = F, B = F · Exhaustively checked and solver cross-checked · 1 filas
¿Siempre falsa (contradicción)? No Contraejemplo: A = F, B = V · Exhaustively checked and solver cross-checked · 2 filas
¿Puede ser verdadera? Sí Asignación de ejemplo: A = F, B = V · Exhaustively checked and solver cross-checked · 2 filas
¿Son equivalentes exclusive_or y four_nands? Sí · Exhaustively checked and solver cross-checked · 4 filas

Tabla de verdad (4 filas)

XOR con cuatro compuertas NAND, equivalencia verificada
#ABexclusive_orfour_nands
0FALSOFALSOFALSOFALSO
1FALSOVERDADEROVERDADEROVERDADERO
2VERDADEROFALSOVERDADEROVERDADERO
3VERDADEROVERDADEROFALSOFALSO

Lectura en lenguaje claro

Principianteexclusive_or es verdadero exactamente cuando exactamente uno entre A y B es verdadero (no ambos).
En todos los demás casos, exclusive_or es falso.
Sobre los operadores:
- XOR (O exclusivo) es verdadero cuando exactamente una de sus dos partes es verdadera.
Intermedioexclusive_or es verdadero exactamente cuando exactamente uno entre A y B es verdadero (no ambos).
En todos los demás casos, exclusive_or es falso.
Avanzadoexclusive_or es verdadero exactamente cuando exactamente uno entre A y B es verdadero (no ambos) [disyunción exclusiva ⊕].
En todos los demás casos, exclusive_or es falso.
Notación formal: A ⊕ B

Diagrama lógico

XOR with four NAND gates, verified equivalent XOR with four NAND gates, verified equivalent A B exclusive_orPRIMARY four_nands XOR NAND NAND NAND NAND NAND
XOR with four NAND gates, verified equivalent XOR with four NAND gates, verified equivalent A B exclusive_orPRIMARY four_nands XOR NAND NAND NAND NAND NAND

Simplificación paso a paso

El motor no encontró ninguna ley que acorte esta expresión; ya es tan corta como sus leyes de reescritura permiten.

A XOR B

Mapa de Karnaugh

A \ B
0
1
0
0m0
1m1
1
1m2
0m3
  1. celdas m1NOT A AND B
  2. celdas m2A AND NOT B
(NOT A AND B) OR (A AND NOT B) · Derived (NOT A AND B) OR (A AND NOT B). Exactly equivalent on every assignment.

Mintérminos y maxtérminos

exclusive_or(A, B) = Σm(1, 2)exclusive_or(A, B) = ΠM(0, 3)

Orden de variables: A, B · 4 filas

Formas normales

Forma normal conjuntiva (FNC)

(A OR B) AND (NOT A OR NOT B)

2 cláusulas, 4 literales · complete · Eliminación de XOR ×1

Forma normal disyuntiva (FND)

(A AND NOT B) OR (NOT A AND B)

2 términos, 4 literales · complete · Eliminación de XOR ×1

Solo NAND, solo NOR

Forma solo con NAND

NAND(NAND(A, NAND(B, B)), NAND(NAND(A, A), B))

5 compuertas · equivalencia verificada en todas las filas

Forma solo con NOR

NOR(NOR(NOR(NOR(A, A), B), NOR(A, NOR(B, B))), NOR(NOR(NOR(A, A), B), NOR(A, NOR(B, B))))

11 compuertas · equivalencia verificada en todas las filas

La misma regla como código

Python
# -*- coding: utf-8 -*-
# Generated by Tangibool (tangibool.python/1). Do not edit by hand; regenerate from the source revision.
# Model: not stated
# Source revision: reference
# Interpretation status: not stated
"""Tangibool evaluator (decision model).

Generated deterministically from the validated Boolean model; it evaluates exactly that
model. It does not establish that the model is a complete, legal, or safe statement of a
real-world rule, and evaluating examples does not verify the rule.

Source revision: reference
Interpretation status: not stated
Exporter: 1.0.0 (format tangibool.python/1; engine 1.0.0; semantics tangibool-semantics/1)
Logic digest: 251ea01c45437510440974ddbe2b9d2eb359f9385d1c8811c0e9f981c6d848b3
Assumptions digest: d58ee1ad38e23e5093e4baa1047b5ae70d6972e08f555e34ac3fa16df63c4a78

Inputs (dict keys, declared order) and generated parameter names:
    'A' -> input_A
    'B' -> input_B

Outputs (declared order) and their functions:
    'exclusive_or' -> output_exclusive_or(inputs)
    'four_nands' -> output_four_nands(inputs)

Assumptions (the declared scope, checked by in_scope(inputs)):
    none; every complete assignment is in scope.

Input contract:
    Every required input must be exactly True or False (type(value) is bool). Missing
    inputs, None, strings, integers (including 0 and 1), and keys that are not declared
    inputs raise InputError before anything is evaluated, including inputs that a
    short-circuiting operator would never read. Results are always bool.

Scope:
    Decision functions raise OutOfScopeError when the inputs violate an assumption; an
    out-of-scope assignment is never turned into a False result. Pass check_scope=False
    to evaluate without the scope check, and use in_scope(inputs) to test it separately.

Usage:

    import tangibool_model as model

    model.evaluate({'A': False, 'B': False})
    # -> {'exclusive_or': False, 'four_nands': False}
    model.output_exclusive_or({'A': False, 'B': False})
    # -> False
    model.evaluate({'A': 1, 'B': False})
    # raises InputError: 1 is an int, not a bool
"""

import collections.abc as _abc
from typing import Dict, Mapping, Tuple

__all__ = [
    "MODEL_INFO",
    "IDENTIFIER_MAP",
    "INPUT_NAMES",
    "OUTPUT_NAMES",
    "ASSUMPTIONS",
    "InputError",
    "OutOfScopeError",
    "minterm_index",
    "in_scope",
    "evaluate",
    "output_exclusive_or",
    "output_four_nands",
]

MODEL_INFO = {
    "format": "tangibool.python/1",
    "mode": "decision",
    "model": None,
    "revision": "reference",
    "interpretation": None,
    "exporter": "1.0.0",
    "engine_version": "1.0.0",
    "semantics_version": "tangibool-semantics/1",
    "logic_digest": "251ea01c45437510440974ddbe2b9d2eb359f9385d1c8811c0e9f981c6d848b3",
    "assumptions_digest": "d58ee1ad38e23e5093e4baa1047b5ae70d6972e08f555e34ac3fa16df63c4a78",
    "primary_output": "exclusive_or",
    "unused_inputs": (),
    "subcircuits": (),
}

# Stable ID -> generated identifier (reversible mapping; names are exact, escaped strings).
IDENTIFIER_MAP = {
    "v_eb99168df7ce": {"kind": "input", "name": "A", "identifier": "input_A"},
    "v_be430de53e1b": {"kind": "input", "name": "B", "identifier": "input_B"},
    "f_e6d885aaad20": {"kind": "output", "name": "exclusive_or", "identifier": "output_exclusive_or"},
    "f_0506b4609622": {"kind": "output", "name": "four_nands", "identifier": "output_four_nands"},
}

INPUT_NAMES = ("A", "B")
OUTPUT_NAMES = ("exclusive_or", "four_nands")
ASSUMPTIONS = ()

_DECLARED_INPUTS = frozenset(INPUT_NAMES)
_SCOPE_INPUTS = ()
_ASSUMPTION_FORMULAS = {entry["name"]: entry["formula"] for entry in ASSUMPTIONS}


class InputError(ValueError):
    """Raised when inputs are missing, undeclared, or not exactly True/False.

    Every required input must satisfy type(value) is bool. Missing inputs, None, strings,
    integers (including 0 and 1), and keys that are not declared inputs are rejected before
    anything is evaluated. A missing input is never treated as False. The individual problems
    are listed in the problems attribute.
    """

    def __init__(self, problems):
        self.problems = tuple(problems)
        super().__init__("Invalid inputs: " + "; ".join(self.problems))


class OutOfScopeError(ValueError):
    """Raised when the inputs violate the model's assumptions (its declared scope).

    Outside its assumptions the rule has no decision: this error is never a False result.
    The names of the violated assumptions are in the violated attribute.
    """

    def __init__(self, violated):
        self.violated = tuple(violated)
        details = ", ".join(name + " (" + _ASSUMPTION_FORMULAS.get(name, "?") + ")" for name in self.violated)
        super().__init__(
            "Out of scope: the inputs violate "
            + ("assumption " if len(self.violated) == 1 else "assumptions ")
            + details
            + ". The rule has no decision outside its assumptions; this is not a False result."
        )


def _short_repr(value: object) -> str:
    text = repr(value)
    return text if len(text) <= 60 else text[:57] + "..."


def _checked_inputs(inputs: Mapping[str, bool], required: Tuple[str, ...], check_scope: bool) -> Dict[str, bool]:
    """Validate inputs strictly before anything is evaluated and return a plain copy."""
    if type(check_scope) is not bool:
        raise TypeError("check_scope must be True or False")
    if not isinstance(inputs, _abc.Mapping):
        raise InputError(["inputs must be a mapping from input names to True/False, got " + type(inputs).__name__])
    problems = []
    for key in inputs:
        if type(key) is not str or key not in _DECLARED_INPUTS:
            declared = ", ".join(repr(name) for name in INPUT_NAMES) or "none"
            problems.append("undeclared input " + _short_repr(key) + " (declared inputs: " + declared + ")")
    needed = tuple(required) + (_SCOPE_INPUTS if check_scope else ())
    for name in needed:
        if name not in inputs:
            problems.append("missing required input " + repr(name))
    for key, value in inputs.items():
        if type(key) is str and key in _DECLARED_INPUTS and type(value) is not bool:
            problems.append(
                "input " + repr(key) + " must be True or False (type bool), got "
                + type(value).__name__ + " " + _short_repr(value)
            )
    if problems:
        raise InputError(list(dict.fromkeys(problems)))
    return dict(inputs.items())


def _require_bool_args(function: str, pairs: Tuple[Tuple[str, object], ...]) -> None:
    problems = [
        "argument " + repr(name) + " of " + function + " must be True or False (type bool), got "
        + type(value).__name__ + " " + _short_repr(value)
        for name, value in pairs
        if type(value) is not bool
    ]
    if problems:
        raise InputError(problems)


def minterm_index(*values: bool) -> int:
    """Row index of an assignment over a declared variable order.

    The first value is the most significant bit, so all-False is row 0. Used for indexed
    minterm (sum) and maxterm (product) forms.
    """
    index = 0
    for value in values:
        if type(value) is not bool:
            raise InputError(["minterm_index() takes True/False values, got " + type(value).__name__ + " " + _short_repr(value)])
        index = index * 2 + (1 if value else 0)
    return index


def _scope_values(values: Dict[str, bool]) -> Tuple[Tuple[str, bool], ...]:
    """Value of every enabled, confirmed assumption for validated inputs, in declared order."""
    return ()


def _require_in_scope(values: Dict[str, bool]) -> None:
    violated = tuple(name for name, holds in _scope_values(values) if not holds)
    if violated:
        raise OutOfScopeError(violated)


def in_scope(inputs: Mapping[str, bool]) -> bool:
    """True when the inputs satisfy every assumption (the model's declared scope).

    Requires the inputs the assumptions read; raises InputError for invalid inputs.
    """
    values = _checked_inputs(inputs, (), True)
    return all(holds for _, holds in _scope_values(values))


# ---- Formula bodies (inputs are validated by the public functions below) ----


def _compute_output_exclusive_or(input_A: bool, input_B: bool) -> bool:
    """Decision output 'exclusive_or': A XOR B"""
    return input_A != input_B


def _compute_output_four_nands(input_A: bool, input_B: bool) -> bool:
    """Decision output 'four_nands': NAND(NAND(A, NAND(A, B)), NAND(B, NAND(A, B)))"""
    return not ((not (input_A and (not (input_A and input_B)))) and (not (input_B and (not (input_A and input_B)))))


# ---- Public API ----


def output_exclusive_or(inputs: Mapping[str, bool], *, check_scope: bool = True) -> bool:
    """Decision output 'exclusive_or' (stable ID f_e6d885aaad20).

    'exclusive_or' := A XOR B

    Required inputs: 'A', 'B'.
    Raises InputError for missing, undeclared, or non-bool inputs, and OutOfScopeError
    when check_scope is True and the inputs violate an assumption.
    """
    values = _checked_inputs(inputs, ("A", "B"), check_scope)
    if check_scope:
        _require_in_scope(values)
    output_exclusive_or_value = _compute_output_exclusive_or(values["A"], values["B"])
    return output_exclusive_or_value


def output_four_nands(inputs: Mapping[str, bool], *, check_scope: bool = True) -> bool:
    """Decision output 'four_nands' (stable ID f_0506b4609622).

    'four_nands' := NAND(NAND(A, NAND(A, B)), NAND(B, NAND(A, B)))

    Required inputs: 'A', 'B'.
    Raises InputError for missing, undeclared, or non-bool inputs, and OutOfScopeError
    when check_scope is True and the inputs violate an assumption.
    """
    values = _checked_inputs(inputs, ("A", "B"), check_scope)
    if check_scope:
        _require_in_scope(values)
    output_four_nands_value = _compute_output_four_nands(values["A"], values["B"])
    return output_four_nands_value


def evaluate(inputs: Mapping[str, bool], *, check_scope: bool = True) -> Dict[str, bool]:
    """Evaluate every output (declared order); each output is computed once.

    Required inputs: 'A', 'B'.
    Raises InputError for missing, undeclared, or non-bool inputs, and OutOfScopeError
    when check_scope is True and the inputs violate an assumption.
    """
    values = _checked_inputs(inputs, ("A", "B"), check_scope)
    if check_scope:
        _require_in_scope(values)
    output_exclusive_or_value = _compute_output_exclusive_or(values["A"], values["B"])
    output_four_nands_value = _compute_output_four_nands(values["A"], values["B"])
    return {
        "exclusive_or": output_exclusive_or_value,
        "four_nands": output_four_nands_value,
    }
PostgreSQL
-- Tangibool PostgreSQL export (tangibool.postgresql/1)
-- Target: PostgreSQL 16 or later with typed boolean inputs; tested with PostgreSQL 18.3 (PGlite 0.5.8).
-- Not a claim of compatibility with other SQL dialects.
-- Model: not stated
-- Source revision: reference
-- Interpretation status: not stated
-- Exporter: 1.0.0 (engine 1.0.0; semantics tangibool-semantics/1)
-- Logic digest: 251ea01c45437510440974ddbe2b9d2eb359f9385d1c8811c0e9f981c6d848b3
-- Assumptions digest: d58ee1ad38e23e5093e4baa1047b5ae70d6972e08f555e34ac3fa16df63c4a78
-- Mode: decision
--
-- Read-only: this file contains only WITH / VALUES / SELECT statements. No DDL, DML,
-- procedures, credentials, network access, or database changes. The expressions are pure
-- Boolean expressions, so their result does not depend on SQL evaluation order.
--
-- NON-NULL INPUT PRECONDITION: NULL is not a truth value of this rule. Every fragment below
-- is defined only when every input column it reads is a non-NULL boolean. The guard template
-- (section 2) marks any NULL input as 'invalid_input' and any row that violates the
-- assumptions as 'out_of_scope', and fills output columns only for 'ok' rows; a NULL in an
-- output column of another row is an envelope marker, not a third truth value. Never wrap
-- the fragments in COALESCE(..., FALSE) or otherwise choose a meaning for unknown inputs.
-- This evaluates the supplied model; it does not show that the model is a complete, legal,
-- or safe statement of a real-world rule.
--
-- Input columns (stable ID: name -> column):
--   v_eb99168df7ce: A -> "in_A"
--   v_be430de53e1b: B -> "in_B"
-- Output columns:
--   f_e6d885aaad20: exclusive_or -> "out_exclusive_or"
--   f_0506b4609622: four_nands -> "out_four_nands"
-- Assumptions (scope K; exported separately and never folded into an output):
--   none; every complete input row is in scope.

-- ----------------------------------------------------------------------------------------------
-- 1. Expression fragments (parenthesised; non-NULL boolean inputs required)
-- ----------------------------------------------------------------------------------------------
--
-- [out_exclusive_or] output exclusive_or (stable ID f_e6d885aaad20)
--   exclusive_or := A XOR B
--   PRECONDITION: "in_A", "in_B" are non-NULL booleans.
--   ("in_A" <> "in_B")
--
-- [out_four_nands] output four_nands (stable ID f_0506b4609622)
--   four_nands := NAND(NAND(A, NAND(A, B)), NAND(B, NAND(A, B)))
--   PRECONDITION: "in_A", "in_B" are non-NULL booleans.
--   (NOT ((NOT ("in_A" AND (NOT ("in_A" AND "in_B")))) AND (NOT ("in_B" AND (NOT ("in_A" AND "in_B"))))))
--

-- ----------------------------------------------------------------------------------------------
-- 2. Guard template (read-only SELECT over an inputs list)
-- ----------------------------------------------------------------------------------------------
-- Replace the example rows between the markers with your own rows, or replace the inputs
-- CTE with a SELECT that provides the boolean columns listed in the header. status is
-- 'invalid_input' when any input is NULL, 'out_of_scope' when the assumptions are false, and
-- 'ok' otherwise; output columns are filled only for 'ok' rows (NULL elsewhere is an
-- envelope marker). Assumption columns are shown for rows with complete inputs.
WITH inputs ("row_id", "in_A", "in_B") AS (
  -- tangibool:example-rows:begin
  VALUES
    (1, FALSE, FALSE),
    (2, FALSE, TRUE),
    (3, TRUE, FALSE),
    (4, TRUE, TRUE),
    (5, NULL::boolean, FALSE)
  -- tangibool:example-rows:end
),
checked AS (
  SELECT
    inputs.*,
    CASE
      WHEN "in_A" IS NULL OR "in_B" IS NULL THEN 'invalid_input'
      ELSE 'ok'
    END AS "status"
  FROM inputs
)
SELECT
  "row_id",
  "in_A",
  "in_B",
  "status",
  CASE WHEN "status" = 'ok' THEN ("in_A" <> "in_B") END AS "out_exclusive_or",
  CASE WHEN "status" = 'ok' THEN (NOT ((NOT ("in_A" AND (NOT ("in_A" AND "in_B")))) AND (NOT ("in_B" AND (NOT ("in_A" AND "in_B")))))) END AS "out_four_nands"
FROM checked
ORDER BY "row_id";
SMT-LIB
; Tangibool SMT-LIB export (tangibool.smtlib/1)
; Profile: SMT-LIB 2.6 Boolean subset (QF_UF): Bool constants, define-fun, let, push/pop, assert, check-sat, echo.
; Tested with Z3 5.1.0 (z3-solver 5.2.0, WebAssembly build).
; Model: not stated
; Source revision: reference
; Interpretation status: not stated
; Exporter: 1.0.0 (engine 1.0.0; semantics tangibool-semantics/1)
; Logic digest: 251ea01c45437510440974ddbe2b9d2eb359f9385d1c8811c0e9f981c6d848b3
; Assumptions digest: d58ee1ad38e23e5093e4baa1047b5ae70d6972e08f555e34ac3fa16df63c4a78
; Mode: decision
;
; Reading results: each query block states what sat and unsat mean for its question. A sat
; answer comes with a witness or counterexample (ask for it with (get-model) after sat only);
; unsat settles that scoped query and is solver-checked, not an independently checked proof
; certificate; unknown settles nothing. Blocks are scoped with push/pop, so assertions never
; accumulate across questions. This checks the supplied model, not whether it is a complete,
; legal, or safe statement of a real-world rule.
;
; Symbols (stable ID: name -> symbol):
;   v_eb99168df7ce: A -> in_A
;   v_be430de53e1b: B -> in_B
;   f_e6d885aaad20: exclusive_or -> out_exclusive_or
;   f_0506b4609622: four_nands -> out_four_nands

(set-option :produce-models true)
(set-info :smt-lib-version 2.6)
(set-logic QF_UF)

; ---- Inputs (declared order)
(declare-const in_A Bool) ; A
(declare-const in_B Bool) ; B

; ---- Outputs (dependencies first)
; output exclusive_or (stable ID f_e6d885aaad20): A XOR B
(define-fun out_exclusive_or () Bool (xor in_A in_B))
; output four_nands (stable ID f_0506b4609622): NAND(NAND(A, NAND(A, B)), NAND(B, NAND(A, B)))
(define-fun out_four_nands () Bool (not (and (not (and in_A (not (and in_A in_B)))) (not (and in_B (not (and in_A in_B)))))))

; ---- Assumptions (the scope K; never folded into an output)
; K: conjunction of the enabled, confirmed assumptions (true when there are none)
(define-fun K () Bool true)

; ---- Definitions only: nothing is asserted and no check is run.
; Add (assert ...) and (check-sat) inside (push 1) ... (pop 1) to ask a question.
LaTeX
exclusive_or = A \oplus B
four_nands = \overline{\overline{A \cdot \overline{A \cdot B}} \cdot \overline{B \cdot \overline{A \cdot B}}}

El error habitual

Fiarse del número de compuertas como medida de corrección. El circuito de cuatro NAND es más corto que las cinco del conversor, pero ser corto no prueba nada; la comprobación de equivalencia sí. Un circuito de cuatro compuertas con una entrada mal cableada seguiría teniendo cuatro compuertas y fallaría al menos en una fila.

Preguntas

¿Por qué el conversor no encuentra el circuito de cuatro compuertas?

Construye cada operador con un patrón NAND fijo y no busca subcircuitos compartidos, así que produce 5 compuertas y etiqueta el resultado como una construcción. El diseño de cuatro compuertas reutiliza NAND(A, B) en dos sitios, que es una idea de diseño y no una regla de reescritura.

¿Se puede hacer el mismo truco con compuertas NOR?

Sí, el circuito dual con cuatro NOR calcula XNOR, e invertir su salida o sus entradas da XOR. El conversor solo con NOR de esta página informa de 11 compuertas para su construcción mecánica de XOR, y la página del AND solo con NOR muestra el caso NOR más simple.

Relacionado

Operadores y compuertas

Herramientas

Véase también

Ejemplos resueltos

Generado a partir del motor de Tangibool, semántica tangibool-semantics/1, motor 1.0.0, compilación . Cada tabla, mapa, simplificación y veredicto de estas páginas lo calculó el motor de Tangibool al compilar el sitio, y cada afirmación la volvió a comprobar el mismo verificador exacto que usa la app. Los límites se indican donde aplican. © 2026 Foreranger LTD.