Métodos de Tomada de Decisão

A Lógica e seus Tipos — da Lógica Clássica à Lógica Fuzzy

Prof. Marcelo R.P. Ferreira

PPGMDS - UFPB - Programa de Pós-Graduação em Modelagem de Decisão e Saúde

2026-07-01

A Lógica e seus Tipos

“A lógica é a anatomia do pensamento.”

— John Locke

Mapa da Aula 2

A Lógica e seus Tipos Aula 2 — 14/07/2026 Bloco A Lógica Clássica e SE Bloco B Lógicas Não Clássicas Bloco C Lógica Fuzzy Proposições · Conectivos Sistemas Especialistas Modal · Temporal Não Monotônica Conjuntos · Pertinência Inferência Mamdani Aplicações em Triagem e Risco Clínico

Conexão com a Aula 1

  • Na Aula 1 vimos que SADs usam modelos para apoiar decisões
  • Os sistemas especialistas baseados em regras são um tipo clássico de SAD orientado a modelos
  • Essas regras seguem uma lógica formal — que é exatamente o que estudamos hoje
  • A lógica clássica diz respeito ao raciocínio preciso; a lógica fuzzy ao raciocínio impreciso
  • Na medicina, a maioria das informações é linguística e vaga: “febre alta”, “pressão elevada”, “dor moderada”

Conexão com a Aula 3: os sistemas de inferência fuzzy e os grafos de decisão que veremos hoje são a base conceitual para as redes bayesianas da próxima aula.

Bloco A — Lógica Clássica e Sistemas Especialistas

O que é Lógica?

  • Lógica é o estudo das formas corretas de raciocínio e inferência
  • Permite derivar conclusões válidas a partir de premissas dadas
  • Em computação e IA, a lógica fornece a linguagem para representar e manipular conhecimento
  • Tipos de lógica variam no que podem expressar e em como tratam a incerteza e a imprecisão
Tipo Lida com Exemplo em saúde
Clássica Verdadeiro ou falso “O paciente tem febre”
Modal Possibilidade, necessidade “É possível que seja pneumonia”
Fuzzy Graus de verdade “A febre é alta”
Probabilística Incerteza quantitativa “85% de chance de infecção”

Proposições e Valores de Verdade

  • Uma proposição (denotada por \(p\), \(q\), etc.) é uma sentença declarativa que pode ser verdadeira (V) ou falsa (F)
  • Não é uma pergunta, ordem ou exclamação
  • Exemplos de proposições:
    • “O paciente tem temperatura acima de 38°C” → V ou F
    • “A pressão sistólica é maior que 140 mmHg” → V ou F
  • Não são proposições:
    • “Qual é a saturação de O₂?” → pergunta
    • “A dor está melhorando” → vago (sem medida precisa)

A lógica clássica exige que toda afirmação seja precisamente V ou F — e é exatamente essa rigidez que a lógica fuzzy vai suavizar mais adiante.

Conectivos Lógicos

Sejam \(p\) e \(q\) proposições.

Conectivo Símbolo Nome Significado
Negação \(\neg p\) NÃO inverte o valor de \(p\)
Conjunção \(p \wedge q\) E V somente se ambos V
Disjunção \(p \vee q\) OU V se ao menos um V
OU exclusivo \(p \oplus q\) XOR V somente se exatamente um V
Condicional \(p \rightarrow q\) SE…ENTÃO F apenas se \(p\) V e \(q\) F
Bicondicional \(p \leftrightarrow q\) SE E SOMENTE SE V se ambos iguais

Exemplo clínico: “SE febre \(\wedge\) tosse \(\wedge\) dispneia ENTÃO suspeitar de pneumonia”

Conectivos Lógicos em R

Em R, os operadores lógicos mapeiam diretamente sobre os conectivos formais:

Conectivo Símbolo lógico R (vetorizado) R (escalar)
Negação \(\neg p\) !x !x
Conjunção \(p \wedge q\) x & y x && y
Disjunção \(p \vee q\) x | y x || y
OU exclusivo \(p \oplus q\) xor(x, y) xor(x, y)

Importante

& e | operam elemento a elemento em vetores. && e || avaliam apenas o primeiro elemento e permitem curto-circuito — use &&/|| em condições de if, e &/| em filtragem de dados clínicos.

Operadores Lógicos em R: Vetorizado vs. Escalar

SIRS (Síndrome da Resposta Inflamatória Sistêmica) — conjunto de critérios clínicos para suspeita de sepse.

# Dados simulados de 5 pacientes
febre      <- c(TRUE, TRUE, FALSE, TRUE, FALSE)
taquicardia <- c(TRUE, FALSE, TRUE, TRUE, FALSE)
dispneia   <- c(FALSE, TRUE, FALSE, TRUE, FALSE)

# Conjunção vetorizada: & (opera em todos os pacientes)
alerta_sirs <- febre & taquicardia
cat("Alerta SIRS (febre E taquicardia):\n")
Alerta SIRS (febre E taquicardia):
print(alerta_sirs)
[1]  TRUE FALSE FALSE  TRUE FALSE
# Disjunção: qualquer um dos critérios respiratórios
alerta_resp <- taquicardia | dispneia
cat("\nAlerta respiratório (taquicardia OU dispneia):\n")

Alerta respiratório (taquicardia OU dispneia):
print(alerta_resp)
[1]  TRUE  TRUE  TRUE  TRUE FALSE
# Negação: pacientes SEM febre
sem_febre <- !febre
cat("\nSem febre:\n")

Sem febre:
print(sem_febre)
[1] FALSE FALSE  TRUE FALSE  TRUE
# Combinação: febre E (taquicardia OU dispneia)
alerta_combinado <- febre & (taquicardia | dispneia)
cat("\nAlerta combinado:\n")

Alerta combinado:
print(alerta_combinado)
[1]  TRUE  TRUE FALSE  TRUE FALSE

Tabela-Verdade Completa Gerada em R

# Gerar todas as combinações de dois valores lógicos
p <- c(TRUE, TRUE, FALSE, FALSE)
q <- c(TRUE, FALSE, TRUE, FALSE)

tabela <- data.frame(p            = p,
                     q            = q,
                     `!p`         = !p,
                     `p & q`      = p & q,
                     `p | q`      = p | q,
                     `xor(p,q)`   = xor(p, q),
                     `p→q (!p|q)` = !p | q,
                     `p↔q`        = p == q,
                     check.names  = FALSE)

nomes <- names(tabela)
tabela_fmt <- as.data.frame(lapply(tabela, function(x) ifelse(x, "V", "F")),
                            check.names = FALSE)
names(tabela_fmt) <- nomes
print(tabela_fmt)
  p q !p p & q p | q xor(p,q) p→q (!p|q) p↔q
1 V V  F     V     V        F          V   V
2 V F  F     F     V        V          F   F
3 F V  V     F     V        V          V   F
4 F F  V     F     F        F          V   V

Verificando Validade de Argumentos em R

Um argumento é válido se, sempre que as premissas são verdadeiras, a conclusão também é. Podemos verificar isso computacionalmente.

# Argumento clínico:
# P1: SE febre E tosse ENTÃO suspeitar de gripe
# P2: O paciente tem febre
# P3: O paciente tem tosse
# Conclusão: suspeitar de gripe

verificar_argumento <- function(febre, tosse) {
  P1 <- (febre & tosse)  # antecedente da regra
  gripe <- P1            # conclusão pela regra
  
  # O argumento é válido se P1→gripe é sempre V quando P1 é V
  valido <- all(!P1 | gripe)  # tautologia do modus ponens
  
  data.frame(febre = febre, tosse = tosse,
             `P1 (f∧t)` = P1,
             gripe = gripe,
             `Regra válida` = valido,
             check.names = FALSE)
}

casos <- expand.grid(febre = c(T,F), tosse = c(T,F))
resultado <- verificar_argumento(casos$febre, casos$tosse)
print(resultado)
  febre tosse P1 (f∧t) gripe Regra válida
1  TRUE  TRUE     TRUE  TRUE         TRUE
2 FALSE  TRUE    FALSE FALSE         TRUE
3  TRUE FALSE    FALSE FALSE         TRUE
4 FALSE FALSE    FALSE FALSE         TRUE

O Operador XOR e Diagnóstico Exclusivo

  • O OU exclusivo (xor) é verdadeiro quando exatamente um dos operandos é verdadeiro
  • Clinicamente útil para diagnósticos mutuamente exclusivos
# Exemplo: diagnóstico diferencial simplificado
# Hipótese A: infecção bacteriana | Hipótese B: infecção viral
# Normalmente são mutuamente exclusivas no mesmo episódio

bacteriana <- c(TRUE,  FALSE, TRUE,  FALSE)
viral      <- c(FALSE, TRUE,  FALSE, FALSE)

cat("Diagnóstico exclusivo (xor — um OU outro, não ambos):\n")
Diagnóstico exclusivo (xor — um OU outro, não ambos):
print(xor(bacteriana, viral))
[1]  TRUE  TRUE  TRUE FALSE
cat("\nDiagnóstico inclusivo (| — ao menos um):\n")

Diagnóstico inclusivo (| — ao menos um):
print(bacteriana | viral)
[1]  TRUE  TRUE  TRUE FALSE
cat("\nAmbas as infecções simultaneamente (& — coinfecção):\n")

Ambas as infecções simultaneamente (& — coinfecção):
print(bacteriana & viral)
[1] FALSE FALSE FALSE FALSE
cat("\nNenhuma das hipóteses confirmadas (!a & !b):\n")

Nenhuma das hipóteses confirmadas (!a & !b):
print(!bacteriana & !viral)
[1] FALSE FALSE FALSE  TRUE

Tabelas-Verdade: Negação e Conjunção

Negação \(\neg p\)

\(p\) \(\neg p\)
V F
F V


Conjunção \(p \wedge q\)

\(p\) \(q\) \(p \wedge q\)
V V V
V F F
F V F
F F F

Exemplo clínico — triagem de sepse:

Definindo:

  • \(p\): temperatura > 38°C
  • \(q\): frequência cardíaca > 90 bpm
  • \(r\): frequência respiratória > 20 rpm

A regra SIRS (Síndrome da Resposta Inflamatória Sistêmica) requer ao menos dois desses critérios:

\[\text{SIRS} \equiv (p \wedge q) \vee (p \wedge r) \vee (q \wedge r)\]

Se \(p = V\), \(q = V\), \(r = F\): \(\text{SIRS} = V\)

Tabelas-Verdade: Disjunção e Condicional

Disjunção \(p \vee q\)

\(p\) \(q\) \(p \vee q\)
V V V
V F V
F V V
F F F

Condicional \(p \rightarrow q\)

\(p\) \(q\) \(p \rightarrow q\)
V V V
V F F
F V V
F F V

Importante

O condicional só é falso quando a premissa é V e a conclusão é F — ou seja, quando a regra “mente”.

Tabelas-Verdade dos Conectivos Lógicos

\(p\) \(q\) \(\neg p\) \(p \wedge q\) \(p \vee q\) \(p \oplus q\) \(p \rightarrow q\) \(p \leftrightarrow q\)
V V F V V F V V
V F F F V V F F
F V V F V V V F
F F V F F F V V

Formas Normais

  • Qualquer fórmula lógica pode ser convertida em formas padrão:
  • Forma Normal Conjuntiva (FNC): conjunção de cláusulas disjuntivas \[(p \vee q) \wedge (\neg p \vee r) \wedge (q \vee \neg r)\]
  • Forma Normal Disjuntiva (FND): disjunção de cláusulas conjuntivas \[(p \wedge q) \vee (\neg p \wedge r)\]
  • Úteis para algoritmos de inferência em sistemas especialistas
  • A FNC é a base do algoritmo de resolução (Robinson, 1965)

Na prática clínica: um protocolo de decisão ramificado pode ser convertido para FNC e verificado automaticamente por um sistema computacional.

Conversão para FNC em R: Exemplo Prático

FNC (Forma Normal Conjuntiva): conjunção de cláusulas, onde cada cláusula é uma disjunção de literais. Base do algoritmo de resolução.

# Protocolo de alta hospitalar (simplificado):
# Alta SE (afebril E SpO2_ok) E (exame_ok OU melhora_clinica)
# Equivalente em FNC: verificar cada cláusula separadamente

verificar_alta <- function(afebril, spo2_ok, exame_ok, melhora) {
  clausula1 <- afebril & spo2_ok          # condição vital obrigatória
  clausula2 <- exame_ok | melhora         # ao menos um critério de melhora
  alta_indicada <- clausula1 & clausula2  # FNC: ambas as cláusulas
  
  data.frame(
    afebril = afebril, spo2_ok = spo2_ok,
    exame_ok = exame_ok, melhora = melhora,
    `Cláusula 1` = clausula1,
    `Cláusula 2` = clausula2,
    `Alta indicada` = alta_indicada,
    check.names = FALSE
  )
}

# Testar quatro cenários de pacientes
res <- verificar_alta(
  afebril  = c(TRUE,  TRUE,  FALSE, TRUE),
  spo2_ok  = c(TRUE,  TRUE,  TRUE,  FALSE),
  exame_ok = c(TRUE,  FALSE, TRUE,  TRUE),
  melhora  = c(FALSE, TRUE,  FALSE, TRUE)
)
print(res)
  afebril spo2_ok exame_ok melhora Cláusula 1 Cláusula 2 Alta indicada
1    TRUE    TRUE     TRUE   FALSE       TRUE       TRUE          TRUE
2    TRUE    TRUE    FALSE    TRUE       TRUE       TRUE          TRUE
3   FALSE    TRUE     TRUE   FALSE      FALSE       TRUE         FALSE
4    TRUE   FALSE     TRUE    TRUE      FALSE       TRUE         FALSE

Lógica de Predicados

  • Estende a lógica proposicional com variáveis, predicados e quantificadores
  • Um predicado expressa uma propriedade ou relação: \(\text{Febre}(x)\), \(\text{Alergia}(x, y)\)
  • Quantificador universal \(\forall\): “para todo”
    • \(\forall x \; [\text{Diabético}(x) \rightarrow \text{MonitorarGlicemia}(x)]\)
  • Quantificador existencial \(\exists\): “existe ao menos um”
    • \(\exists x \; [\text{Paciente}(x) \wedge \text{Internado}(x) \wedge \text{Febre}(x)]\)

Leitura clínica: “Todo paciente diabético deve ter a glicemia monitorada” e “Existe ao menos um paciente internado com febre.”

Predicados em R: Verificação em Banco de Dados Clínicos

# Simulação de banco de dados de pacientes
pacientes <- data.frame(
  id       = 1:6,
  nome     = c("Ana", "Bruno", "Carla", "Diego", "Eva", "Fábio"),
  diabetico = c(TRUE, FALSE, TRUE, FALSE, TRUE, FALSE),
  internado = c(TRUE, TRUE, FALSE, TRUE, FALSE, TRUE),
  glicemia  = c(280, 95, 195, 88, 310, 102)
)

# ∀x [Diabético(x) → MonitorarGlicemia(x)]
# Implementação: todos os diabéticos com glicemia alterada
cat("Diabéticos com glicemia > 200 (∀x: Diabético→Monitorar):\n")
Diabéticos com glicemia > 200 (∀x: Diabético→Monitorar):
print(pacientes[pacientes$diabetico & pacientes$glicemia > 200, 
                c("nome", "diabetico", "glicemia")])
  nome diabetico glicemia
1  Ana      TRUE      280
5  Eva      TRUE      310
# ∃x [Internado(x) ∧ Diabético(x)]  — existe ao menos um
existe_diabetico_internado <- any(pacientes$diabetico & pacientes$internado)
cat(sprintf("\n∃x [Internado(x) ∧ Diabético(x)]: %s\n",
            ifelse(existe_diabetico_internado, "VERDADEIRO", "FALSO")))

∃x [Internado(x) ∧ Diabético(x)]: VERDADEIRO
# Quantos satisfazem a condição?
n <- sum(pacientes$diabetico & pacientes$internado)
cat(sprintf("Pacientes diabéticos internados: %d\n", n))
Pacientes diabéticos internados: 1

Inferência: Modus Ponens e Modus Tollens

Modus Ponens (afirmar afirmando)

\[\frac{p \rightarrow q \\ p}{\therefore \; q}\]

SE febre E tosse ENTÃO suspeitar de gripe.
O paciente tem febre e tosse.
\(\therefore\) Suspeitar de gripe. ✓

Modus Tollens (negar negando)

\[\frac{p \rightarrow q \\ \neg q}{\therefore \; \neg p}\]

SE pneumonia ENTÃO raio-X alterado.
O raio-X está normal.
\(\therefore\) Provavelmente não é pneumonia. ✓

Esses dois padrões de inferência são a base do raciocínio automático em sistemas especialistas clínicos.

Sistemas Especialistas: Arquitetura

Base de Conhecimento Regras SE...ENTÃO Memória de Trabalho Fatos do caso atual Motor de Inferência Forward / Backward Interface com o Usuário Médico / Enfermeiro nova consulta

Forward Chaining (Encadeamento para Frente)

  • Parte dos fatos conhecidos e avança em direção a conclusões
  • Estratégia: “dado o que sei, o que posso concluir?”
  • Orientado a dados: começa pelos dados disponíveis

Exemplo — triagem no pronto-socorro:

  1. Fato: temperatura = 39,2°C → ativa regra: “febre alta”
  2. Fato: SpO₂ (saturação periférica de oxigênio) = 91% → ativa regra: “hipoxemia”
  3. Fato: frequência respiratória = 28 rpm → ativa regra: “taquipneia”
  4. Combinação das três regras → conclui: “alerta de insuficiência respiratória aguda”
  5. Ação: acionar protocolo de emergência

Backward Chaining (Encadeamento para Trás)

  • Parte de uma hipótese e busca os fatos que a sustentam
  • Estratégia: “para confirmar X, quais evidências preciso?”
  • Orientado a metas: começa pela conclusão desejada

Exemplo — investigação de pneumonia:

  1. Meta: confirmar pneumonia
  2. Para pneumonia, preciso de: febre E tosse E (infiltrado no raio-X OU consolidação na TC)
  3. Verificar febre → presente ✓
  4. Verificar tosse → presente ✓
  5. Solicitar raio-X → infiltrado presente ✓
  6. Conclusão: hipótese de pneumonia confirmada → iniciar antibiótico

Fatores de Certeza (Certainty Factors)

  • A lógica clássica é binária, mas na clínica as evidências têm graus de confiança
  • Shortliffe & Buchanan (MYCIN, 1975) introduziram os fatores de certeza (CF)
  • CF \(\in [-1, 1]\): \(-1\) = certamente falso, \(0\) = neutro, \(+1\) = certamente verdadeiro
  • Combinação de evidências:
    • Se CF(\(p\)) = 0,7 e CF(\(q\)) = 0,6, então CF(\(p \wedge q\)) = min(0,7; 0,6) = 0,6
    • CF(\(p \vee q\)) = max(0,7; 0,6) = 0,7

MYCIN foi o primeiro sistema especialista médico de sucesso (diagnóstico de infecções bacterianas), usando exatamente esse mecanismo. Em testes cegos, igualou o desempenho de especialistas humanos em 65% dos casos.

Fatores de Certeza em R: Combinando Evidências

# Regras com fatores de certeza para diagnóstico de pneumonia
# CF ∈ [-1, 1]: -1=certamente falso, 0=neutro, +1=certamente verdadeiro

combinar_cf <- function(cf1, cf2) {
  # Combinação de evidências favoráveis independentes
  if (cf1 >= 0 & cf2 >= 0) {
    return(cf1 + cf2 * (1 - cf1))       # ambas favoráveis
  } else if (cf1 < 0 & cf2 < 0) {
    return(cf1 + cf2 * (1 + cf1))       # ambas desfavoráveis
  } else {
    return((cf1 + cf2) / (1 - min(abs(cf1), abs(cf2))))  # mistas
  }
}

# Evidências para pneumonia
cf_febre_tosse   <- 0.6   # febre + tosse → pneumonia (CF = 0.6)
cf_raio_x        <- 0.8   # infiltrado no raio-X → pneumonia (CF = 0.8)
cf_sem_antibiot  <- -0.3  # ausência de melhora com antibiótico (CF = -0.3)

cf_combinado_1 <- combinar_cf(cf_febre_tosse, cf_raio_x)
cf_final       <- combinar_cf(cf_combinado_1, cf_sem_antibiot)

cat(sprintf("CF (febre+tosse):          %.2f\n", cf_febre_tosse))
CF (febre+tosse):          0.60
cat(sprintf("CF (raio-X alterado):      %.2f\n", cf_raio_x))
CF (raio-X alterado):      0.80
cat(sprintf("CF combinado (1+2):        %.2f\n", cf_combinado_1))
CF combinado (1+2):        0.92
cat(sprintf("CF após evidência negativa:%.2f\n", cf_final))
CF após evidência negativa:0.89
cat(sprintf("\nDiagnóstico de pneumonia: %s\n",
    ifelse(cf_final > 0.5, "PROVÁVEL", 
    ifelse(cf_final > 0.2, "possível", "improvável"))))

Diagnóstico de pneumonia: PROVÁVEL

Exemplo em R: Verificação de Regras Lógicas

Simular um motor de inferência simples para triagem de síndrome gripal baseado em regras SE…ENTÃO.

# Fatos do paciente (V/F)
febre     <- TRUE   # temperatura > 38°C
tosse     <- TRUE   # tosse seca
mialgia   <- FALSE  # dor muscular
dispneia  <- FALSE  # falta de ar

# Regras
gripal     <- febre & tosse & mialgia
alerta_uci <- febre & dispneia   # UTI: Unidade de Terapia Intensiva
isolamento <- febre & (tosse | dispneia)

# Resultados
cat("=== Triagem por Regras Lógicas ===\n")
=== Triagem por Regras Lógicas ===
cat(sprintf("Síndrome Gripal:    %s\n", ifelse(gripal, "POSITIVO", "negativo")))
Síndrome Gripal:    negativo
cat(sprintf("Alerta UTI:         %s\n", ifelse(alerta_uci, "POSITIVO", "negativo")))
Alerta UTI:         negativo
cat(sprintf("Indicar Isolamento: %s\n", ifelse(isolamento, "POSITIVO", "negativo")))
Indicar Isolamento: POSITIVO

Limitações da Lógica Clássica em Saúde

  • Tudo é V ou F — não há meio-termo
  • “Febre alta” é verdadeiro ou falso? Depende do limiar — 37,9°C não é febre, mas 38,0°C é?
  • Não modela gradações linguísticas: “dor leve”, “pressão levemente elevada”
  • Regras são rígidas: uma condição fora do limiar invalida toda a regra
  • Não lida bem com dados ruidosos ou imprecisos — comuns em ambientes clínicos

Importante

A linguagem médica é inerentemente vaga e gradual. A lógica clássica força uma precisão artificial que muitas vezes não existe nos dados reais.

Exercício 1 — Bloco A

Exercício: escreva a tabela-verdade completa da fórmula \(\neg p \vee (p \rightarrow q)\) e verifique com R se é uma tautologia (sempre verdadeira).

p <- c(TRUE, TRUE, FALSE, FALSE)
q <- c(TRUE, FALSE, TRUE, FALSE)

resultado <- !p | (p & q) | (!p)
# Equivalente a: ¬p ∨ (p → q) = ¬p ∨ (¬p ∨ q) = ¬p ∨ q
resultado_simples <- !p | (!p | q)

data.frame(
  p = p, q = q,
  `neg_p` = !p,
  `p→q` = !p | q,
  `neg_p ∨ (p→q)` = !p | (!p | q),
  check.names = FALSE
)
      p     q neg_p   p→q neg_p ∨ (p→q)
1  TRUE  TRUE FALSE  TRUE          TRUE
2  TRUE FALSE FALSE FALSE         FALSE
3 FALSE  TRUE  TRUE  TRUE          TRUE
4 FALSE FALSE  TRUE  TRUE          TRUE

Exercício 2 — Bloco A: Sistemas Especialistas em R

Exercício: implemente um sistema especialista simplificado para classificar risco de desidratação em pacientes pediátricos com base nos critérios da OMS: turgor cutâneo reduzido, olhos fundos e mucosas secas. Classifique: sem desidratação, desidratação leve, desidratação grave.

classificar_desidratacao <- function(turgor_reduzido, olhos_fundos, mucosas_secas) {
  # Regras da OMS (simplificadas)
  # Grave: todos os 3 sinais presentes
  # Leve: 1 ou 2 sinais presentes
  # Sem: nenhum sinal

  n_sinais <- as.integer(turgor_reduzido) +
              as.integer(olhos_fundos) +
              as.integer(mucosas_secas)

  ifelse(n_sinais == 3, "GRAVE",
  ifelse(n_sinais >= 1, "Leve",
                        "Sem desidratação"))
}

# Cenários clínicos
dados <- data.frame(
  turgor   = c(TRUE,  TRUE,  FALSE, FALSE),
  olhos    = c(TRUE,  TRUE,  FALSE, TRUE),
  mucosas  = c(TRUE,  FALSE, FALSE, FALSE)
)
dados$classificacao <- classificar_desidratacao(
  dados$turgor, dados$olhos, dados$mucosas
)
print(dados)
  turgor olhos mucosas    classificacao
1   TRUE  TRUE    TRUE            GRAVE
2   TRUE  TRUE   FALSE             Leve
3  FALSE FALSE   FALSE Sem desidratação
4  FALSE  TRUE   FALSE             Leve

A Tabela-Verdade do Casamento


Marcelo Juliana Conclusão
Errado Certa Juliana está certa
Certo Certa Juliana está certa
Certo Errada Juliana está certa
Errado Errada Marcelo está errado


“Qualquer semelhança com a realidade é mera coincidência.”

Bloco B — Lógicas Não Clássicas

Outras Lógicas

  • Lógica Modal: adiciona os operadores de possibilidade (\(\Diamond p\) — “é possível que \(p\)”) e necessidade (\(\Box p\) — “é necessariamente verdade que \(p\)”). Usada implicitamente quando listamos diagnósticos diferenciais (“é possível que seja pneumonia”) ou definimos protocolos obrigatórios (“necessariamente isolar”).

  • Lógica Temporal: adiciona operadores para raciocinar sobre sequência e duração de eventos (\(p \mathcal{U} q\) — “\(p\) até que \(q\)”). Base dos sistemas de alertas com janela de tempo em UTI (Unidade de Terapia Intensiva): “se SpO₂ (saturação periférica de oxigênio) < 90% por mais de 30 segundos, acionar alarme”.

Não vamos aprofundar essas lógicas aqui — o importante é saber que existem e têm aplicações em SAD clínicos. O foco deste bloco é a lógica não monotônica, muito mais diretamente útil para o raciocínio diagnóstico.

Lógica Não Monotônica

  • Na lógica clássica, uma vez provado algo, é sempre verdadeiro (monotônico)
  • Na prática médica, conclusões mudam quando novas informações chegam
  • Lógica não monotônica: permite revogar conclusões anteriores

Exemplo clínico:

  1. Paciente com tosse + febre → suspeita de gripe ✓
  2. Novo dado: contato com caso de tuberculose confirmado → revisão da suspeita
  3. Novo dado: raio-X com cavitação → conclusão anterior revogada → suspeita de TB

Sistemas de apoio à decisão precisam ter essa capacidade de atualização de hipóteses à medida que novos exames chegam.

Revisão de Hipótese em R: Raciocínio Não Monotônico

# Simulação de atualização diagnóstica à medida que chegam novos dados
# (raciocínio não monotônico: hipóteses podem ser revistas)

atualizar_diagnostico <- function(historico_evidencias) {
  # Hipóteses iniciais com pesos
  hipoteses <- c(gripe = 0.6, pneumonia = 0.2, COVID = 0.1, outro = 0.1)
  
  cat("=== Processo de Atualização Diagnóstica ===\n\n")
  
  for (evidencia in historico_evidencias) {
    cat(sprintf("Nova evidência: %s\n", evidencia))
    
    if (evidencia == "febre+tosse") {
      hipoteses["gripe"]     <- hipoteses["gripe"]     * 1.5
      hipoteses["pneumonia"] <- hipoteses["pneumonia"] * 1.3
    } else if (evidencia == "contato_COVID") {
      hipoteses["COVID"]     <- hipoteses["COVID"]     * 4.0
      hipoteses["gripe"]     <- hipoteses["gripe"]     * 0.6
    } else if (evidencia == "raio_x_normal") {
      hipoteses["pneumonia"] <- hipoteses["pneumonia"] * 0.1
    }
    
    # Normalizar para somar 1
    hipoteses <- hipoteses / sum(hipoteses)
    mais_provavel <- names(which.max(hipoteses))
    cat(sprintf("  Diagnóstico mais provável: %s (%.1f%%)\n\n",
                mais_provavel, max(hipoteses) * 100))
  }
  hipoteses
}

evidencias <- c("febre+tosse", "contato_COVID", "raio_x_normal")
final <- atualizar_diagnostico(evidencias)
=== Processo de Atualização Diagnóstica ===

Nova evidência: febre+tosse
  Diagnóstico mais provável: gripe (66.2%)

Nova evidência: contato_COVID
  Diagnóstico mais provável: gripe (41.5%)

Nova evidência: raio_x_normal
  Diagnóstico mais provável: gripe (50.7%)

Comparação das Lógicas

Característica Clássica Não Monotônica Fuzzy
Valores de verdade {V, F} {V, F} [0, 1]
Revisão de crenças
Gradações linguísticas
Fronteiras de conjuntos Rígidas Rígidas Graduais
Robustez a ruído clínico Baixa Média Alta
Uso típico em SAD Regras e alertas Atualização diagnóstica Triagem e dosagem

Bloco C — Lógica Fuzzy

A Motivação: Imprecisão na Linguagem Médica

  • “O paciente está com febre alta” — o que é alto? Acima de 38? De 39? De 40?
  • Pressão elevada” — 135 mmHg é elevada? E 139? E 141?
  • Dor moderada” — como quantificar rigorosamente?
  • Idoso” — 60 anos é idoso? 65? 70?
  • Forçar limiares rígidos cria saltos abruptos clinicamente injustificáveis

O problema do limiar: Um paciente com PAS = 139 mmHg está “normal”. Com 140 mmHg está “hipertenso”. A diferença de 1 mmHg muda o diagnóstico — mas muda de fato o risco clínico?

Conjuntos Crisp vs. Conjuntos Fuzzy

Conjunto Crisp (clássico)

Pertinência \(\in \{0, 1\}\) — binária

“Febre” se temperatura > 38°C:

Temp (°C) Pertence?
37,9 0 (não)
38,0 1 (sim)
39,5 1 (sim)
41,0 1 (sim)

Salto abrupto em 38°C — irreal.

Conjunto Fuzzy

Pertinência \(\mu \in [0, 1]\) — gradual

“Febre Alta” com função trapezoidal:

Temp (°C) \(\mu_{\text{febre alta}}\)
37,5 0,00
38,5 0,33
39,5 0,83
40,5 1,00
41,0 1,00

Transição suave — realista. ✓

Definição Formal de Conjunto Fuzzy

  • Um conjunto fuzzy \(A\) num universo \(U\) é uma função: \[A: U \rightarrow [0, 1]\]
  • \(A(x) = \mu_A(x)\) é o grau de pertinência de \(x\) em \(A\)
  • \(\mu_A(x) = 0\): \(x\) definitivamente não pertence a \(A\)
  • \(\mu_A(x) = 1\): \(x\) definitivamente pertence a \(A\)
  • \(0 < \mu_A(x) < 1\): \(x\) pertence parcialmente a \(A\)
  • Conjuntos crisp são casos especiais: \(\mu_A(x) \in \{0, 1\}\)

Zadeh (1965): “A fuzzy set is a class of objects with a continuum of grades of membership.”

Função de Pertinência Triangular

A função triangular é definida por três parâmetros \((a, b, c)\):

\[\mu_{\text{tri}}(x; a, b, c) = \max\left(0, \min\left(\frac{x-a}{b-a}, \frac{c-x}{c-b}\right)\right)\]

Clique para ver o código
library(ggplot2)

tri_mf <- function(x, a, b, c)
  pmax(0, pmin((x - a)/(b - a), (c - x)/(c - b)))

x <- seq(35, 42, by = 0.02)
df <- rbind(
  data.frame(x=x, mu=tri_mf(x,36.0,36.8,37.5), cat="Normal (36.0–36.8–37.5)"),
  data.frame(x=x, mu=tri_mf(x,37.0,38.0,39.0), cat="Subfebril (37.0–38.0–39.0)"),
  data.frame(x=x, mu=tri_mf(x,38.0,39.0,40.5), cat="Febre Mod. (38.0–39.0–40.5)"),
  data.frame(x=x, mu=tri_mf(x,39.5,41.0,42.0), cat="Febre Alta (39.5–41.0–42.0)")
)

ggplot(df, aes(x=x, y=mu, color=cat)) +
  geom_line(linewidth=1.2) +
  scale_color_manual(values=c("#7ecff7","#a8f77e","#f7c87e","#f07050")) +
  labs(title="Funções Triangulares — Temperatura Corporal (°C)",
       x="Temperatura (°C)", y="Grau de pertinência μ(x)", color="") +
  theme_minimal(base_size=13) +
  theme(legend.position="bottom")

Funções de Pertinência Trapezoidal e Sigmoide

Trapezoidal\((a, b, c, d)\): platô entre \(b\) e \(c\)

\[\mu_{\text{trap}}(x) = \max\!\left(0, \min\!\left(\frac{x-a}{b-a}, 1, \frac{d-x}{d-c}\right)\right)\]

Útil quando há um intervalo de valores “totalmente pertencentes” — ex.: febre definitivamente alta acima de 40°C.

Sigmoide — suave e assimétrica

\[\mu_{\text{sig}}(x; c, k) = \frac{1}{1 + e^{-k(x-c)}}\]

Útil para conceitos que crescem monotonicamente — ex.: “pressão elevada” (não há limite superior claro).

Clique para ver o código
library(ggplot2)
x <- seq(100, 180, by = 0.5)
trap <- pmax(0, pmin((x-120)/10, 1, (175-x)/5))
sig  <- 1 / (1 + exp(-0.15*(x - 140)))
df <- rbind(
  data.frame(x=x, mu=trap, tipo="Trapezoidal — Pressão Normal/Limítrofe"),
  data.frame(x=x, mu=sig,  tipo="Sigmoide — Hipertensão (c=140, k=0.15)")
)
ggplot(df, aes(x=x, y=mu, color=tipo)) +
  geom_line(linewidth=1.2) +
  scale_color_manual(values=c("#7ecff7","#f07050")) +
  geom_vline(xintercept=140, color="#aaa", linetype="dashed") +
  annotate("text", x=140, y=0.6, label="140 mmHg",
           color="#aaa", hjust=-0.1, size=3.5) +
  labs(title="PAS (mmHg)", x="PAS (mmHg)", y="μ(x)", color="") +
  theme_minimal(base_size=11) + theme(legend.position="bottom")

\(\alpha\)-Cortes: Conectando Fuzzy e Crisp

  • Um \(\alpha\)-corte de um conjunto fuzzy \(A\) é o conjunto crisp de todos os elementos com pertinência \(\geq \alpha\):

\[A_\alpha = \{x \in U : \mu_A(x) \geq \alpha\}\]

  • Para \(\alpha = 1\): obtemos o núcleo — os elementos que pertencem completamente
  • Para \(\alpha \to 0\): obtemos o suporte — todos os elementos com pertinência positiva
  • α-cortes permitem converter conclusões fuzzy em decisões binárias quando necessário

Exemplo clínico: SE o grau de suspeita de pneumonia \(> 0{,}7\) (α = 0,7), ENTÃO iniciar antibiótico — usando um α-corte para tomar uma decisão clínica binária a partir de um raciocínio fuzzy.

Variáveis Linguísticas

  • Uma variável linguística é uma variável cujos valores são palavras ou frases da linguagem natural
  • Cada valor linguístico é representado por um conjunto fuzzy
  • Exemplo: a variável linguística “Temperatura” tem os valores:
    • “Normal”, “Subfebril”, “Febre Moderada”, “Febre Alta”, “Febre Muito Alta”
  • Modificadores linguísticos (hedges) alteram as funções de pertinência:
    • Muito febre alta”: \(\mu^2(x)\) — eleva ao quadrado, restringe o conjunto
    • Pouco febre alta”: \(\sqrt{\mu(x)}\) — raiz quadrada, expande o conjunto

Importância clínica: variáveis linguísticas permitem que médicos e enfermeiros descrevam os sistemas fuzzy em linguagem natural, sem precisar especificar matematicamente cada função de pertinência.

Funções de Pertinência: Formas Comuns

Clique para ver o código
library(ggplot2)

x <- seq(35, 42, by = 0.05)

# Triangular: febre moderada (pico em 38.5)
tri <- pmax(0, pmin((x - 37.5)/1, (39.5 - x)/1))

# Trapezoidal: febre alta (plato acima de 39.5)
trap <- pmax(0, pmin((x - 38.5)/1, 1, (42 - x)/0.5))

# Gaussiana: normal (centrada em 36.8)
gauss <- exp(-0.5 * ((x - 36.8)/0.4)^2)

df <- data.frame(
  x = rep(x, 3),
  mu = c(tri, trap, gauss),
  tipo = rep(c("Triangular — Febre Moderada",
               "Trapezoidal — Febre Alta",
               "Gaussiana — Temperatura Normal"), each = length(x))
)

ggplot(df, aes(x = x, y = mu, color = tipo)) +
  geom_line(linewidth = 1.2) +
  scale_color_manual(values = c("#7ecff7", "#f07050", "#a8f77e")) +
  labs(title = "Funções de Pertinência para Temperatura Corporal",
       x = "Temperatura (°C)", y = "Grau de pertinência μ(x)",
       color = "") +
  theme_minimal(base_size = 13) +
  theme(legend.position = "bottom")

Operações em Conjuntos Fuzzy

Dadas duas funções de pertinência \(A\) e \(B\) sobre o mesmo universo \(U\):

Operação Fórmula Analogia crisp
Complemento \(\mu_{\bar{A}}(x) = 1 - \mu_A(x)\) \(\neg p\)
Interseção (E) \(\mu_{A \cap B}(x) = \min(\mu_A(x), \mu_B(x))\) \(p \wedge q\)
União (OU) \(\mu_{A \cup B}(x) = \max(\mu_A(x), \mu_B(x))\) \(p \vee q\)

Exemplo: paciente com \(\mu_{\text{febre alta}}(x) = 0{,}7\) e \(\mu_{\text{taquicardia}}(x) = 0{,}5\)

  • E (ambas condições): \(\min(0{,}7;\; 0{,}5) = 0{,}5\)
  • OU (ao menos uma): \(\max(0{,}7;\; 0{,}5) = 0{,}7\)
  • SEM febre alta: \(1 - 0{,}7 = 0{,}3\)

Operações Fuzzy em R: Implementação Prática

# Funções auxiliares para operações fuzzy
fuzzy_e   <- function(a, b) pmin(a, b)      # interseção (mínimo)
fuzzy_ou  <- function(a, b) pmax(a, b)      # união (máximo)
fuzzy_nao <- function(a)    1 - a           # complemento
fuzzy_prod <- function(a, b) a * b          # produto (t-norma alternativa)

# Cenário clínico: paciente com múltiplos parâmetros avaliados
mu_febre_alta  <- 0.75   # temperatura = 39.5°C
mu_taquicardia <- 0.60   # FC = 108 bpm (Frequência Cardíaca)
mu_hipoxemia   <- 0.40   # SpO2 = 93%

cat("=== Operações Fuzzy para Triagem de Sepse ===\n\n")
=== Operações Fuzzy para Triagem de Sepse ===
cat(sprintf("μ(febre alta):   %.2f\n", mu_febre_alta))
μ(febre alta):   0.75
cat(sprintf("μ(taquicardia):  %.2f\n", mu_taquicardia))
μ(taquicardia):  0.60
cat(sprintf("μ(hipoxemia):    %.2f\n\n", mu_hipoxemia))
μ(hipoxemia):    0.40
cat(sprintf("Febre E Taquicardia (min):    %.2f\n",
            fuzzy_e(mu_febre_alta, mu_taquicardia)))
Febre E Taquicardia (min):    0.60
cat(sprintf("Febre E Taquicardia (prod):   %.2f\n",
            fuzzy_prod(mu_febre_alta, mu_taquicardia)))
Febre E Taquicardia (prod):   0.45
cat(sprintf("Febre OU Hipoxemia (max):     %.2f\n",
            fuzzy_ou(mu_febre_alta, mu_hipoxemia)))
Febre OU Hipoxemia (max):     0.75
cat(sprintf("SEM febre alta (compl.):      %.2f\n",
            fuzzy_nao(mu_febre_alta)))
SEM febre alta (compl.):      0.25
cat(sprintf("Febre E Taquic. E Hipox.(min):%.2f\n",
            fuzzy_e(fuzzy_e(mu_febre_alta, mu_taquicardia), mu_hipoxemia)))
Febre E Taquic. E Hipox.(min):0.40

T-normas e T-conormas

  • O mínimo e o máximo são as operações padrão, mas não as únicas
  • T-norma (AND generalizado): qualquer função \(T: [0,1]^2 \to [0,1]\) satisfazendo condições de comutatividade, associatividade, monotonicidade e \(T(x,1)=x\)
  • T-conorma (OR generalizado): dual da t-norma
Nome T-norma (AND) T-conorma (OR)
Zadeh (Mín/Máx) \(\min(a, b)\) \(\max(a, b)\)
Produto \(a \cdot b\) \(a + b - a \cdot b\)
Łukasiewicz \(\max(0, a+b-1)\) \(\min(1, a+b)\)

Na prática clínica, o produto é frequentemente preferido por ser mais conservador — exige que ambas as condições sejam fortes para que a conjunção seja forte.

Visualizando Operações Fuzzy

Clique para ver o código
library(ggplot2)

# Para visualização, usamos um paciente na zona de transição
# temp=38.2°C: rampa de febre (37.5→38.5), mf = (38.2-37.5)/1 = 0.70
# PAS=136mmHg: rampa de hipertensão (130→140),  mh = (136-130)/10 = 0.60
t_val <- 38.2; p_val <- 136
mf <- pmax(0, pmin((t_val - 37.5) / 1, 1))
mh <- pmax(0, pmin((p_val - 130) / 10, 1))

df_ops <- data.frame(
  Operacao = c("μ(Febre Alta)", "μ(Hipertensão)",
               "E (min)", "OU (max)", "Sem Febre (1-μ)"),
  Valor = c(mf, mh, min(mf, mh), max(mf, mh), 1 - mf)
)

ggplot(df_ops, aes(x = Operacao, y = Valor, fill = Operacao)) +
  geom_col(width = 0.6) +
  scale_fill_manual(values = c("#f07050","#7ecff7","#f7c87e","#a8f77e","#c87ef7")) +
  scale_y_continuous(limits = c(0, 1)) +
  geom_text(aes(label = round(Valor, 2)), vjust = -0.4, size = 4.5) +
  labs(title = sprintf("Operações Fuzzy — Paciente: temp=%.1f°C, PAS=%.0f mmHg",
                       t_val, p_val),
       x = "", y = "Grau de pertinência") +
  theme_minimal(base_size = 13) +
  theme(legend.position = "none")

Regras Fuzzy: Estrutura

  • Regras da forma: SE <antecedente fuzzy> ENTÃO <consequente fuzzy>
  • Antecedentes e consequentes são proposições fuzzy — não V/F, mas com grau
  • Exemplo de base de regras para triagem de dor:
Regra SE (dor) E (duração) ENTÃO (prioridade)
R1 leve curta baixa
R2 leve longa moderada
R3 moderada qualquer moderada
R4 intensa qualquer alta
R5 insuportável qualquer emergência

Construindo uma Base de Regras Clínicas em R

# Base de regras para classificação de risco cardiovascular
# Variáveis: pressão (mmHg), colesterol (mg/dL)
# Saída: risco (0-10)

# Funções de pertinência
mu_pas_normal  <- function(x) pmax(0, pmin((x-100)/20, (130-x)/10))
mu_pas_limit   <- function(x) pmax(0, pmin((x-120)/10, (150-x)/10))
mu_pas_alta    <- function(x) pmax(0, pmin((x-140)/10, 1))

mu_col_normal  <- function(x) pmax(0, pmin((x-100)/50, (200-x)/20))
mu_col_limit   <- function(x) pmax(0, pmin((x-180)/20, (240-x)/20))
mu_col_alto    <- function(x) pmax(0, pmin((x-220)/20, 1))

# Centros das categorias de saída (risco 0-10)
centros_risco <- c(baixo=1.5, moderado=5.0, alto=8.5)

avaliar_risco_cv <- function(pas, col) {
  # Fuzzificação
  mpa_n <- mu_pas_normal(pas); mpa_l <- mu_pas_limit(pas); mpa_a <- mu_pas_alta(pas)
  mco_n <- mu_col_normal(col); mco_l <- mu_col_limit(col); mco_a <- mu_col_alto(col)

  # Regras (min para conjunção)
  R1 <- min(mpa_n, mco_n)  # normal + normal → baixo
  R2 <- min(mpa_l, mco_n)  # limítrofe + normal → moderado
  R3 <- min(mpa_n, mco_l)  # normal + limítrofe → moderado
  R4 <- min(mpa_l, mco_l)  # limítrofe + limítrofe → alto
  R5 <- min(mpa_a, mco_l)  # alto + limítrofe → alto
  R6 <- min(mpa_a, mco_a)  # alto + alto → alto

  # Defuzzificação: média ponderada dos centros
  pesos  <- c(R1, R2+R3, R4+R5+R6)
  escore <- sum(centros_risco * pesos) / sum(pesos)
  escore
}

# Testar três perfis de pacientes
perfis <- data.frame(
  paciente = c("A", "B", "C"),
  PAS      = c(120, 138, 160),
  Col      = c(180, 215, 260)
)
perfis$risco_cv <- round(mapply(avaliar_risco_cv, perfis$PAS, perfis$Col), 2)
print(perfis)
  paciente PAS Col risco_cv
1        A 120 180      1.5
2        B 138 215      8.5
3        C 160 260      8.5

Sistema de Inferência Fuzzy: Visão Geral

Entradas Valores nítidos (temp, PAS, dor...) Fuzzificação Converter entrada em graus μ Inferência Aplicar regras fuzzy Defuzzificação Converter saída fuzzy em número

As quatro etapas:

  1. Fuzzificação: converter entradas nítidas em graus de pertinência
  2. Avaliação das regras: calcular o grau de ativação de cada regra
  3. Agregação: combinar os consequentes de todas as regras ativas
  4. Defuzzificação: converter a saída fuzzy em um valor nítido

Etapa 1: Fuzzificação

  • Converter o valor nítido de entrada em graus de pertinência para cada conjunto fuzzy
  • Exemplo: temperatura = 39,2°C
temp_val <- 39.2

# Funções de pertinência para temperatura
mu_normal   <- pmax(0, pmin((temp_val - 36.0)/0.8, (37.4 - temp_val)/0.6))
mu_subfebril <- pmax(0, pmin((temp_val - 37.0)/0.5, (38.5 - temp_val)/0.5))
mu_febre_mod <- pmax(0, pmin((temp_val - 38.0)/0.7, (40.0 - temp_val)/0.8))
mu_febre_alt <- pmax(0, pmin((temp_val - 39.0)/0.5, 1))

cat(sprintf("Temperatura: %.1f°C\n\n", temp_val))
Temperatura: 39.2°C
cat(sprintf("μ(Normal):       %.3f\n", mu_normal))
μ(Normal):       0.000
cat(sprintf("μ(Subfebril):    %.3f\n", mu_subfebril))
μ(Subfebril):    0.000
cat(sprintf("μ(Febre Mod.):   %.3f\n", mu_febre_mod))
μ(Febre Mod.):   1.000
cat(sprintf("μ(Febre Alta):   %.3f\n", mu_febre_alt))
μ(Febre Alta):   0.400

Etapa 2: Sistema de Inferência Mamdani

  • Método mais intuitivo e amplamente usado em aplicações clínicas (Mamdani & Assilian, 1975)
  • Para cada regra: o grau de ativação é determinado pela t-norma (mín) dos antecedentes
  • O consequente é truncado na altura do grau de ativação

Exemplo com duas regras:

  • R1: SE febre_moderada (\(\mu = 0{,}25\)) E taquicardia (\(\mu = 0{,}6\)) ENTÃO urgência = moderada
    • Ativação: \(\min(0{,}25;\; 0{,}6) = 0{,}25\) → truncar “moderada” em 0,25
  • R2: SE febre_alta (\(\mu = 0{,}40\)) ENTÃO urgência = alta
    • Ativação: \(0{,}40\) → truncar “alta” em 0,40

Etapa 3: Agregação

  • Combinar os consequentes (já truncados) de todas as regras ativas
  • Operação padrão: máximo ponto a ponto das funções de saída truncadas
  • Resulta numa função fuzzy de saída que representa a conclusão combinada do sistema
Clique para ver o código
library(ggplot2)

urgencia <- seq(0, 10, by = 0.05)

# Funções de pertinência da saída: baixa, moderada, alta
mu_baixa <- pmax(0, pmin((urgencia)/3, (4 - urgencia)/1))
mu_mod   <- pmax(0, pmin((urgencia - 3)/2, (8 - urgencia)/2))
mu_alta  <- pmax(0, pmin((urgencia - 6)/1.5, 1))

# Graus de ativação das regras do exemplo anterior
a_R1 <- 0.25; a_R2 <- 0.40

# Consequentes truncados
tronc_mod  <- pmin(mu_mod,  a_R1)
tronc_alta <- pmin(mu_alta, a_R2)

# Agregação: máximo
agregado <- pmax(tronc_mod, tronc_alta)

df <- data.frame(
  x = rep(urgencia, 4),
  mu = c(tronc_mod, tronc_alta, agregado, pmax(mu_mod, mu_alta)),
  tipo = rep(c("R1 truncada (mod, α=0.25)",
               "R2 truncada (alta, α=0.40)",
               "Agregado (máximo)",
               "Original (sem truncamento)"), each = length(urgencia))
)

ggplot(df, aes(x = x, y = mu, color = tipo, linetype = tipo)) +
  geom_line(linewidth = 1.1) +
  scale_color_manual(values = c("#7ecff7","#f07050","#a8f77e","#ccc")) +
  scale_linetype_manual(values = c("solid","solid","solid","dashed")) +
  labs(title = "Agregação Mamdani — Função de Saída Combinada",
       x = "Urgência (0–10)", y = "Grau de pertinência", color = "", linetype = "") +
  theme_minimal(base_size = 13) +
  theme(legend.position = "bottom")

Etapa 4: Defuzzificação

  • Converter a função fuzzy de saída em um único valor nítido
  • Centroide (Centro de Gravidade) — método mais comum:

\[x^* = \frac{\int x \cdot \mu_{\text{agg}}(x) \, dx}{\int \mu_{\text{agg}}(x) \, dx} \approx \frac{\sum_i x_i \cdot \mu_{\text{agg}}(x_i)}{\sum_i \mu_{\text{agg}}(x_i)}\]

urgencia <- seq(0, 10, by = 0.05)
mu_mod   <- pmax(0, pmin((urgencia - 3)/2, (8 - urgencia)/2))
mu_alta  <- pmax(0, pmin((urgencia - 6)/1.5, 1))
tronc_mod  <- pmin(mu_mod,  0.25)
tronc_alta <- pmin(mu_alta, 0.40)
agregado   <- pmax(tronc_mod, tronc_alta)

# Centroide
centroide <- sum(urgencia * agregado) / sum(agregado)
cat(sprintf("Valor defuzzificado (centroide): %.2f / 10\n", centroide))
Valor defuzzificado (centroide): 7.02 / 10
cat(sprintf("Interpretação: urgência %.0f%%\n", centroide * 10))
Interpretação: urgência 70%

Funções de Pertinência na Prática Clínica

Clique para ver o código
library(ggplot2)

# Escala de dor (0-10) com 4 categorias linguísticas
dor <- seq(0, 10, by = 0.05)

mu_leve    <- pmax(0, pmin(1, (3 - dor)/2))
mu_mod     <- pmax(0, pmin((dor - 1)/2, (6 - dor)/2))
mu_intensa <- pmax(0, pmin((dor - 4)/2, (9 - dor)/2))
mu_insup   <- pmax(0, pmin((dor - 7)/2, 1))

df <- data.frame(
  x   = rep(dor, 4),
  mu  = c(mu_leve, mu_mod, mu_intensa, mu_insup),
  cat = rep(c("Leve", "Moderada", "Intensa", "Insuportável"), each = length(dor))
)
df$cat <- factor(df$cat, levels = c("Leve","Moderada","Intensa","Insuportável"))

ggplot(df, aes(x = x, y = mu, color = cat)) +
  geom_line(linewidth = 1.2) +
  scale_color_manual(values = c("#a8f77e","#7ecff7","#f7c87e","#f07050")) +
  labs(title = "Funções de Pertinência — Escala de Dor (0–10)",
       x = "Intensidade da dor (EVA — Escala Visual Analógica)", y = "Grau de pertinência μ(x)",
       color = "Categoria") +
  theme_minimal(base_size = 13)

Aplicação: Triagem de Urgência Clínica

  • Um sistema de triagem fuzzy avalia múltiplas variáveis linguísticas simultaneamente
  • Variáveis de entrada: temperatura, frequência cardíaca, SpO₂, escala de dor
  • Base de regras: definida por especialistas em triagem (protocolo Manchester fuzzy)
  • Saída: escore de urgência contínuo → classificação em cores (verde/amarelo/laranja/vermelho)

Vantagem sobre Manchester clássico: o sistema fuzzy elimina a descontinuidade dos limiares. Um paciente com SpO₂ de 94% (limiar clássico: amarelo) não recebe a mesma classificação que um com 91% — o sistema fuzzy os diferencia adequadamente.

Visualizando o Sistema de Triagem Completo

Clique para ver o código
library(ggplot2)

# Grade de temperatura x SpO2 → escore de urgência
temp_grid <- seq(36.5, 41, by = 0.2)
spo2_grid <- seq(88, 100, by = 0.5)
grade <- expand.grid(temp = temp_grid, spo2 = spo2_grid)

calcular_urgencia <- function(t, s) {
  mu_tf <- pmax(0, pmin((t - 37.5)/1, 1))
  mu_ta <- pmax(0, pmin((t - 39.0)/0.8, 1))
  mu_sn <- pmax(0, pmin((s - 94)/2, 1))
  mu_sl <- pmax(0, pmin((s - 89)/2, (96 - s)/2))
  mu_sg <- pmax(0, pmin((92 - s)/2, 1))

  R1 <- pmin(1 - mu_tf, mu_sn) * 1.5
  R2 <- pmin(mu_tf, mu_sl)     * 4.0
  R3 <- pmin(mu_ta, mu_sl)     * 7.0
  R4 <- pmin(mu_tf, mu_sg)     * 8.5
  R5 <- pmin(mu_ta, mu_sg)     * 9.5

  pesos <- c(R1, R2, R3, R4, R5)
  if (sum(pesos) == 0) return(1)
  sum(pesos) / length(pesos) * 2
}

grade$urgencia <- mapply(calcular_urgencia, grade$temp, grade$spo2)

ggplot(grade, aes(x = temp, y = spo2, fill = urgencia)) +
  geom_tile() +
  scale_fill_gradient2(low = "#a8f77e", mid = "#f7c87e", high = "#f07050",
                       midpoint = 5, limits = c(0, 10),
                       name = "Urgência
(0-10)") +
  labs(title = "Superfície de Urgência — Sistema de Triagem Fuzzy",
       subtitle = "Verde: baixa urgência | Vermelho: emergência",
       x = "Temperatura (°C)", y = "SpO₂ (%)") +
  theme_minimal(base_size = 13)

Exercício 2 — Bloco C: Fuzzificação

Exercício: um paciente chega ao pronto-socorro com SpO₂ = 93%. Calcule os graus de pertinência para as categorias “Normal” (96–100%), “Hipoxemia Leve” (91–95%) e “Hipoxemia Grave” (< 90%) usando funções trapezoidais, e interprete o resultado.

spo2 <- 93

# Funções de pertinência para SpO2
mu_normal    <- pmax(0, pmin((spo2 - 94)/2, 1))
mu_hip_leve  <- pmax(0, pmin((spo2 - 89)/2, (96 - spo2)/2))
mu_hip_grave <- pmax(0, pmin((92 - spo2)/2, 1))

cat(sprintf("SpO2: %d%%\n\n", spo2))
SpO2: 93%
cat(sprintf("μ(Normal):         %.3f\n", mu_normal))
μ(Normal):         0.000
cat(sprintf("μ(Hipoxemia Leve): %.3f\n", mu_hip_leve))
μ(Hipoxemia Leve): 1.500
cat(sprintf("μ(Hipoxemia Grave):%.3f\n", mu_hip_grave))
μ(Hipoxemia Grave):0.000
cat("\nInterpretação: o paciente está predominantemente em hipoxemia leve,\n")

Interpretação: o paciente está predominantemente em hipoxemia leve,
cat("com componente crescente de hipoxemia grave. Monitoramento intensivo indicado.\n")
com componente crescente de hipoxemia grave. Monitoramento intensivo indicado.

Exercício 3 — Bloco C: Sistema Completo em R

Exercício: implemente um mini-sistema de inferência Mamdani para classificar urgência de atendimento com base em temperatura e SpO₂. Calcule para: temp = 39°C, SpO₂ = 93%.

# Entradas
temp_val <- 39.0
spo2_val <- 93.0

# Fuzzificação — Temperatura
mu_t_normal <- pmax(0, pmin((38 - temp_val)/1, 1))
mu_t_febre  <- pmax(0, pmin((temp_val - 37.5)/1, (40.5 - temp_val)/1))
mu_t_alta   <- pmax(0, pmin((temp_val - 39.0)/0.8, 1))

# Fuzzificação — SpO2
mu_s_normal    <- pmax(0, pmin((spo2_val - 94)/2, 1))
mu_s_hip_leve  <- pmax(0, pmin((spo2_val - 89)/2, (96 - spo2_val)/2))
mu_s_hip_grave <- pmax(0, pmin((92 - spo2_val)/2, 1))

# Regras (min para conjunção)
R1_urgencia_baixa   <- min(mu_t_normal, mu_s_normal)
R2_urgencia_mod     <- min(mu_t_febre,  mu_s_hip_leve)
R3_urgencia_alta    <- min(mu_t_alta,   mu_s_hip_leve)
R4_urgencia_critica <- min(mu_t_febre,  mu_s_hip_grave)

cat("=== Graus de ativação das regras ===\n")
=== Graus de ativação das regras ===
cat(sprintf("R1 (urgência baixa):   %.3f\n", R1_urgencia_baixa))
R1 (urgência baixa):   0.000
cat(sprintf("R2 (urgência mod.):    %.3f\n", R2_urgencia_mod))
R2 (urgência mod.):    1.500
cat(sprintf("R3 (urgência alta):    %.3f\n", R3_urgencia_alta))
R3 (urgência alta):    0.000
cat(sprintf("R4 (urgência crítica): %.3f\n", R4_urgencia_critica))
R4 (urgência crítica): 0.000
# Defuzzificação simplificada: média ponderada dos centros
centros  <- c(1.5, 4.0, 7.0, 9.5)   # centros das categorias (0-10)
pesos    <- c(R1_urgencia_baixa, R2_urgencia_mod,
              R3_urgencia_alta, R4_urgencia_critica)
escore   <- sum(centros * pesos) / sum(pesos)

cat(sprintf("\nEscore de urgência: %.2f / 10\n", escore))

Escore de urgência: 4.00 / 10
cat(sprintf("Classificação: %s\n",
    ifelse(escore < 3, "Verde (não urgente)",
    ifelse(escore < 5, "Amarelo (pouco urgente)",
    ifelse(escore < 7, "Laranja (urgente)",
                       "Vermelho (emergência)")))))
Classificação: Amarelo (pouco urgente)

Lógica Fuzzy em Aplicações Reais de Saúde

  • Dosagem de medicamentos: sistemas fuzzy para calcular doses de insulina, heparina ou sedação em UTI com base em múltiplos parâmetros fisiológicos
  • Classificação de imagens médicas: sistemas de auxílio ao diagnóstico por imagem com saída de grau de suspeição (não “positivo/negativo”)
  • Monitoramento contínuo: detecção precoce de deterioração clínica combinando múltiplos sinais com graus de alarme graduais
  • Protocolos de triagem: versões fuzzy dos protocolos de Manchester e ESI, reduzindo paradoxos de limiar

Referência: KLIR & YUAN (1995, caps. 12–13) e NGUYEN & WALKER (2019, cap. 13) trazem exemplos detalhados de sistemas fuzzy em controle e tomada de decisão clínica.

Exercício 4 — Integrador: Lógica Clássica vs. Fuzzy

Exercício: um hospital usa dois sistemas para classificar risco de readmissão de pacientes com insuficiência cardíaca:

  • Sistema A (clássico): risco alto SE idade > 70 E FE (Fração de Ejeção) < 35% E internações > 2 no último ano
  • Sistema B (fuzzy): usa pertinências graduais para “idoso”, “FE muito reduzida” e “múltiplas internações”

Paciente: 68 anos, FE (Fração de Ejeção) = 36%, 2 internações no último ano.

Classifique pelo Sistema A e calcule os graus de ativação no Sistema B.

# Dados do paciente
idade   <- 68; fe <- 36; intern <- 2

# Sistema A (lógica clássica — binário)
risco_a <- (idade > 70) & (fe < 35) & (intern > 2)
cat(sprintf("Sistema A (clássico): %s\n\n",
            ifelse(risco_a, "RISCO ALTO", "risco baixo")))
Sistema A (clássico): risco baixo
# Sistema B (lógica fuzzy)
mu_idoso <- pmax(0, pmin((idade - 60)/15, 1))
mu_fe    <- pmax(0, pmin((40 - fe)/10, 1))
mu_int   <- pmax(0, pmin((intern - 1)/3, 1))

cat("Sistema B (fuzzy):\n")
Sistema B (fuzzy):
cat(sprintf("  μ(idoso):              %.3f\n", mu_idoso))
  μ(idoso):              0.533
cat(sprintf("  μ(FE muito reduzida):  %.3f\n", mu_fe))
  μ(FE muito reduzida):  0.400
cat(sprintf("  μ(múltiplas intern.):  %.3f\n", mu_int))
  μ(múltiplas intern.):  0.333
cat(sprintf("  Grau de risco (min):   %.3f\n", min(mu_idoso, mu_fe, mu_int)))
  Grau de risco (min):   0.333
cat(sprintf("  Grau de risco (prod.): %.3f\n", mu_idoso * mu_fe * mu_int))
  Grau de risco (prod.): 0.071
cat("\nConclusão: Sistema A não detecta risco; Sistema B indica risco moderado.\n")

Conclusão: Sistema A não detecta risco; Sistema B indica risco moderado.

Síntese: Comparando as Abordagens Lógicas

Aspecto Lógica Clássica Lógica Fuzzy
Valores de verdade {V, F} [0, 1]
Fronteiras de conjuntos Rígidas Graduais
Limiares clínicos Arbitrários, binários Suaves, contínuos
Linguagem médica Difícil de capturar Natural
Interpretabilidade Alta Alta
Robustez a ruído Baixa Alta
Aplicações típicas Regras de negócio, alertas simples Triagem, dosagem, monitoramento

Recapitulando a Aula 2

Bloco A — Lógica Clássica

  • Proposições, conectivos, tabelas-verdade
  • Formas normais (FNC, FND)
  • Lógica de predicados e quantificadores
  • Modus Ponens e Modus Tollens
  • Sistemas especialistas: forward/backward chaining
  • Fatores de certeza (MYCIN)

Bloco B — Lógicas Não Clássicas

  • Modal: possibilidade e necessidade
  • Temporal: raciocínio sobre sequências
  • Não monotônica: revisão de hipóteses

Bloco C — Lógica Fuzzy

  • Motivação: imprecisão da linguagem médica
  • Conjuntos fuzzy e funções de pertinência
  • Operações: complemento, interseção, união
  • T-normas e t-conormas
  • Regras fuzzy e bases de regras
  • Sistema de inferência Mamdani (4 etapas)
  • Defuzzificação pelo centroide
  • Aplicações em triagem e risco clínico

Conexão com a Aula 3

  • Na Aula 3 trataremos do Tratamento da Incerteza de forma mais ampla
  • Árvores de decisão: estrutura similar aos sistemas especialistas, mas com probabilidades nos nós de chance
  • Redes bayesianas: generalização probabilística dos sistemas especialistas — combina lógica de predicados com probabilidade condicional (Teorema de Bayes, que revimos na Aula 1)
  • Diferença fundamental: lógica fuzzy trata imprecisão linguística; redes bayesianas tratam incerteza probabilística — são complementares, não concorrentes

Um SAD médico sofisticado pode usar lógica fuzzy para fuzzificar as entradas clínicas e uma rede bayesiana para propagar as crenças diagnósticas. As duas abordagens se somam.

Lista de Verificação: O que Você Deve Saber Após Esta Aula

  1. ✅ Construir tabelas-verdade para fórmulas proposicionais simples
  2. ✅ Identificar modus ponens e modus tollens em argumentos clínicos
  3. ✅ Explicar a diferença entre forward e backward chaining
  4. ✅ Descrever as limitações da lógica clássica para raciocínio clínico
  5. ✅ Definir conjunto fuzzy e função de pertinência
  6. ✅ Realizar as operações de complemento, interseção e união fuzzy
  7. ✅ Descrever as quatro etapas de um sistema de inferência Mamdani
  8. ✅ Calcular a defuzzificação pelo centroide ou média ponderada

Leitura Complementar para a Próxima Aula

  • NGUYEN & WALKER (2019): Caps. 1–2 (conjuntos fuzzy e operações) e Cap. 13 (controle e modelagem fuzzy)
  • KLIR & YUAN (1995): Cap. 1 (introdução) e Cap. 12 (aplicações em engenharia)
  • Para a Aula 3: revisar probabilidade condicional e Teorema de Bayes (já visto na Aula 1); os slides de revisão ainda estarão disponíveis

Na Aula 3 trabalharemos com os livros SCUTARI & DENIS (Bayesian Networks with Examples in R) e KOLLER & FRIEDMAN (Probabilistic Graphical Models), já disponíveis como referências da disciplina.

Referências

Lógica Clássica e Sistemas Especialistas

  • GENESERETH, M.; NILSSON, N. Logical Foundations of Artificial Intelligence. Morgan Kaufmann, 1987.
  • SHORTLIFFE, E. H.; BUCHANAN, B. G. A model of inexact reasoning in medicine. Mathematical Biosciences, v. 23, p. 351–379, 1975. (Artigo original do MYCIN)

Lógica Fuzzy

  • ZADEH, L. A. Fuzzy sets. Information and Control, v. 8, n. 3, p. 338–353, 1965. (Artigo original)
  • KLIR, G. J.; YUAN, B. Fuzzy Sets and Fuzzy Logic: Theory and Applications. Prentice Hall, 1995.
  • NGUYEN, H. T.; WALKER, E. A. A First Course in Fuzzy Logic. 4. ed. CRC Press, 2019.
  • LOOTSMA, F. A. Fuzzy Logic for Planning and Decision Making. Springer, 1997.
  • MAMDANI, E. H.; ASSILIAN, S. An experiment in linguistic synthesis with a fuzzy logic controller. International Journal of Man-Machine Studies, v. 7, p. 1–13, 1975.

Obrigado!

Métodos de Tomada de Decisão · PPGMDS · UFPB

Aula 2 — A Lógica e seus Tipos

Prof. Marcelo R.P. Ferreira · DE-UFPB


Próxima aula: 21/07/2026 — Tratamento da Incerteza