Importante

Você está vendo a versão anterior da nova experiência da Alura que estamos preparando para você. Em breve, ela ganha uma identidade visual novinha totalmente pensada em potencializar seus estudos!

0
respostas

Faça como eu fiz: construindo um sistema inteligente de recomendação

import heapq
from dataclasses import dataclass

@dataclass(frozen=True)
class Produto:
    nome: str
    categoria: str
    probabilidade_conversao: float

    def __post_init__(self):
        if not (0.0 <= self.probabilidade_conversao <= 1.0):
            raise ValueError(
                f"Probabilidade de conversão inválida para {self.nome}: "
                f"{self.probabilidade_conversao}"
            )

    def __repr__(self):
        return f"{self.nome} ({self.categoria}, conv={self.probabilidade_conversao:.2f})"

class AStarRecommendation:
    def __init__(self, produtos: list[Produto]):
        self.produtos = produtos
        self.grafo = self._construir_grafo_completo(produtos)

    def _construir_grafo_completo(self, produtos: list[Produto]):

        grafo = {p: [] for p in produtos}
        for origem in produtos:
            for destino in produtos:
                if origem != destino:
                    custo = self._custo_aresta(destino)
                    grafo[origem].append((destino, custo))
        return grafo

    @staticmethod
    def _custo_aresta(destino: Produto) -> float:
        # custo mínimo pequeno para evitar aresta de custo zero
        return max(1.0 - destino.probabilidade_conversao, 0.01)

    @staticmethod
    def heuristica(produto_atual: Produto, objetivo: Produto) -> float:
        return max(1.0 - objetivo.probabilidade_conversao, 0.0)

    def buscar_melhor_caminho(self, inicio: Produto, objetivo: Produto):
        if inicio not in self.grafo or objetivo not in self.grafo:
            raise ValueError("Produto de início ou objetivo não está no grafo.")

        # fila de prioridade: (f_score, contador, produto)
        contador = 0
        fronteira = [(self.heuristica(inicio, objetivo), contador, inicio)]

        veio_de = {}
        custo_g = {p: float("inf") for p in self.grafo}
        custo_g[inicio] = 0.0

        visitados = set()

        while fronteira:
            _, _, atual = heapq.heappop(fronteira)

            if atual == objetivo:
                return self._reconstruir_caminho(veio_de, atual), custo_g[atual]

            if atual in visitados:
                continue
            visitados.add(atual)

            for vizinho, custo_aresta in self.grafo[atual]:
                novo_custo = custo_g[atual] + custo_aresta
                if novo_custo < custo_g[vizinho]:
                    custo_g[vizinho] = novo_custo
                    f_score = novo_custo + self.heuristica(vizinho, objetivo)
                    veio_de[vizinho] = atual
                    contador += 1
                    heapq.heappush(fronteira, (f_score, contador, vizinho))

        return None, float("inf")

    @staticmethod
    def _reconstruir_caminho(veio_de: dict, atual: Produto) -> list[Produto]:
        caminho = [atual]
        while atual in veio_de:
            atual = veio_de[atual]
            caminho.append(atual)
        caminho.reverse()
        return caminho

    def recomendar(self, produto_atual: Produto, top_n: int = 3):
        resultados = []
        for candidato in self.produtos:
            if candidato == produto_atual:
                continue
            caminho, custo = self.buscar_melhor_caminho(produto_atual, candidato)
            resultados.append((candidato, custo, caminho))

        resultados.sort(key=lambda x: x[1])
        return resultados[:top_n]

if __name__ == "__main__":
    produtos = [
        Produto("Notebook", "Eletrônicos", 0.35),
        Produto("Mouse", "Acessórios", 0.72),
        Produto("Cadeira", "Móveis", 0.48),
        Produto("Teclado", "Acessórios", 0.65),
        Produto("Monitor", "Eletrônicos", 0.55),
        Produto("Fone", "Acessórios", 0.80),
    ]

    sistema = AStarRecommendation(produtos)

    origem = produtos[0]
    destino = produtos[-1]

    caminho, custo = sistema.buscar_melhor_caminho(origem, destino)

    print("=== Melhor caminho (A*) ===")
    print(f"De: {origem}")
    print(f"Para: {destino}")
    print(f"Caminho: {' -> '.join(p.nome for p in caminho)}")
    print(f"Custo total: {custo:.4f}\n")

    print("=== Recomendações a partir do Notebook Gamer ===")
    for produto, custo, caminho in sistema.recomendar(origem, top_n=3):
        print(
            f"- {produto.nome} (custo={custo:.4f}) "
            f"via {' -> '.join(p.nome for p in caminho)}"
        )