<a href="https://colab.research.google.com/github/seldoncode/Python_CoderDojo/blob/main/Python_CoderDojo18.ipynb" target="_parent"></a>

# Matemáticas

### Valor absoluto
Valor positivo independientemente de su signo.


```python
x = -5
print(abs(x))

def valor_absoluto(x):
  if x<0:
    x = -x
  return x

print(valor_absoluto(x))
```

    5
    5


### Potencia de un número


```python
x = 2
y = 5
print(x**y)
print(pow(x,y))

def potencia(x,y):    # y tiene que ser un número entero
  resultado = 1
  for i in range(y):
    resultado *= x
  return resultado

potencia(x,y)
```

    32
    32





    32



### Raiz cuadrada

#### Método 1
* Método muy poco eficiente.
* Admite un error grande, o si queremos que el error admitido sea menor, el tiempo de cálculo será muy grande.


```python
import math
a = 81
print(a**.5)           # elevando a (1/2)
print(math.sqrt(a))    # con la librería math usando la función sqrt

def raiz1(n):
  x = 0
  while x * x <= n:    # buscamos un número (x) que multiplicado por si mismo de el número buscado (n)
    x += 0.001
  return x

print(raiz1(a))
```

    1414.213562373095
    1414.213562373095
    1414.2139999734702


#### Método 2
Método babilónico


```python
import math
x = 2_000_000       # tiene que ser no negativo
print(x**.5)
print(math.sqrt(x))

def raiz2(x):
  b = 1             # base del rectángulo
  h = x / b         # altura del rectángulo
  if x<1: b,h=h,b   # para que conveja el caso en que x<1
  while True:       # deseamos que b y h coincidan para que el rectángulo sea un cuadrado
    med = (b+h)/2   # media
    if abs(med*med - x) < 1e-9:
      return med
    elif med*med -x < 0:
      b = med
    else:
      h = med

print(raiz2(x))
```

    1414.213562373095
    1414.213562373095
    1414.213562373095


#### Método 3
* Es una variante del método anterior.
* En lugar de usar una cierta tolerancia o error admitido para salir del bucle y terminar el cálculo, lo que hacemos es aprovecharnos de que los cálculos con coma flotante llega un momento en que ya no admiten más precisión y que la media se estanca y no mejora en precisión.
* Cuando vemos que la media se ha estancado es cuando finaliza el bucle
* Sabemos que la media se ha estancado porque tomamos su valor de la iteración anterior con la variable old y la comparamos con la nueva media med.
* Cuando ambas son iguales se acaba el bucle.
* Cuando ```med == old```


```python
import math
x = 2_000_000
print(x**.5)
print(math.sqrt(x))

def raiz3(x):
  b = 1
  h = x / b
  if x < 1:
    b, h = h, b
  med = None
  while True:
    old = med      # old es el valor de la media de la anterior iteración
    med = (b+h)/2  # en esta nueva iteración se recalcula el valor de la media
    if med == old: # se termina cuando la media se estanca
      return med
    elif med*med -x < 0:
      b = med
    else:
      h = med

print(raiz3(x))
```

    1414.213562373095
    1414.213562373095
    1414.213562373095


#### Método 4
* Deseamos calcular la raiz cuadrada de x
* Buscamos un infimo (inf) suficientemente bajo, con el que estemos seguros de que su cuadrado es menor que x
* Buscamos un supremo (sup) suficientemente alto, con el que estemos seguros de que su cuadrado es mayor que x
* Si el infimo (inf) no es suficientemente bajo nos metemos en un bucle para hacer mitades hasta que se cumpla que su cuadrado es menor o igual que x
* Si el supremo (sup) no es suficientemente alto nos metemos en un bucle para hacer dobles hasta que se cumpla que su cuadrado es mayor o igual que x


```python
import math
x = 2_000_000
print(x**.5)
print(math.sqrt(x))

def raiz4(x):
  tolerancia = 1e-9
  inf = 1   # infimo
  sup = 100 # supremo
  while inf*inf > x:  # nos aseguramos que el inf es suficientemente bajo
    inf /= 2
  while sup*sup < x:  # nos aseguramos que el sup es suficientemente alto
    sup *= 2
  while True:
    med = (inf+sup)/2   # media
    dif_med = med*med-x # diferencia entre x y la media al cuadrado
    if abs(dif_med) < tolerancia:
      break
    if dif_med > 0:
      sup = med
    else:
      inf = med
  return med

print(raiz4(x))
```

    1414.213562373095
    1414.213562373095
    1414.2135623730953


### Factorial
* El factorial de 5 es:
 - 5! = 5 * 4 * 3 * 2 * 1 = 120
* El factorial de n es:
 - n! = n * (n-1) * (n-2) * ···· * 1

#### Método 1
Usando una librería.


```python
import math
print(math.factorial(5))
print(math.factorial(0))  # por definición, el factorial de cero es uno
```

    120
    1


#### Método 2
Programando nuestra propia función factorial.


```python
def fac(n):
  f = 1
  for i in range(1,n+1):
    f *= i
  return f

print(fac(5))
print(fac(0))
```

    120
    1


### Combinaciones sin repetición
* Calcular el número de combinaciones sin repetición de m elementos tomados de n en n.
* Requiere calcular el factorial.

#### Método 1
$$C^n_m=\frac{m!}{n!*(m-n)!}$$


```python
from math import factorial
def combinaciones_sin_repeticion(m,n):
  if m<n:
    return 0
  else:
    comb = factorial(m) / (factorial(n) * factorial(m-n))
    return comb

print(combinaciones_sin_repeticion(8,3))
```

    56.0


#### Método 2
* Podemos simplificar la expresión para no realizar tantos cálculos.


$$C^8_3 = \frac{8!}{3!*(8-3)!} = \frac{8!}{3!*5!} = \frac{8·7·6·5!}{3!*5!} = \frac{8·7·6}{3·2·1} = 56$$


```python
from math import factorial

def combina_sin_rep(m,n):
  if m<n:
    return 0
  else:
    numerador = denominador = 1
    for i in range(m,m-n,-1):
      numerador *= i
      denominador *= i-(m-n)
    return numerador / denominador

print(combina_sin_rep(8,3))
print(factorial(8) / (factorial(3) * factorial(8-3)))
```

    56.0
    56.0


### Números primos
#### Ver si un número es primo

# Método 1
Medimos tiempos en este algoritmo sin optimizar.


```python
from sympy import isprime
from time import time

def es_primo(n):
  if n<2: return False  # para el caso en que sea n=1
  for i in range(2,n):
    if n%i==0:
      return False
  return True

n = 1234567891

t0 = time()
print(es_primo(n))
t1 = time()
print(isprime(n))
print(t1-t0, "segundos")
```

    True
    True
    170.44832563400269 segundos


# Método 2
Algoritmo optimizado:
 -  buscamos divisores hasta la raiz cuadrada del número analizado
 - solo los impares


```python
from sympy import isprime
from math import sqrt
from time import time

def es_primo(n):  # n tiene que ser un entero mayor que 1
  if n == 2: return True # si n es 2 es primo
  elif n%2==0: return False # si n!=2 es par, entonces no es primo 
  for i in range(3,int(sqrt(n)+1),2):
    if n%i == 0:
      return False
  return True

n = 1234567891

t0 = time()
print(es_primo(n))
t1 = time()
print(isprime(n))
print(t1-t0, "segundos")
```

    True
    True
    0.0044820308685302734 segundos


#### Encontrar todos los primos en un rango
* Crear una función que proporcione todos los números primos que existen en un rango.
* El usuario nos proporciona el número menos y el mayor del rango y se pasan como parámetros a la función

#### Método 1


```python
def encontrar_primos(a, b):
  lista = []
  for n in range(a, b+1):
    contador = 0
    for i in range(1, n+1):
      if n%i == 0:
        contador += 1
    if contador == 2: # Un primo solo es divisible entre 1 y él mismo
      lista.append(n)
  return lista

a = int(input("Número inicial: ") or 1)
b = int(input("Número final: ") or 101)
print(encontrar_primos(a,b))
```

    Número inicial: 
    Número final: 
    [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101]


#### Método 2
* Eliminamos tener que dividir entre 1 y entre n
* Ya no hace falta que el contador sea igual a 2
* Eliminamos el contador y usamos una bandera


```python
def encontrar_primos(a, b):
  lista = []
  if a == 1: # para que no se considere el 1 como primo
    a = 2    # el número inicial fuera 1, asi se comiena en 2
  for n in range(a, b+1):
    primo = True    # bandera, inicialmente consideramos el número como Primo
    for i in range(2, n):
      if n%i == 0:
        primo = False
        break
    if primo:
      lista.append(n)
  return lista

a = int(input("Número inicial: ") or 1)
b = int(input("Número final: ") or 101)
print(encontrar_primos(a,b))
```

    Número inicial: 
    Número final: 
    [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101]


#### Método 3


```python
from math import sqrt

def encontrar_primos(a, b):
  lista = []
  for n in range(a,b+1):
    primo = True
    if n==2:
      lista.append(2)
      continue
    if n%2==0 or n==1:
      continue
    for i in range(3,int(sqrt(n))+1,2):
      if n%i == 0:
        primo = False
        break
    if primo:
      lista.append(n)
  return lista  

a = int(input("Número inicial: ") or 1)
b = int(input("Número final: ") or 101)
print(encontrar_primos(a,b))
```

    Número inicial: 
    Número final: 
    [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101]


#### Método 4  For ... break ... else
* Ver el uso de ```break``` y de ```else``` en un ```for```
* [w3schools](https://www.w3schools.com/python/python_for_loops.asp)
* [documentación oficial](https://docs.python.org/3/tutorial/controlflow.html)
* Las instrucciones que van con el ```else``` se ejecutan al final del bucle ```for```
* El bloque ```else``` NO se ejecutará si el bucle se detiene con una instrucción ```break```.
* Si el ciclo se rompe (```break```), el bloque ```else``` no se ejecuta.


```python
def encontrar_primos(a, b):
  lista = []
  for n in range(a, b+1):
    for i in range(2, n):
      if n % i == 0:
        break
    else:
      lista.append(n)
  return lista

a = int(input("Número inicial: ") or 2) # no empezar en 1
b = int(input("Número final: ") or 101)
print(encontrar_primos(a,b))
```

    Número inicial: 
    Número final: 
    [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101]


# Método 5
* Buscando un método eficiente para números grandes


```python
def encontrar_primos(a, b):
  lista = []
  for n in range(a, b+1):
    if n==2:
      lista.append(2)
      continue
    if n%2==0 or n==1:
      continue
    for i in range(3, int(sqrt(n))+1, 2): # solo impares y hasta la raiz cuadrada
      if n%i == 0:
        break
    else:
      lista.append(n)
  return lista

a = int(input("Número inicial: ") or 1_000_000_000)
b = int(input("Número final: ") or 1_000_000_100)
print(encontrar_primos(a,b))
```

    Número inicial: 
    Número final: 
    [1000000007, 1000000009, 1000000021, 1000000033, 1000000087, 1000000093, 1000000097]


### Descomposición en factores primos
* Dado un número realizar la descomposición en factores primos.
* Ejemplo:
 - 33.880   =   2 × 2 × 2 × 5 × 7 × 11 × 11   =   2^3 × 5 × 7 × 11^2
 - 19800  =  2^3 × 3^2 × 5^2 × 11
 - 12108  =  2^2 × 3 × 1009
 - 19800  =  2^3 × 3^2 × 5^2 × 11
  - 202 = 2 × 101
 - 9900000246 = 2 × 3 × 61 × 27049181


#### Método 1
Generamos una lista con todos los factores primos de un número.


```python
def descomponer(n):
  primos = []
  for i in range(2, n+1):
    while n % i == 0:
      primos.append(i)
      n /= i            # también puede ser: n //= i
  return primos

#print(descomponer(33880))
#print(descomponer(19800))
#print(descomponer(12108))
#print(descomponer(19800))
print(descomponer(202))
print(descomponer(9900000246))
```

    [2, 101]
    [2, 3, 61, 27049181]


#### Método 3
* Intentaremos reducir tiempos de proceso considerando solo los impares mayores de 2



```python
#!/bin/python3
# Descomposición en factores primos

def factoring(n): #descomposición en factores primos
  text= str(n) + ' = '
  i = 1
  for i in range(1, n+1, 2): # impares, hasta el mismo n ya que podría ser primo
    if i==1: i=2             # salvo el 1 que será 2
    counter = 0
    while n % i == 0:
      n /= i
      counter += 1
    if counter == 1:
      text += str(i)+ ' × '
    elif counter > 1:
      text += str(i) + '^' + str (counter) + ' × '
  text += '1'
  return text

if __name__ == "__main__":
  while True:
    try:
      n = int(input('Introduzca el número a factorizar: ') or 202)
      if 1 < n <= 1e10:
        break
      else:
        print('Por favor, introduzca un número en el rango [2,10_000_000_000]')
    except ValueError:
      print('Por favor itroduzca un número entero positivo.')

  print(factoring(n))
```

    Introduzca el número a factorizar: 
    202 = 2 × 101 × 1


#### Método 3
* Similar al método anterior, pero ahora llegamos en el for solo hasta la mitad (aproximadamente) de los valores anteriores.
* Esto es debido a que si n es divisible entre 2 (que es el primer primo), el siguiente n será n/2.
* Con ello nos hemos quitado la mitad de los valores de n para evaluar.


```python
#!/bin/python3
# Descomposición en factores primos

def factoring(n): #descomposición en factores primos
  text= str(n) + ' = '
  i = 1
  for i in range(1, int(n/2)+1, 2):      # impares
    if i==1: i=2                         # salvo el 1 que será 2
    counter = 0
    while n % i == 0:
      n /= i
      counter += 1
    if counter == 1:
      text += str(i)+ ' × '
    elif counter > 1:
      text += str(i) + '^' + str (counter) + ' × '
  if text[-2] == "=":       # si no hay divisores
    text += str(n) + ' × '  # en ese caso el propio n será primo
  text += '1'
  return text

print(factoring(33880))
print(factoring(19800))
print(factoring(12108))
print(factoring(19800))
print(factoring(202))
print(factoring(9900000246))
```

### Múltiplos de un número
* Los múltiplos de un número son infinitos
* Ejemplo
 - Los múltiplos de 5 son: 
  * 5 × 1 = 5
  * 5 × 2 = 10
  * 5 × 3 = 15
  * 5 × 4 = 20
  * 5 × 5 = 25
  * ... / ...

#### Método 1
Múltiplos de un número hasta cierto límite


```python
def multiplos_hasta_limite(numero, limite):
  multiplos = []
  i = 1
  while numero*i <= limite:
    multiplos.append(numero*i)
    i += 1
  return multiplos

multiplos_hasta_limite(5,50)
```




    [5, 10, 15, 20, 25, 30, 35, 40, 45, 50]



#### Método 2
* Múltiplos de un número, indicando el número y la cantidad de múltiplos que queremos obtener


```python
def multiplos_hasta_cantidad(numero, cantidad):
  multiplos = []
  for i in range(1,cantidad+1):
    multiplos.append(numero*i)
  return multiplos

multiplos_hasta_cantidad(5, 10)
```




    [5, 10, 15, 20, 25, 30, 35, 40, 45, 50]



### Divisores de un número
* Son todos los números que son capaces de dividir a un número con división exacta
* La división es exacta cuando el resto es cero
* Ejemplo
 - El 6 es divisor de 30 porque al dividir 30 entre 6 el resultado es 5.0
 - 30 % 6 = 0
 - Los divisores de 30 son: [1, 2, 3, 5, 6, 10, 15, 30]


```python
def divisores(n):
  lista = []
  for i in range(1,n+1):
    if n % i == 0:
      lista.append(i)
  return lista

divisores(30)
```




    [1, 2, 3, 5, 6, 10, 15, 30]



### Máximo común divisor
* Dados variso números, el máximo común divisor de todos ellos se obtiene calculando el máximo de todos los divisores comunes.
* Ejemplo
 - Divisores de 20: 1,2,4,5,10,20
 - Divisores de 12: 1,2,3,4,6,12
 - Divisores comunes: 1,2,4
 - El Máximo Común Divisor es: 4

# Método 1


```python
def divisores(n):
  lista = []
  for i in range(1,n+1):
    if n % i == 0:
      lista.append(i)
  return lista

def mcd(n1,n2):
  d1 = divisores(n1)
  d2 = divisores(n2)
  print(d1)
  print(d2)
  comunes = []
  for i in d1:
    if i in d2:
      comunes.append(i)
  print(comunes)
  return comunes[-1] # retorna el último de los divisores comunes que es el mayor

mcd(60,48)
```

    [1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30, 60]
    [1, 2, 3, 4, 6, 8, 12, 16, 24, 48]
    [1, 2, 3, 4, 6, 12]





    12



#### Método 2
* Por fuerza bruta.
* Recorre todos los número posibles hasta encontrar el máximo común divisor
* Recorremos todos los posibles valores para encontrar el divisor común mayor.
* Este método es más eficiente que el anterior (tarda menos).


```python
def mcd(n1, n2):
  for i in range(min(n1,n2), 1, -1): # el bucle es descendente hasta el mínimo de n1 y n2
    if n1%i == 0 and n2%i == 0:      # i es divisor exacto de n1 y n2
      return i                       # se retorna ese i al ser el primer divisor común

print(mcd(60, 48))
print(mcd(1199092733403101, 50420370))
```

    12
    73


#### Método 3 Algoritmo de Euclides
* Si tenemos dos números enteros positivos, a y b, tales que a > b >= 0:
 - a = b*q + r
* entonces:
 - mcd(a,b) = mcd(b,r)
* Ejemplo 1:
 - buscamos el mcd entre 12 y 8
 - 12 = 8*1 + 4
 - mcd(12,8) = mcd(8,4)
* Ejemplo 2: usando segmentos
 - buscamos el mcd entre 16 y 10
 - 16 % 10 = 6
 - 10 % 6 = 4
 - 6 % 4 = 2
 - 4 % 2 = 0 (fin)
 - mcd(16,10)=mcd(10,6)=mcd(6,4)=mcd(4,2)=mcd(2,0)=2
* Ejemplo 3:
 - buscamos el mcd entre 16 y 9
 - 16 % 9 = 7
 - 9 % 7 = 2
 - 7 % 2 = 1
 - 2 % 1 = 0 (fin)
 - mcd(16,9)=mcd(9,7)=mcd(7,2)=mcd(2,1)=mcd(1,0)=1
 - 16 y 9 son "primos entre si" lo que supone que su único divisor común es 1


```python
def mcd_euclides(n1, n2):
  while True:          # repetimos el proceso
    resto = n1 % n2
    if resto == 0:     # hasta que el resto se cero
      return n2        # al llegar a ese punto se retorna el mcd
    else:              # si no se ha llegado a resto cero
      n1 = n2          # n1 pasa a ser n2
      n2 =resto        # n2 toma el valor del resto

print(mcd_euclides(60,48))
```

    12


#### Método 4
Una variante del código anterior, mejorando un poco el estilo.


```python
def mcd_euclides(n1, n2):
  while n1 % n2 != 0:          # repetimos el proceso mientras no se llege a resto cero
    resto = n1 % n2
    n1 = n2
    n2 = resto
  return n2

print(mcd_euclides(60,48))
```

    12


#### Método 5
Una pequeña variación sobre el código anterior.


```python
def mcd_euclides(n1, n2):
  while n1 % n2 != 0:
    n1, n2 = n2, n1%n2
  return n2

print(mcd_euclides(60,48))
```

    12


##### Tiempos
Veamos cuantos segundos tarda el algoritmo de Euclides (método 5) y el de fuerza bruta (método 2) con números grandes.


```python
import time
a =1199092733403101
b = 50420370
t0 = time.time()
fb = mcd(a,b)
t1 = time.time()
t2 = time.time()
eu = mcd_euclides(a,b)
t3 = time.time()
print(fb, t1-t0)
print(eu, t3-t2)
```

    73 6.726821422576904
    73 4.6253204345703125e-05


### Conjetura de Collatz
* Una conjetura es una proposición matemática que se ha comprobado que se cumple para una gran cantidad de casos pero aún no se ha logrado probar que se cumple para todos los casos posibles.
* Si una conjetura no se cumpliera para un solo caso quedaría refutada.
* Si se llegara a probar que esa conjetura es válida para todos los casos posibles pasaría a ser considerada como un teoréma matemático.
* La [conjetura de Collatz](https://es.wikipedia.org/wiki/Conjetura_de_Collatz) nos dice que si partimos de un número n, entero positivo y hacemos las siguientes operaciones recurrentes, siempre se llega a 1.
 - Si n es par se divide entre 2
 - Si n es impar se multiplica por 3 y luego se suma 1
* Ejemplo:
  - n = 13
  - por ser 13 impar: 3 * 13 + 1 = 40
  - por ser 40 par: 40 / 2 = 20
  - por ser 20 par: 20 / 2 = 10
  - por ser 10 par: 10 / 2 =5
  - por ser 5 impar: 5 * 3 + 1 = 16
  - por ser 16 par: 16 / 2 = 8
  - por ser 8 par: 8 / 2 = 4
  - por ser 4 par: 4 / 2 = 2
  - por ser 2 par: 2 / 2 = 1 (ya hemos llegado a 1)
  - La secuencia es: 13, 40, 20, 10, 5, 16, 8, 4, 2, **1**

#### Método 1
Mostramos la secuencia hasta llegar a 1.


```python
def collatz(n):
  sec = [n]
  while n > 1:
    if n % 2:
      n = 3 * n + 1
    else:
      n //= 2    # poniendo división entera no devuelve números float
    sec.append(n)
  return sec

print(collatz(11))
```

    [11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]


#### Método 2
Comprobamos que se cumple la conjetura para los números hasta un millón.


```python
def collatz(n):
  while n > 1:
    if n % 2:
      n = 3 * n + 1
    else:
      n //= 2
  return n

for i in range(1, 1_000_001):
  if i % 100_000 == 0:
    print("Comprobado hasta el", i)
  if collatz(i) != 1:
    print("No se cumple para el", i)
    break
else:
  print("Se cumple para todos los números del rango indicado.")
```

    Comprobado hasta el 100000
    Comprobado hasta el 200000
    Comprobado hasta el 300000
    Comprobado hasta el 400000
    Comprobado hasta el 500000
    Comprobado hasta el 600000
    Comprobado hasta el 700000
    Comprobado hasta el 800000
    Comprobado hasta el 900000
    Comprobado hasta el 1000000
    Se cumple para todos los números del rango indicado.


### Conjetura de Goldbach
La [Conjetura de Goldbach](https://es.wikipedia.org/wiki/Conjetura_de_Goldbach) dice:
* Todo número par mayor que 2 puede escribirse como suma de dos números primos.
* Hacer un programa que muestre todas las parejas de primos en las que se puede expresar todo número par mayor que 2.
* Ejemplo para el número 14
 - 3+11=14
 - 7+7=14
 - 1+13=14 no vale porque el 1 no se considera primo

#### Método 1


```python
def es_primo(n):
  if n<2:
    return False
  for i in range(2,n):
    if n % i == 0:
      return False
  return True

def lista_primos(n):
  primos = []
  for i in range(2,n):
    if es_primo(i):
      primos.append(i)
  return primos

def parejas(n):
  primos = lista_primos(n)
  listado = []
  for i in primos:
    for j in primos:
      if i+j==n:
        candidato = [i,j]
        candidato.sort()
        if candidato not in listado:
          listado.append([i,j])
  return listado

n = 50
print(lista_primos(n))
print(parejas(n))
```

    [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
    [[3, 47], [7, 43], [13, 37], [19, 31]]


#### Método 2
* Dado el número par n (por ejemplo n = 14)
* Recorremos todos los números primos desde 2 hasta n-1 (hasta 13)
 - Llegamos al primer primo (a) que es 2
 - Si al número n le restamos el primer primo (a), nos quedará otro número (llamado b)
 - Si b es primo, ya tenemos nuestra primera pareja de primos
 - Continuamos con el siguiente primo después del 2, que es 3 y repetimos el proceso
* Al final la parejas se repiten ya que:
 - 3 + 11 = 14
 - 11 + 3 = 14
* Para evitar repetidos añadimos otro condicional para que solo trabaje con números donde a<=b


```python
def es_primo(n):
  if n<2:
    return False
  for i in range(2, n):  # desde 2 hasta n-1
    if n % i == 0:
      return False
  return True

n = 50
for a in range(2,n):
  if es_primo(a):
    b = n - a
    if es_primo(b):
      if a <= b:
        print(f"Pareja de Primos {a} + {b} = {n}")
```

    Pareja de Primos 3 + 47 = 50
    Pareja de Primos 7 + 43 = 50
    Pareja de Primos 13 + 37 = 50
    Pareja de Primos 19 + 31 = 50


#### Método 3
Optimizando el código para evitar que nos den un n impar o un n que no sea mayor que 2.


```python
def es_primo(n):
  if n<2:
    return False
  for i in range(2,n):  # desde 2 hasta n-1
    if n % i == 0:
      return False
  return True

n = 100

if n % 2 == 0 and n > 2:
  print(f"{n} ", end="")
  encontrado = False
  for a in range(2, n):
    if es_primo(a):
      b = n - a
      if es_primo(b):
        encontrado = True
        if a <= b:
          print(f"= {a}+{b} ", end="")
  if not encontrado:
    print("No se ha encontrado ninguna pareja")
else:
  print("No es un número válido")
```

    100 = 3+97 = 11+89 = 17+83 = 29+71 = 41+59 = 47+53 

3e29bac6ff4ecfaab39c4b95a67d9ef5d42b806d
ipynb
Jupyter Notebook
Python_CoderDojo18.ipynb
seldoncode/Python_CoderDojo
2b0e33ac517cae853af00122b14c4e5719d770c3
MIT
Python_CoderDojo18.ipynb
seldoncode/Python_CoderDojo
2b0e33ac517cae853af00122b14c4e5719d770c3
MIT
Python_CoderDojo18.ipynb
seldoncode/Python_CoderDojo
2b0e33ac517cae853af00122b14c4e5719d770c3
MIT
Qwen/Qwen-72B
1. YES
2. YES