En python
podemos utilizar isinstance
para determinar si un objeto es un entero
In [1]:
isinstance(4,int)
Out[1]:
In [2]:
isinstance([3,4],int)
Out[2]:
Podemos con esta función definir otra que detecte si un objeto es un número es natural
In [3]:
def isnatural(n):
if not isinstance(n,int):
return false
return n>=0
In [4]:
isnatural(3)
Out[4]:
In [5]:
isnatural(-3)
Out[5]:
La funciones sucesor y predecesor quedarían como sigue
In [6]:
sucesor = lambda x: x+1
In [7]:
sucesor(2)
Out[7]:
In [8]:
def prec(n):
if not isnatural(n):
raise TypeError("El argumento debe ser un número natural")
if n==0:
raise ValueError("El 0 no tiene predecesor")
return n-1
In [9]:
prec(1)
Out[9]:
Con esto podemos definir una suma
In [10]:
def suma(m,n):
if n == 0:
return m
return sucesor(suma(m,prec(n)))
In [11]:
suma(2,3)
Out[11]:
Con la librería sympy
podemos hacer cálculo simbólico. En particular, podemos calcular el valor de algunas sumas con parámetros. Una ramificación de sympy
, diofant
, permite resolver algunos de los problemas que tenía sympy
con rsolve
.
In [12]:
from diofant import *
Cuando vamos a utilizar un símbolo tenemos que declararlo primero. Para el ejemplo que sigue vamos a utilizar n
como entero e i
como contador
In [13]:
n, i = symbols("n, i", integer = True)
Calculemos por ejemplo $\sum_{i=1}^n i$
In [14]:
s = Sum(i,(i,1,n))
In [15]:
s
Out[15]:
Si queremos calcular el "valor" de esta sumatoria, podemos utilizar el método doit
In [16]:
s.doit()
Out[16]:
In [17]:
pprint(_)
In [18]:
summation(i,(i,1,n))
Out[18]:
Y una suma de potencias
In [19]:
s=Sum(i**30,(i,1,n))
In [20]:
s.doit()
Out[20]:
In [21]:
a = Symbol("a")
In [22]:
Sum(a**i,(i,1,n)).doit()
Out[22]:
Veamos cómo podemos utilizar sympy
para hacer algunos ejemplos de inducción
Por ejemplo, veamos que para todo $6\mid 7^n-1$ para todo $n\in \mathbb N$. Empezamos definiendo una función que nos devuelva $7^n-1$
In [23]:
f = lambda n: 7**n -1
Primero veamos qué valor tiene en el 0
In [24]:
f(0)
Out[24]:
Si por hipótesis de inducción $f(n)$ es un múltiplo de 6, entonces para probar que $f(n+1)$ también lo es, bastará demostrar que la diferencia $f(n+1)-f(n)$ es un múltiplo de 6
In [25]:
simplify(f(n+1)-f(n))
Out[25]:
In [26]:
f = lambda n: 7**(2*n)+16*n-1
In [27]:
f(0)
Out[27]:
In [28]:
simplify((f(n+1)-f(n)) % 64)
Out[28]:
Por tanto, si $3\times 7^{2n}+1$ es un múltiplo de $4$, habremos acabado
In [29]:
g = lambda n: 3*7**(2*n)+1
In [30]:
g(0)
Out[30]:
In [31]:
simplify((g(n+1)-g(n))%4)
Out[31]:
Veamos cómo las funciones recursivas se pueden acelerar usando memorización de los pasos anteriores
In [32]:
fib = lambda n: n if n<2 else fib(n-2)+fib(n-1)
In [33]:
fib(10)
Out[33]:
In [34]:
fibonacci(10)
Out[34]:
In [35]:
import time
In [36]:
start=time.time()
fib(30)
time.time()-start
Out[36]:
In [37]:
start=time.time()
fibonacci(30)
time.time()-start
Out[37]:
Un posible solución a este problema es ir almacenando los resultados previos de cálculo, lo cual es conocido como memoization
In [38]:
import functools
In [39]:
@functools.lru_cache(maxsize=None)
def fibo(num):
if num < 2:
return num
else:
return fibo(num-1) + fibo(num-2)
In [40]:
start=time.time()
fibo(30)
time.time()-start
Out[40]:
Las ecuaciones en recurrencia se pueden resolver con el comando rsolve
de sympy
Empecemos con un ejemplo:
Empezamos declarando a
como función
In [41]:
a = Function('a')
In [42]:
rsolve(a(n+2)-5*a(n+1)+6*a(n),a(n))
Out[42]:
In [43]:
rsolve(a(n+2)-5*a(n+1)+6*a(n),a(n), {a(0):0,a(1):1})
Out[43]:
Vamos a resolverlo mediante el polinomio característico
In [44]:
x=Symbol("x")
solve(x**2-5*x+6)
Out[44]:
Y por tanto la solución general será de la forma $a_n=u 2^n + v 3^n$, para ciertas constantes $u$ y $v$ que tenemos que encontrar a partir de las condiciones iniciales
Imponemos por tanto que $a_0=0=u+v$ y que $a_1=1=2u+3v$
In [45]:
u,v = symbols("u,v")
solve([u+v,2*u+3*v-1])
Out[45]:
Por lo que $a_n=-2^n+3^n$, como habíamos visto arriba
Veamos un ejemplo de una que no sea homogénea
In [46]:
rsolve(a(n+2)-5*a(n+1)+6*a(n)-n,a(n))
Out[46]:
Y ahora otro ejemplo no lineal
In [47]:
f=a(n)-(n-1)*(a(n-1)+a(n-2))
In [48]:
rsolve(f,a(n),{a(0):1})
Out[48]:
In [49]:
simplify(factorial(n)-(n-1)*(factorial(n-1)+factorial(n-2)))
Out[49]:
Por último, veamos algunos ejemplos de la relación de problemas
In [50]:
from diofant import *
n=Symbol("n", integer = True)
a=Function('a')
rsolve(a(n)-2*a(n-1)-n,a(n),[1])
Out[50]:
Comprobamos la solución
In [51]:
f = lambda n : 3*2**n-n-2
In [52]:
f(0)
Out[52]:
In [53]:
simplify(f(n)-2*f(n-1)-n)
Out[53]:
Veamos ahora un ejemplo con término no lineal
In [54]:
from diofant import *
n=Symbol("n", integer = True)
x=Function('x')
rsolve(x(n)-2*x(n-1)-3**n,x(n),[1])
Out[54]:
In [55]:
f = lambdify(n,_)
In [56]:
f(1)
Out[56]:
In [57]:
f(n)-2*f(n-1)
Out[57]:
In [58]:
expand(_)
Out[58]:
Otro ejemplo más
In [59]:
rsolve(x(n)-2*x(n-1)-n-2**n,x(n),[0])
Out[59]:
In [60]:
f = lambda n: n*2**n
In [61]:
f(n)-2*f(n-1)
Out[61]:
In [62]:
expand(_)
Out[62]:
Los ejemplos anteriores y el siguiente dieron lugar a este informe de error
In [63]:
rsolve(x(n)-2*x(n-1)-3**n*(n+1),x(n),[0])
Out[63]:
In [64]:
f = lambda n:3*2**n-3*3**n+3*n*3**n
In [65]:
f(0)
Out[65]:
In [66]:
f(1)
Out[66]:
In [67]:
f(2)
Out[67]:
In [68]:
f(n)-2*f(n-1)-(n+1)*3**n
Out[68]:
In [69]:
expand(_)
Out[69]:
Resolvamos ahora $x_0=0$, $x_1=1$, $x_n=3x_{n-1}-2x_{n-2}+2^n+2n$.
In [70]:
rsolve(x(n)-3*x(n-1)+2*x(n-2)-2**n-2*n,x(n),[0,1])
Out[70]:
Hagámoslo ahora a mano. La solución tendrá un polinomio característico asociado $(x-2)(x-1)(x-1)^2(x-2)=(x-2)^2(x-1)^3$. Por tanto la solución será de la forma $x_n=r2^n+sn2^n+t 1^n+u n 1^n + vn^2 1^n$.
In [71]:
def f(n):
if n==0:
return 0
if n==1:
return 1
return 3*f(n-1)-2*f(n-2)+2**n+2*n
In [72]:
[f(i) for i in range(5)]
Out[72]:
In [73]:
from diofant import *
In [74]:
r,s,t,u,v = symbols("r,s,t,u,v")
n=Symbol("n", integer=True)
eq = lambda n:r*2**n+s*n*2**n+t+u*n+v*n**2
solve([eq(0)-f(0),eq(1)-f(1),eq(2)-f(2),eq(3)-f(3), eq(4)-f(4)],[r,s,t,u,v])
Out[74]:
In [75]:
eq(n).subs(_)
Out[75]:
In [76]:
f = lambdify(n,_)
In [77]:
simplify(f(n)-3*f(n-1)+2*f(n-2)-2**n-2*n)
Out[77]: