# ATIVIDADE 01 PARA NOTA P2
# Matemática Discreta com Python
# Aluno: Samuel Ribeiro
# ============================================================
# FUNÇÃO AUXILIAR - PROTEÇÃO CONTRA ENTRADAS INVÁLIDAS
# ============================================================
# Criei essa função para proteger todas as entradas numéricas do programa.
# Se eu usasse int(input()) diretamente, o programa quebraria com um
# erro fatal (ValueError) caso alguém (vulgo Cristiane) digitasse uma letra
# ou apertasse enter sem digitar nada. Com o try/except, o erro é capturado
# e o programa simplesmente avisa e pede de novo, dentro de um loop
# while True, até receber um número inteiro válido de verdade.
def obter_inteiro(mensagem):
while True:
try:
return int(input(mensagem))
except ValueError:
print("Entrada invalida! Por favor, digite um numero inteiro.")
except EOFError:
print("\nEntrada encerrada.")
return None
# ============================================================
# EXERCÍCIO 1 - OPERAÇÕES E PROPRIEDADES DE CONJUNTOS
# ============================================================
def exercicio_1():
print("\n--- Exercicio 1: Operacoes e Propriedades de Conjuntos ---")
# Optei por ler os elementos como um único texto, pedindo ao usuário
# que separe por vírgula. É mais prático do que perguntar elemento
# por elemento em um loop interativo.
# Captura EOFError caso o stdin acabe (comportamento do OneCompiler).
try:
entrada_a = input("Informe os elementos do conjunto A (separados por virgula): ")
entrada_b = input("Informe os elementos do conjunto B (separados por virgula): ")
except EOFError:
print("\nEntrada encerrada.")
return
# Trocamos as vírgulas por espaços vazios e usamos o split() vazio.
# O split() nativo corta o texto considerando qualquer bloco de espaços,
# o que resolve o problema caso a pessoa digite "1 2 3" ao inves de "1, 2, 3".
# A condicao 'elem not in A' garante que nao havera duplicatas, respeitando
# a definicao matematica de conjunto (sem elementos repetidos).
A = []
for elem in entrada_a.replace(',', ' ').split():
if elem not in A:
A.append(elem)
B = []
for elem in entrada_b.replace(',', ' ').split():
if elem not in B:
B.append(elem)
print("\n[Passo 2] Conjuntos informados:")
print(f"Conjunto A: {A}")
print(f"Conjunto B: {B}")
# UNIÃO (A U B):
# Copiei todos os elementos de A de uma vez com list(A) e depois
# varri o conjunto B. Para cada elemento de B, só adiciono na
# união se ele ainda não estiver lá, evitando repetições.
uniao = list(A)
print("\n[Passo 3.1] Calculando Uniao A U B...")
for elem in B:
if elem not in uniao:
print(f"Adicionando '{elem}' do conjunto B a uniao.")
uniao.append(elem)
print(f"Resultado A U B: {uniao}")
# INTERSEÇÃO (A ∩ B):
# Percorri o conjunto A elemento por elemento. Se aquele elemento
# também existir dentro de B, então ele é comum aos dois conjuntos
# e pertence à interseção. Fui adicionando cada um na lista.
intersecao = []
print("\n[Passo 3.2] Calculando Intersecao A n B...")
for elem in A:
if elem in B:
print(f"O elemento '{elem}' esta em ambos os conjuntos.")
intersecao.append(elem)
print(f"Resultado A n B: {intersecao}")
# DIFERENÇA (A - B):
# Tudo que tem em A e não tem em B. Varro A e salvo apenas
# o que não está contido em B.
diferenca_a_b = []
print("\n[Passo 3.3] Calculando Diferenca A - B...")
for elem in A:
if elem not in B:
diferenca_a_b.append(elem)
else:
print(f"O elemento '{elem}' foi removido de A pois pertence a B.")
print(f"Resultado A - B: {diferenca_a_b}")
# DIFERENÇA (B - A):
# O mesmo raciocínio de forma invertida: varro B e salvo
# apenas o que não está contido em A.
diferenca_b_a = []
print("\n[Passo 3.4] Calculando Diferenca B - A...")
for elem in B:
if elem not in A:
diferenca_b_a.append(elem)
else:
print(f"O elemento '{elem}' foi removido de B pois pertence a A.")
print(f"Resultado B - A: {diferenca_b_a}")
# PRODUTO CARTESIANO (A x B):
# O produto cartesiano exige combinar cada elemento de A com todos
# os de B. Usei dois laços for aninhados: o externo percorre A e
# o interno percorre B. Em cada combinação formo um par ordenado
# (a, b) e guardo na lista.
produto_cartesiano = []
print("\n[Passo 4] Calculando Produto Cartesiano A x B...")
for a in A:
for b in B:
print(f"Formando par: ({a}, {b})")
produto_cartesiano.append((a, b))
print(f"Resultado A x B: {produto_cartesiano}")
# SUBCONJUNTO (A contido em B):
# Parti do princípio que é verdade (a_contido_b = True).
# Se achar qualquer elemento de A que não está em B, mudo para
# False e dou break para parar a busca, poupando processamento.
print("\n[Passo 5] Verificando subconjuntos...")
a_contido_b = True
for elem in A:
if elem not in B:
a_contido_b = False
print(f"Elemento '{elem}' de A nao esta em B. Logo, A nao e subconjunto de B.")
break
if a_contido_b:
print("Todos os elementos de A estao em B. Logo, A e subconjunto de B (A c B).")
# SUBCONJUNTO (B contido em A): mesmo raciocínio, direção inversa.
b_contido_a = True
for elem in B:
if elem not in A:
b_contido_a = False
print(f"Elemento '{elem}' de B nao esta em A. Logo, B nao e subconjunto de A.")
break
if b_contido_a:
print("Todos os elementos de B estao em A. Logo, B e subconjunto de A (B c A).")
# CARDINALIDADE:
# Usei len() para contar o número de elementos de cada conjunto.
# A cardinalidade é simplesmente o tamanho da lista.
print("\n[Passo 6] Cardinalidades:")
print(f"|A| (Cardinalidade de A): {len(A)}")
print(f"|B| (Cardinalidade de B): {len(B)}")
print(f"|A U B| (Cardinalidade da Uniao): {len(uniao)}")
print(f"|A n B| (Cardinalidade da Intersecao): {len(intersecao)}")
# ============================================================
# EXERCÍCIO 2 - MDC, MMC, DIV E MOD
# ============================================================
def exercicio_2():
print("\n--- Exercicio 2: MDC, MMC, DIV e MOD ---")
# Usei obter_inteiro para garantir que so numeros inteiros sejam aceitos.
num1 = obter_inteiro("Informe o primeiro numero inteiro positivo: ")
num2 = obter_inteiro("Informe o segundo numero inteiro positivo: ")
# Se o EOF aconteceu (stdin esgotado no OneCompiler), encerra o exercicio.
if num1 is None or num2 is None:
return
# Trava de seguranca: o MDC e definido para inteiros positivos.
# Alem disso, evita o erro fatal ZeroDivisionError que aconteceria
# se num2 fosse 0 nas operacoes de divisao logo abaixo.
if num1 <= 0 or num2 <= 0:
print("Os numeros devem ser positivos!")
return
# DIV e MOD:
# O operador // já faz a divisão inteira retornando o quociente
# sem casas decimais. O operador % retorna o resto da divisão.
# São operadores nativos do Python, sem precisar de nenhuma função extra.
print("\n[Passo 2.1] Calculando Divisao Inteira (DIV) e Resto (MOD)...")
div_inteira = num1 // num2
resto = num1 % num2
print(f"Calculo: {num1} dividido por {num2}")
print(f"Divisao inteira (DIV): {num1} // {num2} = {div_inteira}")
print(f"Resto da divisao (MOD): {num1} % {num2} = {resto}")
# MDC - ALGORITMO DE EUCLIDES:
# Implementei o algoritmo manualmente com um while. A lógica é:
# enquanto o segundo número não for zero, calculo o resto da divisão
# do primeiro pelo segundo. Depois, o primeiro vira o segundo e o
# segundo vira esse resto. Quando o segundo chegar a zero, o MDC
# é o valor que sobrou no primeiro. Imprimo cada passo para mostrar
# o raciocínio completo.
print("\n[Passo 2.2] Calculando MDC pelo Algoritmo de Euclides...")
a = num1
b = num2
while b != 0:
novo_resto = a % b
print(f"Dividindo {a} por {b} -> Resto = {novo_resto}")
a = b
b = novo_resto
mdc = a
print(f"O MDC({num1}, {num2}) e {mdc}")
# MMC A PARTIR DO MDC:
# Usei a propriedade matemática: o produto de dois números é igual
# ao produto do MDC pelo MMC. Isolando o MMC: MMC = (A * B) / MDC.
# Usei // para manter como inteiro, pois essa divisão é sempre exata.
print("\n[Passo 2.3] Calculando MMC a partir do MDC...")
mmc = (num1 * num2) // mdc
print(f"Formula: ({num1} * {num2}) / {mdc}")
print(f"O MMC({num1}, {num2}) e {mmc}")
# ============================================================
# EXERCÍCIO 3 - ARITMÉTICA MODULAR, CONGRUÊNCIA E INVERSO EM Zn
# ============================================================
def exercicio_3():
print("\n--- Exercicio 3: Aritmetica Modular, Congruencia e Inverso em Zn ---")
n = obter_inteiro("Informe o valor de n (para o conjunto Zn): ")
if n is None:
return
if n <= 0:
print("O valor de n deve ser maior que 0.")
return
# Usei list(range(n)) para criar o conjunto Zn.
# O range(n) gera os números de 0 até n-1 automaticamente,
# que é exatamente a definição de Zn = {0, 1, ..., n-1}.
Zn = list(range(n))
print(f"Conjunto Z_{n} = {Zn}")
# TABELA DE ADIÇÃO MODULAR:
# Usei dois laços for aninhados: o externo para as linhas (i)
# e o interno para as colunas (j). O cálculo (i+j)%n garante que
# o resultado sempre fique dentro de Zn. As f-strings com :2
# forçam cada número a ocupar 2 caracteres, deixando a tabela alinhada.
print(f"\n[Passo 2.1] Tabela de Adicao Modular em Z_{n}:")
print(" + | " + " ".join(f"{x:2}" for x in Zn))
print("-" * (5 + 4 * n))
for i in Zn:
linha = f"{i:2} | "
for j in Zn:
resultado = (i + j) % n
linha += f"{resultado:2} "
print(linha)
# TABELA DE MULTIPLICAÇÃO MODULAR:
# Mesma lógica da adição, mas o cálculo é (i*j)%n.
print(f"\n[Passo 2.2] Tabela de Multiplicacao Modular em Z_{n}:")
print(" * | " + " ".join(f"{x:2}" for x in Zn))
print("-" * (5 + 4 * n))
for i in Zn:
linha = f"{i:2} | "
for j in Zn:
resultado = (i * j) % n
linha += f"{resultado:2} "
print(linha)
# CONGRUÊNCIA MODULAR:
# Calculei a%n e b%n separadamente. Dois números são congruentes
# módulo n quando deixam o mesmo resto ao ser divididos por n.
# Então basta comparar se mod_a == mod_b.
print("\n[Passo 3 e 4] Verificacao de Congruencia")
a = obter_inteiro("Informe um numero inteiro a: ")
b = obter_inteiro("Informe um numero inteiro b: ")
if a is None or b is None:
return
mod_a = a % n
mod_b = b % n
print(f"Calculando {a} mod {n} = {mod_a}")
print(f"Calculando {b} mod {n} = {mod_b}")
if mod_a == mod_b:
print(f"Como os restos sao iguais ({mod_a} == {mod_b}), {a} e congruente a {b} (mod {n}).")
else:
print(f"Como os restos sao diferentes ({mod_a} != {mod_b}), {a} NAO e congruente a {b} (mod {n}).")
# INVERSO MULTIPLICATIVO (DESAFIO EXTRA):
# O inverso de x em Zn é um número y tal que (x * y) mod n = 1.
# Para encontrar, fiz uma busca percorrendo todos os elementos de Zn.
# O primeiro y que der resultado 1 é o inverso. Guardo ele e saio
# do loop com break. Se varrer o Zn inteiro sem encontrar, o elemento
# não possui inverso multiplicativo.
print("\n[Desafio Extra]")
x = obter_inteiro(f"Informe um elemento x pertencente a Z_{n} (entre 0 e {n-1}): ")
if x is None:
return
if x < 0 or x >= n:
print(f"O elemento {x} nao pertence a Z_{n}.")
return
print(f"Buscando o inverso multiplicativo de {x} em Z_{n}...")
inverso = None
for y in Zn:
produto = (x * y) % n
print(f"Testando {y}: ({x} * {y}) mod {n} = {produto}")
if produto == 1:
inverso = y
break
if inverso is not None:
print(f"-> O inverso multiplicativo de {x} em Z_{n} e {inverso}, pois ({x} * {inverso}) mod {n} = 1.")
else:
print(f"-> O elemento {x} NAO possui inverso multiplicativo em Z_{n}.")
# ============================================================
# FUNÇÃO PRINCIPAL - MENU DO PROGRAMA
# ============================================================
# Criei o main com um while True para manter o menu ativo.
# O usuário escolhe qual exercício rodar digitando 1, 2 ou 3,
# ou digita 0 para encerrar. Qualquer outra entrada avisa
# que é inválida e repete o menu.
def main():
print("=" * 50)
print("BEM-VINDO A LISTA DE EXERCICIOS DE MATEMATICA DISCRETA")
print("=" * 50)
while True:
print("\nSelecione uma opcao:")
print("1 - Exercicio 1 (Conjuntos)")
print("2 - Exercicio 2 (MDC, MMC, DIV, MOD)")
print("3 - Exercicio 3 (Aritmetica Modular, Congruencia, Inverso)")
print("0 - Sair")
# Captura EOFError caso o stdin acabe (comportamento do OneCompiler).
try:
opcao = input("\nDigite sua opcao (ou '0' para sair): ")
except EOFError:
print("\nPrograma encerrado.")
break
if opcao == '1':
exercicio_1()
elif opcao == '2':
exercicio_2()
elif opcao == '3':
exercicio_3()
elif opcao == '0':
print("Encerrando o programa...")
break
else:
print("Opcao invalida! Tente novamente.")
if __name__ == "__main__":
main()