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]: