Introduction To Computation and Programming Using Python, Revised - Guttag, John V..en - Es
Introduction To Computation and Programming Using Python, Revised - Guttag, John V..en - Es
com
Introducció n a la
computació n y
programació n usando
Python
Introducció n a la
computació n y
programació n usando
Python
Juan V. Guttag
Reservados todos los derechos. Ninguna parte de este libro puede ser reproducida
en forma alguna por ningú nmedios electró nicos o mecá nicos (incluidas fotocopias,
grabaciones o almacenamiento y recuperació n de informació n) sin el permiso por
escrito del editor.
Los libros de MIT Press se pueden comprar con descuentos especiales por cantidad
para uso comercial o de promoció n de ventas. Para obtener informació n, envíe un
correo electró [email protected] o escriba al Departamento de
Ventas Especiales, The MIT Press, 55 Hayward Street, Cambridge, MA 02142.
bibliotecarioyoFCongresosCatalogació ng-‐en-‐
paginascm
Incluye índice.
ES Bnorte978-‐0-‐262-‐52500-‐8(pbk. :alkpapel)
computadoras. I. Título.QA76.73.P48G882013
005.13'3—dc23
10987654321
A mi familia:
Olga
David
Andrea
Michae
l Mark
Addie
CONTENIDO
PREFACIOxiii...............................................................................................................................................
AGRADECIMIENTOSxv...........................................................................................................................
1 CONSEGUIRINICIADO1...................................................................................................................
2.1.3 INACTIVO13.......................................................................................................................
2.3.1 Entrada18..................................................................................................................................
3.2 ParaBucles23.....................................................................................................................................
3.5 Newton-Raphson32.......................................................................................................................
4.1.3 Alcance37............................................................................................................................
4.2 Especificaciones41..........................................................................................................................
4.3 Recursió n44.......................................................................................................................................
4.3.2 palíndromos48........................................................................................................................
4.4 GlobalVariables50....................................................................................................................
4.5 Mó dulos51..........................................................................................................................................
4.6 Archivos53..........................................................................................................................................
SUPERIOR.. 56 5.1Tuplas56.......................................................................................
5.2.1 Clonación63..........................................................................................
5.2.2 ListaComprensión63............................................................................
5.5 Diccionarios67.............................................................................................
6 PRUEBAS YDEPURACIÓN70................................................................................
6.1 Prueba70......................................................................................................
6.1.3 ConductiblePruebas74..........................................................................
6.2 Depuración76...............................................................................................
7 EXCEPCIONES YASERCIONES84........................................................................
7.1 ManejoExcepciones84......................................................................................
7.3 Afirmaciones90................................................................................................
8.2 herencia99....................................................................................................
8.3.1 Generadores106..................................................................................
9.3.1 ConstanteComplejidad118..........................................................................................
9.3.2 logarítmicoComplejidad118.............................................................................................
9.3.3 LinealComplejidad119........................................................................................................
9.3.4 Log-LinealComplejidad120...............................................................................................
9.3.5 PolinomioComplejidad120................................................................................................
9.3.6 ExponencialComplejidad121............................................................................................
10.3 PicadilloMesas137................................................................................................................
11 PLOTEAR Y MAS SOBRECLASES141........................................................................................
11.1 Trazado usandoPyLab141........................................................................................................
11.2 Trazado de hipotecas, una extensió nEjemplo146..................................................
12 PROGRAMAS ESTOCÁ STICOS, PROBABILIDAD YESTADÍSTICAS152......................
12.1 estocá sticoProgramas153.................................................................................................
12.2 Estadística inferencial ySimulació n155......................................................................
12.3 Distribuciones166........................................................................................................................
12.3.1 Distribuciones normales y confianzaNiveles168.................................................
12.3.2 UniformeDistribuciones170..........................................................................................
13.3 TraicioneroCampos191...........................................................................
14 MONTE CARLOSIMULACIÓN193.............................................................................
14.1 de pascalproblema194...................................................................................
14.4 Hallazgo.............................................................................................................200
15 COMPRENSIÓN EXPERIMENTALDATOS207.........................................................
16.5 Muestreosesgo228.........................................................................................
16.6 ContextoAsuntos229......................................................................................
16.10 JustoCuidado233.........................................................................................
17.1 MochilaProblemas234...................................................................................
17.1.1 AvaroAlgoritmos235...............................................................................
18 DINÁ MICAPROGRAMACION252................................................................................................
18.1 secuencias de fibonacci,Revisado252..........................................................................
18.2 Programació n Diná mica y la Mochila 0/1problema254............................................
18.3 Programació n Diná mica yDivide y vencerá s261....................................................
19 UNA MIRADA RÁ PIDA A LA MÁ QUINAAPRENDIZAJE262............................................
19.1 CaracterísticaVectores264.......................................................................................................
19.2 DistanciaMétricas266.................................................................................................................
19.3 Agrupació n270.......................................................................................................................
19.4 Tipos Ejemplo yClú ster272...............................................................................................
19.5 K-mediasAgrupació n274...................................................................................................
19.6 un artificialEjemplo276.............................................................................................................
19.7 un menos artificialEjemplo280..............................................................................................
19.8 Envasearriba286...........................................................................................................................
PYTHON 2.7 RÁ PIDOREFERENCIA287....................................................................................
ÍNDICE289....................................................................................................................................................
PREFACIO
Este libro se basa en un curso del MIT que se ofrece dos veces al añ o desde 2006. El
curso está dirigido a estudiantes con poca o ninguna experiencia previa en
programació n que deseen comprender los enfoques computacionales para la
resolució n de problemas. Cada añ o, algunos de los estudiantes de la clase utilizan el
curso como un trampolín para cursos de informá tica má s avanzados. Pero para la
mayoría de los estudiantes será su ú nico curso de informá tica.
Debido a que el curso será el ú nico curso de informá tica para la mayoría de los
estudiantes, nos enfocamos en la amplitud en lugar de la profundidad. El objetivo es
brindarles a los estudiantes una breve introducció n a muchos temas, para que
tengan una idea de lo que es posible cuando llegue el momento de pensar en có mo
usar la computació n para lograr una meta. Dicho esto, no es un curso de
“apreciació n de la computació n”. Es un curso desafiante y riguroso en el que los
estudiantes dedican mucho tiempo y esfuerzo a aprender a manipular la
computadora a su antojo.
El objetivo principal de este libro es ayudarlo a usted, el lector, a ser há bil para hacer
un uso productivo de las técnicas computacionales. Debe aprender a aplicar modos
computacionales de pensamientos para enmarcar problemas y guiar el proceso de
extracció n de informació n de datos de manera computacional. El conocimiento
principal que obtendrá de este libro es el arte de la resolució n de problemas
computacionales.
Pero la mayor parte de esta parte del libro está dedicada a temas que no se
encuentran en la mayoría de los textos introductorios: visualización de datos,
pensamiento probabilístico y estadístico, modelos de simulación y uso de
computación para comprender datos.
1Esta fue la supuesta respuesta de Euclides, alrededor del año 300 a. C., a la solicitud del
rey Ptolomeo de una forma más fácil de aprender matemáticas.
EXPRESIONES DE GRATITUD
Eric Grimson, Chris Terman y David Guttag brindaron una ayuda vital. Eric, que es el
Canciller del MIT, logró encontrar el tiempo para leer casi todo el libro con mucho
cuidado. Encontró numerosos errores (incluido un nú mero vergonzoso, para mí, de
errores técnicos) y señ aló los lugares donde faltaban las explicaciones necesarias.
Chris también leyó partes del manuscrito y descubrió errores. También me ayudó a
luchar contra Microsoft Word, al que finalmente convencimos para que hiciera la
mayor parte de lo que queríamos. David superó su aversió n a la informá tica y
corrigió varios capítulos.
Las versiones preliminares de este libro se utilizaron en el curso MIT 6.00 y el curso
MITx 6.00x. Varios estudiantes de estos cursos señ alaron errores. Un estudiante de
6.00x, JC Cabrejas, fue particularmente ú til. Encontró una gran cantidad de errores
tipográ ficos y má s de unos pocos errores té cnicos.
Como todos los profesores exitosos, les debo mucho a mis estudiantes de
posgrado. La foto en la contraportada de este libro me muestra apoyando a
algunos de mis estudiantes actuales. En el laboratorio, sin embargo, son ellos
quienes me apoyan. Ademá s de hacer una excelente investigació n (y permitirme
tomar parte del cré dito por ello), Guha Balakrishnan, Joel Brooks, Ganeshapillai
Gartheeban, Jen Gong, Yun Liu, Anima Singh, Jenna Wiens y Amy Zhao brindaron
comentarios ú tiles sobre este manuscrito. .
Tengo una deuda especial de gratitud con Julie Sussman, PPA Hasta que comencé a
trabajar con Julie, no tenía idea de la diferencia que podía hacer un editor. Había
trabajado con correctores de estilo en libros anteriores y pensé que eso era lo que
necesitaba para este libro. Me equivoqué. Necesitaba un colaborador que pudiera
leer el libro con los ojos de un estudiante y decirme lo que había que hacer, lo que
debería hacerse y lo que podría hacerse si tuviera el tiempo y la energía para hacerlo.
Julie me enterró en "sugerencias" que eran demasiado buenas para ignorarlas. Su
dominio combinado del idioma inglés y la programació n es bastante notable.
Una computadora hace dos cosas, y só lo dos cosas: realiza cá lculos y recuerda los
resultados de esos cá lculos. Pero hace esas dos cosas extremadamente bien. La
computadora típica que se sienta en un escritorio o en un maletín realiza
aproximadamente mil millones de cá lculos por segundo. Es difícil imaginar lo
realmente rá pido que es. Piensa en sostener una pelota a un metro del suelo y
soltarla. Para cuando llegue al piso, su computadora podría haber ejecutado má s
de mil millones de instrucciones. En cuanto a la memoria, una computadora
típica puede tener cientos de gigabytes de almacenamiento. ¿Como de grande es?
Si un byte (la cantidad de bits, normalmente ocho, necesarios para representar
un cará cter) pesara una onza (que no es así), 100 gigabytes pesarían má s de
3.000.000 de toneladas. A modo de comparació n, ese es aproximadamente el
peso de todo el carbó n producido en un añ o en los EE. UU.
2Muchos creen que Heron no fue el inventor de este mé todo y, de hecho, hay alguna
evidencia de que los antiguos babilonios lo conocían bien.
2 Capítulo 1. Primeros pasos
Entonces, ¿có mo se captura esta idea de una receta en un proceso mecá nico? Una
forma sería diseñ ar una má quina diseñ ada específicamente para calcular raíces
cuadradas.
Por extrañ o que parezca, las primeras má quinas informá ticas eran, de hecho,
computadoras de programa fijo, lo que significa que estaban diseñ adas para hacer
cosas muy específicas y, en su mayoría, eran herramientas para resolver un
problema matemá tico específico, por ejemplo, para calcular la trayectoria de una
artillería. caparazó n. Una de las primeras computadoras (construida en 1941 por
Atanasoff y Berry) resolvía sistemas de ecuaciones lineales, pero no podía hacer nada
má s. La má quina bombe de Alan Turing, desarrollada durante la Segunda Guerra
Mundial, fue diseñ ada estrictamente con el propó sito de descifrar los có digos Enigma
alemanes. Algunas computadoras muy simples todavía usan este enfoque. Por
Tanto el programa como los datos que manipula residen en la memoria. Por lo
general, hay un contador de programa que apunta a una ubicació n particular en
la memoria y el cá lculo comienza con la ejecució n de la instrucció n en ese punto.
La mayoría de las veces, el inté rprete simplemente pasa a la siguiente instrucció n
de la secuencia, pero no siempre. En algunos casos, realiza una prueba y, sobre la
base de esa prueba, la ejecució n puede saltar a algú n otro punto en la secuencia
de instrucciones. Esto se llama flujo de control y es esencial para permitirnos
escribir programas que realicen tareas complejas.
En 1936, el matemá tico britá nico Alan Turing describió un dispositivo informá tico
hipoté tico que se ha dado en llamar Má quina Universal de Turing. La má quina
tenía una memoria ilimitada en forma de cinta en la que se podían escribir ceros y
unos, y algunas instrucciones primitivas muy sencillas para mover, leer y escribir
en la cinta. La tesis de Church-Turing establece que si una funció n es computable,
se puede programar una má quina de Turing para calcularla.
Cualquiera que sea el conjunto de primitivas que uno tenga, y cualquiera que sean
los métodos que uno tenga para usarlas, lo mejor y lo peor de la programació n son
lo mismo: la computadora hará exactamente lo que le digas que haga. Esto es
bueno porque significa que puedes hacer que haga todo tipo de cosas divertidas y
ú tiles. Es algo malo porque cuando no hace lo que quieres que haga, normalmente
no tienes a nadie a quien culpar sino a ti mismo.
La semá ntica está tica define qué cadenas sintá cticamente vá lidas tienen un
significado. En inglé s, por ejemplo, la cadena “I are big” tiene la forma
<pronombre> <verbo de enlace> <adjetivo>, que es una secuencia
sintá cticamente aceptable. Sin embargo, no es vá lido en inglé s, porque el
sustantivo “I” es singular y el verbo “are” es plural. Este es un ejemplo de un
error semá ntico está tico. En Python, la secuencia 3.2/'abc' está sintá cticamente
bien formada (<literal> <operador> <literal>), pero
Capítulo 1. Primeros 5
produce un error semá ntico está tico ya que no tiene sentido dividir un nú mero por
una cadena de caracteres.
La situació n con respecto a los errores semá nticos está ticos es un poco má s
compleja. Algunos lenguajes de programació n, por ejemplo, Java, realizan
muchas comprobaciones semá nticas está ticas antes de permitir que se ejecute
un programa. Otros, por ejemplo, C y Python (por desgracia), hacen una
verificació n semá ntica relativamente menos está tica. Python hace una cantidad
considerable de verificació n semá ntica está tica mientras ejecuta un programa.
Sin embargo, no detecta todos los errores semá nticos está ticos. Cuando estos
errores no se detectan, el comportamiento de un programa suele ser
impredecible. Veremos ejemplos de esto má s adelante en el libro.
Siempre que sea posible, los programas deben escribirse de tal manera que
cuando no funcionen correctamente, sea evidente. Discutiremos có mo hacer esto
a lo largo del libro.
Sin embargo, Python tiene varias ventajas sobre muchos otros lenguajes. Es un
lenguaje relativamente simple que es fá cil de aprender. Debido a que Python está
diseñ ado para ser interpretado, puede proporcionar el tipo de retroalimentació n en
tiempo de ejecució n que es especialmente ú til para los programadores novatos.
También hay una gran cantidad de bibliotecas disponibles gratuitamente que
interactú an con Python y brindan una funcionalidad extendida ú til.
Varios de ellos se utilizan en este libro.
8 Capítulo 2. Introducción a
Ahora estamos listos para comenzar a aprender algunos de los elementos bá sicos de
Python. Estos son comunes a casi todos los lenguajes de programació n en concepto,
aunque no necesariamente en detalle.
Se debe advertir al lector que este libro no es de ninguna manera una introducció n
completa a Python. Usamos Python como vehículo para presentar conceptos
relacionados con la resolució n de problemas computacionales y el pensamiento. El
lenguaje se presenta a cuentagotas, segú n sea necesario para este propó sito ulterior.
Las características de Python que no necesitamos para ese propó sito no se presentan
en absoluto. Nos sentimos có modos al no cubrir todo el idioma porque hay
excelentes recursos en línea que describen casi todos los aspectos del idioma.
Cuando enseñ amos el curso en el que se basa este libro, sugerimos a los estudiantes
que confíen en estos recursos gratuitos en línea para obtener material de referencia
de Python.
Recomendamos que inicie un shell de Python ahora y lo use para probar los
ejemplos contenidos en el resto del capítulo. Y, para el caso, también má s adelante
en el libro.
Un comando, a menudo llamado declaració n, instruye al inté rprete para que haga
algo. Por ejemplo, la declaració nimprime '¡Regla de los yanquis!'le indica al
inté rprete que emita la cadena¡Regla de los yanquis!a la ventana asociada con el
shell.
Capitulo 2. Introducción a 9
La secuencia de comandos
imprime '¡Regla de los
yanquis!' print '¡Pero no
en Boston!'
print 'Yankees gobiernan', '¡pero no en Boston!'
Los tipos son escalares o no escalares. Los objetos escalares son indivisibles.
Piense en ellos como los á tomos del lenguaje.7 Los objetos no escalares, por
ejemplo, las cadenas, tienen una estructura interna.
6EnPython 3, imprimir es una funció n en lugar de un comando. Por lo tanto, uno escribiría
print('¡Gobiernan los Yankees!', 'pero no en Boston').
7Sí,
los á tomos no son verdaderamente indivisibles. Sin embargo, dividirlos no es fá cil y
hacerlo puede tener consecuencias que no siempre son deseables.
1 Capítulo 2. Introducción a
El operador == se usa para probar si dos expresiones dan como resultado el mismo
valor, y el operador != se usa para probar si dos expresiones dan como resultado
valores diferentes.
El símbolo >>> es un indicador de shell que indica que el intérprete espera que el
usuario escriba algú n có digo de Python en el shell. La línea debajo de la línea con el
indicador se produce cuando el intérprete evalú a el có digo de Python ingresado en
el indicador, como se ilustra en la siguiente interacció n con el inté rprete:
>>> 3 + 2
5
>>> 3.0 + 2.0
5.0
>>> 3 != 2
Verdadero
El tipo de funció n integrada de Python se puede utilizar para averiguar el tipo de un objeto:
>>> tipo(3)
<escriba 'int'>
>>> tipo(3.0)
<escriba 'flotador'>
Figura
yo+jes la suma dei2.1
yj.Operadores
Siiyjambosen tipos
son tyflotar
En En
de tipo t, el resultado es
unEn t. Si alguno de ellos es unflotar, el resultado es unflotar.
Los operadores aritmé ticos tienen la precedencia habitual. Por ejemplo, * se une
yo-jesimenos
má s estrechamente quej+,. Si
pori yj
loambos
que lason de tipo
expresió n En t, el
x+y*2 seresultado es unEn t.
evalú a multiplicando
Si alguno de ellos es unflotar, el resultado es unflotar.
primero y por 2 y luego sumando el resultado a x. El orden de evaluació n puede ser
cambiado
i*j por
es el producto de i y j. Si i y j son del tipo int, el resultado es un int.
Si cualquiera de ellos es un flotante, el resultado es un flotante.
yo//jes divisió n entera. Por ejemplo, el valor de 6//2 es el int 3 y el
valor de 6//4 es el int 1. El valor es 1 porque la divisió n de enteros
devuelve el cociente e ignora el resto.
yo/jes i dividido por j. En Python 2.7, cuando i y j son del tipo int, el
resultado también es un int; de lo contrario, el resultado es un float. En
este libro, nunca usaremos / para dividir un int por otro. Usaremos //
para hacer eso. (En Python 3, el operador /, gracias a Dios, siempre
devuelve un flotante. Por ejemplo, en Python 3 el valor de 6/4 es 1,5).
yo% jes el resto cuando el int i se divide por el int j. Por lo general,
se pronuncia "i mod j", que es la abreviatura de "i modulo j".
i**jesielevado a the poderj. Siiyjambos son de tipoEn t, el
resultado es unEn t. Si alguno de ellos es unflotar, el resultado es
unflotar.
Los operadores de comparació n son == (igual), != (no igual), > (mayor),
>=(al menos), <, (menos) y <= (como má ximo).
Capitulo 2. Introducción a 1
usar paré ntesis para agrupar subexpresiones, por ejemplo, (x+y)*2 primero suma x e
y, y luego multiplica el resultado por 2.
No unesVerdaderosiaesFALSO, yFALSOsiaesVerdadero.
Primero vincula los nombres pi8 y radio a diferentes objetos de tipo int. Luego vincula
el á rea del nombre a un tercer objeto de tipo int. Esto se representa en el panel
izquierdo de la Figura 2.2.
8Sicree que el valor real deπno es 3, tienes razó n. Incluso demostramos ese hecho en el
Capítulo 15.
1 Capítulo 2. Introducción a
un =3.14159pi = 3.14159
segundo =11,2 de diámetro = 11,2
c =un*(b**2)área = pi*(diámetro**2)
9"¿Lo que hay en un nombre? Lo que llamamos rosa con cualquier otro nombre olería igual de dulce”.
Capitulo 2. Introducción a 1
ya que le permite usar asignació n mú ltiple para intercambiar los enlaces de dos
variables.
imprimirá
x = 3
y = 2
2.1.3 INACTIVO
Escribir programas directamente en el shell es muy inconveniente. La mayoría de
los programadores prefieren usar algú n tipo de editor de texto que sea parte de un
entorno de desarrollo integrado (IDE).
En este libro, usaremos IDLE,10 el IDE que viene como parte del paquete de
instalació n está ndar de Python. IDLE es una aplicació n, como cualquier otra
aplicació n en su computadora. Inícielo de la misma manera que iniciaría
cualquier otra aplicació n, por ejemplo, haciendo doble clic en un icono.
IDLE proporciona
Se dice que un programa para el cual el tiempo má ximo de ejecució n está limitado
por la duració n del programa se ejecuta en tiempo constante. Esto no quiere decir
que cada vez que se ejecute ejecute el mismo nú mero de pasos. Significa que existe
una constante, k, tal que se garantiza que el programa no tomará má s de k pasos para
ejecutarse. Esto implica que el tiempo de ejecució n no crece con el tamañ o de la
entrada al programa.
Los programas de tiempo constante son bastante limitados en lo que pueden hacer.
Considere, por ejemplo, escribir un programa para contar los votos en una elecció n.
Sería realmente sorprendente si uno pudiera escribir un programa que pudiera
hacer esto en un tiempo que fuera independiente del nú mero de votos emitidos. De
hecho, se puede probar que es imposible hacerlo. El estudio de la dificultad
intrínseca de los problemas es el tema de la complejidad computacional.
Volveremos sobre este tema varias veces en este libro.
Se dice que el operador + está sobrecargado: tiene diferentes significados segú n los
tipos de objetos a los que se aplica. Por ejemplo, significa suma cuando se aplica a
dos nú meros y concatenació n cuando se aplica a dos cadenas. El operador * tambié n
está sobrecargado. Significa lo que espera que signifique cuando sus operandos son
ambos nú meros. Cuando se aplica a un int y un str, duplica el str. Por ejemplo, la
expresió n 2*'Juan' tiene el valor
Que exista verificació n de tipo es algo bueno. Convierte los errores por descuido (ya
veces sutiles) en errores que detienen la ejecució n, en lugar de errores que hacen
que los programas se comporten de maneras misteriosas. La verificació n de tipos en
Python no es tan fuertecomo en algunos otros lenguajes de programació n (por ejemplo,
Java). Por ejemplo, está bastante claro qué debería significar < cuando se usa para comparar dos
cadenas o dos nú meros. Pero, ¿cuá l debería ser el valor de '4' < 3? De manera bastante
arbitraria, los diseñ adores de Python decidieron que debería ser Falso, porque todos los valores
numéricos deberían ser menores que todos los valores de tipo str. Los diseñ adores de algunos
otros lenguajes decidieron que dado que tales expresiones no tienen un significado obvio,
deberían generar un mensaje de error.
Las cadenas son uno de varios tipos de secuencias en Python. Comparten las
siguientes operaciones con todos los tipos de secuencia.
2.3.1 Aporte
Python 2.7 tiene dos funciones (consulte el Capítulo 4 para ver una discusió n de las
funciones en Python) que se pueden usar para obtener entradas directamente de un
usuario, input y raw_input.12 Cada una toma una cadena como argumento y la
muestra como un aviso en el shell. . Luego espera a que el usuario escriba algo,
seguido de presionar la tecla Intro. Para raw_input, la línea de entrada se trata como
una cadena y se convierte en el valor devuelto por la funció n; input trata la línea
escrita como una expresió n de Python e infiere un tipo. En este libro, usamos solo
raw_input, que es menos probable que conduzca a programas que se comporten de
manera inesperada.
Considere el có digo
>>> nombre = raw_input('Ingrese su
nombre: ') Ingrese su nombre: George
Washington
>>> print '¿Eres realmente', nombre,
'?' ¿Eres realmente George Washington?
>>> print '¿De verdad eres ' + nombre +
'?' ¿Eres realmente George Washington?
Ahora considera,
Observe que la variable n está ligada a la cadena '3', no al int 3. Así, por ejemplo, el
valor de la expresió n n*4 es '3333' en lugar de 12. La buena noticia es que cada vez
que una cadena es vá lida literal de algú n tipo, se le puede aplicar una conversió n de
tipo.
2.4 Iteración
Un mecanismo de iteració n genérico (también llamado bucle) se muestra en la
Figura 2.4. Como una declaració n condicional, comienza con una prueba. Si la
prueba se evalú a como Verdadero, el programa ejecuta el cuerpo del ciclo una vez y
luego regresa para reevaluar la prueba. Este proceso se repite hasta que la prueba
se evalú a como Falso, despué s de lo cual el control pasa al có digo que sigue a la
declaració n de iteració n.
12Python3 tiene un solo comando, entrada. Algo confuso, la entrada de Python 3 tiene la
misma semá ntica que raw_input en Python 2.7. Imagínate.
Capitulo 2. Introducción a 1
La cuarta vez que se alcanza la prueba, se evalú a como Falso y el flujo de control
continú a con la instrucció n de impresió n que sigue al ciclo.
Cada vez que se ejecuta el cuerpo del ciclo, el valor de itersLeft se reduce
exactamente en 1. Esto significa que si itersLeft comenzó mayor que 0, despué s de un
nú mero finito de iteraciones del ciclo, itersLeft == 0. En este punto, la prueba del
ciclo se evalú a como False y el control continú a con el có digo que sigue a la
instrucció n while.
¿Qué pasa si el valor de x es -1? Algo muy malo sucede. El control entrará en el bucle
y cada iteració n moverá itersLeft má s lejos de 0 en lugar de acercarse a é l. Por lo
tanto, el programa continuará ejecutando el ciclo para siempre (o hasta que ocurra
algo malo, por ejemplo, un error de desbordamiento). ¿Có mo podemos eliminar este
defecto en el programa? Inicializar itersLeft al valor absoluto de x casi funciona. El
ciclo termina, pero imprime un valor negativo. Si también se cambia la declaració n
de asignació n dentro del bucle, a ans = ans+abs(x), el có digo funciona
correctamente.
Ahora hemos cubierto prá cticamente todo lo que necesitamos saber sobre Python
para comenzar a escribir programas interesantes que se ocupen de nú meros y
cadenas. Ahora nos tomamos un breve descanso del aprendizaje del idioma. En el
pró ximo capítulo, usamos Python para resolver algunos problemas simples.
Ahora, insertemos algunos errores y veamos qué sucede. Primero, intente comentar
la declaració n ans = 0. El inté rprete de Python imprime el mensaje de error,
NameError: el nombre 'ans' no está definido, porque el inté rprete intenta encontrar
el valor al que está vinculado ans antes de que se haya vinculado a algo. Ahora,
restaure la inicializació n de ans, reemplace la declaració n ans = ans + 1 por
ans = ans, y trata de encontrar la raíz cúbica de 8. Después de que
te canses de esperar, ingresa "control c" (mantén presionada la tecla
control y la tecla c simultáneamente). Esto lo regresará al indicador
de usuario en el shell.
Vea qué tan grande es el nú mero entero que necesita ingresar antes de que haya
una pausa perceptible antes de que se imprima el resultado.
funció n incorporadarango xen lugar derango, desderango xgenera los valores solo a
medida que los necesita elparabucle.14
Considere el có digo
x = 4
para i en el rango
(0, x): imprime i
se imprime
0
1
2
3
x = 4
para i en el rango
(0, x): imprime i
x = 5
se imprime
0
1
2
3
0
1
0
1
0
1
porque la funció n de rango en el ciclo externo se evalú a solo una vez, pero la
funció n de rango en el ciclo interno se evalú a cada vez que se alcanza la instrucció n
for interna.
totales = 0
para c en '123456789':
total = total + int(c)
imprimir total
suma los dígitos en la cadena indicada por el literal '123456789' e imprime el total.
Probablemente debería comenzar diciendo que necesita una mejor declaració n del
problema. Por ejemplo, ¿qué debería hacer el programa si se le pide que encuentre
la raíz cuadrada de 2? La raíz cuadrada de 2 no es un nú mero racional. Esto
significa que no hay forma de representar con precisió n su valor como una cadena
finita de dígitos (o como un flotante), por lo que el problema planteado
inicialmente no se puede resolver.
X = 25
épsilon = 0.01
paso =
épsilon**2
numGuesses = 0
respuesta = 0.0
while abs(ans**2 - x) >= épsilon y ans <= x:
ans += paso
númeroAdivinas += 1
imprime 'númConjeturas =',
númeroConjeturas si abs(ans**2
- x) >= épsilon:
print 'Error en la raíz cuadrada
de', x else:
Figura 3.3 Aproximación a la raíz cuadrada mediante enumeración exhaustiva
¿Qué crees que sucederá si establecemos x = 0.25? ¿Encontrará una raíz cercana a
0,5? No. La enumeració n exhaustiva es una técnica de bú squeda que funciona solo si
el conjunto de valores buscados incluye la respuesta. En este caso, estamos
enumerando los valores entre 0 y x. Cuando x está entre 0 y 1, la raíz cuadrada de x
no se encuentra en este intervalo. Una forma de solucionar esto es cambiar la
primera línea del ciclo while a
while abs(ans**2 - x) >= épsilon y ans*ans <= x:
¿Qué crees que pasó ? Seguramente existe un nú mero de punto flotante que se
aproxima a la raíz cuadrada de 123456 con una precisió n de 0,01. ¿Por qué nuestro
programa no lo encontró ? El problema es que nuestro tamañ o de paso era
demasiado grande y el programa omitió todas las respuestas adecuadas. Intente
hacer que el paso sea igual a epsilon ** 3 y
Capítulo 3. Algunos programas numéricos 2
Aproximadamente, ¿cuá ntas conjeturas tendrá que hacer? El tamañ o del paso será
0,000001 y la raíz cuadrada de 123456 es de alrededor de 351,36. Esto significa que
el programa tendrá que hacer alrededor de 351 000 000 intentos para encontrar una
respuesta satisfactoria. Podríamos intentar acelerarlo comenzando má s cerca de la
respuesta, pero eso supone que sabemos la respuesta.
Considere el problema de descubrir si una palabra que comienza con una secuencia
dada de letras aparece en algú n diccionario impreso del idioma inglés. La
enumeració n exhaustiva, en principio, funcionaría. Puede comenzar en la primera
palabra y examinar cada palabra hasta que encuentre una palabra que comience con
la secuencia de letras o se quede sin palabras para examinar. Si el diccionario
contiene n palabras, tomaría, en promedio, n/2 sondeos para encontrar la palabra. Si
la palabra no estuviera en el diccionario, necesitaría n sondeos. Por supuesto,
aquellos que han tenido el placer de buscar una palabra en un diccionario físico (en
lugar de en línea) nunca procederían de esta manera.
0 máximo
0 adivinar máximo
X = 25
épsilon = 0,01
número de conjeturas = 0
bajo = 0.0
alto = máximo (1.0, x)
respuesta = (alto + bajo)/2.0
while abs(ans**2 - x) >= épsilon:
imprimir 'bajo =', bajo, 'alto =', alto, 'ans =', ans
numGuesses += 1
si
respuest
a**2 <
x: baja
=
respuest
a
No hay nada especial en el hecho de que estemos usando este algoritmo para
encontrar raíces cuadradas. Por ejemplo, al cambiar un par de 2 a 3, podemos
usarlo para aproximar una raíz cú bica de un nú mero no negativo. En el pró ximo
capítulo presentaremos un mecanismo de lenguaje que nos permite generalizar
este có digo para encontrar cualquier raíz.
Tal vez usted, como la mayoría de las personas, encuentre doblemente sorprendente
que imprima,
1.0 no es 1.0
¿Por qué llega a la clá usula else en primer lugar? Y si de alguna manera llega allí,
¿por qué está imprimiendo una frase tan sin sentido?
Cuando aprendió por primera vez sobre los nú meros decimales, es decir, los
nú meros en base 10, aprendió que un nú mero decimal está representado por una
secuencia de dígitos 0123456789. El dígito má s a la derecha es el lugar 100, el
siguiente dígito a la izquierda el lugar 101, etc. Por ejemplo, la secuencia de dígitos
decimales 302 representa 3*100 + 0*10 + 2*1. ¿Cuá ntos nú meros diferentes se
pueden representar mediante una secuencia de longitud n? Una secuencia de
longitud uno puede representar cualquiera de diez nú meros (0 - 9). Una secuencia
de longitud dos puede representar cien nú meros diferentes (0-99). Má s
generalmente, con una secuencia de longitud n, uno puede representar 10n
nú meros diferentes.
Tal vez porque la mayoría de la gente tiene diez dedos, parece que nos gusta usar
decimales para representar nú meros. Por otro lado, todos los sistemas informá ticos
modernos representan nú meros en binario. Esto no se debe a que las computadoras
nazcan con dos dedos. Esto se debe a que es fá cil construir interruptores de
hardware, es decir, dispositivos que pueden estar en solo uno de dos estados,
encendido o apagado. Que la computadora use una representació n binaria y las
personas una representació n decimal puede llevar a una disonancia cognitiva
ocasional.
¿Qué pasa con la fracció n decimal 1/10, que escribimos en Python como 0.1? Lo
mejor que podemos hacer con cuatro dígitos binarios significativos es (0011, -101).
Esto es equivalente a 3/32, es decir, 0,09375. Si tuviéramos cinco dígitos binarios
significativos, representaríamos 0,1 como (11001, -1000), que equivale a 25/256, es
decir, 0,09765625. ¿Cuá ntos dígitos significativos necesitaríamos para obtener una
representació n de coma flotante exacta de 0.1? ¡Un nú mero infinito de dígitos! No
existen enteros sig y exp tales que sig * 2-exp sea igual a 0,1. Así que no importa
cuá ntos bits elija usar Python (o cualquier otro lenguaje) para representar nú meros
de punto flotante, solo podrá representar una aproximació n a 0.1. En la mayoría de
las implementaciones de Python, hay 53 bits de precisió n disponibles para nú meros
de coma flotante, por lo que los dígitos significativos almacenados para el nú mero
decimal 0.1 será n
11001100110011001100110011001100110011001100110011001
imprimir
1.0 no es 1.0
Ahora vemos que la prueba x == 1.0 produce el resultado Falso porque el valor al que
está vinculada x no es exactamente 1.0. ¿Qué se imprime si agregamos al final de la
clá usula else el có digo print x == 10.0*0.1? Imprime Falso porque durante al menos una
iteració n del bucle, Python se quedó sin dígitos significativos e hizo algunos redondeos.
No es lo que nos enseñ aron nuestros maestros de primaria, pero sumar 0,1 diez veces
no produce el mismo valor que multiplicar 0,1 por 10.
imprimir x
imprima 1.0 en lugar del valor real de la variable x? Porque los diseñ adores de
Python pensaron que sería conveniente para los usuarios si la impresió n hiciera un
redondeo automá tico. Esta es probablemente una suposició n precisa la mayor parte
del tiempo. Sin embargo, es importante tener en cuenta que lo que se muestra no
necesariamente coincide exactamente con el valor almacenado en la má quina.
3.5 Newton-Raphson
El algoritmo de aproximació n má s utilizado suele atribuirse a Isaac Newton. Por
lo general, se le llama método de Newton, pero a veces se le conoce como el
método de Newton-Raphson.15 Puede usarse para encontrar las raíces reales de
muchas funciones, pero lo veremos solo en el contexto de encontrar las raíces
reales de una funció n. polinomio de una variable. La generalizació n a polinomios
con mú ltiples variables es sencilla tanto matemá tica como algorítmicamente.
Newton demostró un teorema que implica que si un valor, llá mese suposició n, es
una aproximació n a la raíz de un polinomio, entonces suponga:
p(suposició n)/p'(suposició n), donde p' es la primera derivada de p, es una mejor
aproximació n.16
15Joseph Raphson publicó un mé todo similar casi al mismo tiempo que Newton.
diecisé isSe
puede pensar que la primera derivada de una funció n f(x) expresa có mo cambia el
valor de f(x) con respecto a los cambios en x. Si no ha encontrado derivados anteriormente,
no se preocupe. No es necesario que los comprenda, ni tampoco los polinomios, para
comprender la implementació n del método de Newton.
Capítulo 3. Algunos programas numéricos 3
Lo que no quiere decir que debas usar solo estas declaraciones. En este punto,
hemos cubierto una gran cantidad de mecanismos del lenguaje, pero el có digo
ha sido una sola secuencia de instrucciones, todas fusionadas. Por ejemplo, en el
ú ltimo capítulo vimos el có digo de la Figura 4.1.
Python proporciona varias características lingü ísticas que hacen que sea
relativamente fá cil generalizar y reutilizar el có digo. Lo má s importante es la
funció n.
Capter4.Divertidoacciones,Salbardilla,undABtr 3
definitivamentenombre de la función(lista de
parámetros formales):cuerpo de función
La secuencia de nombres (X,yen este ejemplo) dentro de los paré ntesis que siguen
al nombre de la funció n se encuentran los pará metros formales de la funció n.
Cuando se usa la funció n, los pará metros formales está n vinculados (como en una
declaració n de asignació n) a los pará metros reales (a menudo denominados
argumentos) de la invocació n de la funció n (también denominada llamada de
funció n). Por ejemplo, la invocació n
máx(3, 4)
uneXa3yya4.
1. Las expresiones que componen los pará metros reales se evalú an y los
pará metros formales de la funció n se vinculan a los valores resultantes. Por
ejemplo, la invocació nmáx(3+4, z)unirá el pará metro formalXa7y el
pará metro formalya cualquier valor de la variableztiene cuando se evalú a la
invocació n.
Ejercicio de dedos:Escriba una funció n isIn que acepte dos cadenas como
argumentos y devuelva True si cualquiera de las cadenas aparece en cualquier
parte de la otra, y False en caso contrario. Sugerencia: es posible que desee
utilizar la operació n str incorporada en.
Los argumentos de palabras clave se usan comú nmente junto con los valores de
pará metros predeterminados. Podemos, por ejemplo, escribir
def printName(firstName, lastName, reverse = False):
if reverse:
imprimir apellido + ', ' + nombre
más:
imprimir nombre, apellido
imprimirá
Olga Puchmajerova
Puchmajerova, Olga
Puchmajerova, Olga
4.1.3 Alcance
Veamos otro pequeñ o ejemplo,
def f(x): #nombre x usado como parámetro
formal y = 1
x = x + y
imprime 'x =',
x devuelve x
x = 3
y = 2
z = f(x) #valor de x usado como parámetro real
print 'z =', z
imprime 'x =',
x imprime 'y
=', y
¿Que esta pasando aqui? En la llamada de f, el pará metro formal x está ligado
localmente al valor del pará metro real x. Es importante notar que aunque los
pará metros reales y formales tienen el mismo nombre, no son la misma variable.
Cada funció n define un nuevo espacio de nombres, también llamado á mbito. El
3 Capter4.Divertidoacciones,Salbardilla,undABtr
El pará metro formal x y la variable local y que se usan en f existen solo dentro del
alcance de la definició n de f. La instrucció n de asignació n x = x + y dentro del cuerpo
de la funció n vincula el nombre local x con el objeto 4. Las asignaciones en f no
tienen ningú n efecto sobre las vinculaciones de los nombres x e y que existen fuera
del alcance de f.
x = 3
z = f(x)
imprime 'x
Capter4.Divertidoacciones,Salbardilla,undABtr 3
Cuando se invoca h desde dentro de f, se crea otro marco de pila, como se muestra en
la columna 3. Este marco contiene solo la variable local z. ¿Por qué no contiene
tambié n x? Se agrega un nombre al á mbito asociado con una funció n solo si ese
nombre es un pará metro formal de la funció n o una variable que está vinculada a un
objeto dentro del cuerpo de la funció n. En el cuerpo de h, x aparece solo en el lado
derecho de una instrucció n de asignació n. La aparició n de un nombre (x en este caso)
que no está ligado a ninguna parte del cuerpo de la funció n (el cuerpo de h) hace que
el intérprete busque el marco de pila anterior asociado con el á mbito dentro del cual
se define la funció n (el marco de pila asociado con f). Si se encuentra el nombre (que
es en este caso) se utiliza el valor al que está vinculado (4). Si no se encuentra allí, se
produce un mensaje de error.
porque las funciones son objetos y se pueden devolver como cualquier otro tipo
de objeto. Por lo tanto, z se puede vincular al valor devuelto por f, y la llamada de
funció n z() se puede usar para invocar la funció n que estaba vinculada al nombre
g dentro de f, aunque el nombre g no se conoce fuera del contexto de f. .
definición g():
imprimi
r xx =
1
x =
3f()
x =
3g()
4.2 Especificaciones
La figura 4.5 define una funció n, findRoot, que generaliza la bú squeda de bisecció n
que usamos para encontrar raíces cuadradas en la figura 4.1. Tambié n contiene una
funció n, testFindRoot, que se puede usar para probar si findRoot funciona o no
segú n lo previsto.
La funció n testFindRoot es casi tan larga como findRoot. Para los programadores sin
experiencia, escribir funciones de prueba como esta a menudo parece ser una pé rdida
de esfuerzo. Sin embargo, los programadores experimentados saben que una inversió n
en escribir có digo de prueba a menudo paga grandes dividendos. Ciertamente es mejor
que escribir casos de prueba en el shell una y otra vez durante la depuració n (el proceso
de descubrir por qué un programa no funciona y luego arreglarlo). También nos obliga a
pensar en qué pruebas son má s esclarecedoras.
El texto entre las comillas triples se llama docstring en Python. Por convenció n, los
programadores de Python usan docstrings para proporcionar especificaciones de
funciones. Se puede acceder a estas cadenas de documentos utilizando la funció n de
ayuda integrada.
Si ingresamos al shell y escribimos ayuda (abs), el sistema mostrará
Ayuda sobre la función incorporada abs en el
módulo incorporado: abs (...)
abs(número) -> número
Devuelve el valor absoluto del argumento.
si escribimos
buscarraiz(
Las funciones son una forma de crear elementos computacionales que podemos
considerar primitivos. Así como tenemos las funciones integradas max y abs, nos
gustaría tener el equivalente de una funció n integrada para encontrar raíces y para
muchas otras operaciones complejas. Las funciones facilitan esto proporcionando
descomposició n y abstracció n.
Capter4.Divertidoacciones,Salbardilla,undABtr 4
La abstracció n tiene que ver con el olvido. Hay muchas formas de modelar esto, por
ejemplo, el aparato auditivo de la mayoría de los adolescentes.
El padre dice: Sí, pero regresa antes de la medianoche y asegú rate de que el
tanque de gasolina esté lleno.
De alguna manera, uno necesita que todos sepan lo que está n haciendo los demá s,
sin generar tanto trabajo que nadie esté dispuesto a participar. Aquí es donde entra
en juego la abstracció n. Se pueden escribir veinticinco especificaciones, cada una de
las cuales indica qué material deben aprender los estudiantes en cada clase, pero sin
dar ningú n detalle sobre có mo se debe enseñ ar ese material. Lo que obtuviste puede
no ser pedagó gicamente maravilloso, pero al menos podría tener sentido.
4.3 recursividad
Es posible que haya oído hablar de la recursividad y, con toda probabilidad,
piense en ella como una técnica de programació n bastante sutil. Esa es una
leyenda urbana difundida por informá ticos para hacer creer a la gente que
somos má s inteligentes de lo que realmente somos.
La recursividad es una idea muy importante, pero no es tan sutil y es má s que una té cnica de
programació n.
Considere parte del có digo legal de los Estados Unidos que define la noció n de
ciudadano "natural". A grandes rasgos, la definició n es la siguiente
La primera ecuació n define el caso base. La segunda ecuació n define factorial para
todos los nú meros naturales, excepto el caso base, en términos del factorial del
nú mero anterior.
22La definició n exacta de "nú mero natural" está sujeta a debate. Algunos lo definen como los
nú meros enteros positivos y otros como los nú meros enteros no negativos. Es por eso que
fuimos explícitos acerca de los posibles valores de n en la cadena de documentació n de la
figura 4.6.
4 Capter4.Divertidoacciones,Salbardilla,undABtr
El ú ltimo día del primer mes (llá melo mes 0), habrá una hembra (lista para concebir
el primer día del pró ximo mes). El ú ltimo día del segundo mes, seguirá habiendo
una sola hembra (ya que no dará a luz hasta el primer día del mes siguiente). El
ú ltimo día del pró ximo mes habrá dos hembras (una preñ ada y otra no). El ú ltimo
día del pró ximo mes habrá tres hembras (dos preñ adas y una no). Etcétera. Veamos
esta progresió n en forma tabular.
Tiene dos casos base, no solo uno. En general, puede tener tantos
casos base como necesite.
En el caso recursivo, hay dos llamadas recursivas, no solo una. Una vez
má s, puede haber tantos como necesite.
def fib(n):
"""Asume n an int >= 0
Devuelve Fibonacci de
n"""
si n == 0 o n ==
1: devuelve 1
demás:
devolver fib(n-1) + fib(n-2)
def testFib(n):
para i en el rango (n+1):
Escribir el có digo es la parte fá cil de resolver este problema. Una vez que
pasamos de la declaració n vaga de un problema sobre conejitos a un conjunto de
ecuaciones recursivas, el có digo casi se escribe solo. Encontrar algú n tipo de
forma abstracta de expresar una solució n al problema en cuestió n es muy a
menudo el paso má s difícil en la construcció n de un programa ú til. Hablaremos
mucho má s sobre esto má s adelante en el libro.
24Eldañ o causado por los descendientes de esos veinticuatro lindos conejitos se ha estimado
en $ 600 millones por añ o, y está n en proceso de comerse muchas plantas nativas hasta la
extinció n.
25Que llamemos a esto una secuencia de Fibonacci es un ejemplo de una interpretació n
eurocé ntrica de la historia. La gran contribució n de Fibonacci a las matemá ticas europeas
fue su libro Liber Abaci, que introdujo a los matemá ticos europeos muchos conceptos ya
bien conocidos por los eruditos indios y á rabes. Estos conceptos incluían nú meros
ará bigos hindú es y el sistema decimal. Lo que hoy llamamos la secuencia de Fibonacci se
tomó del trabajo del matemá tico sá nscrito Pingala.
26Si te sientes especialmente geek, trata de escribir un poema de Fibonacci. Esta es una
forma de poesía en la que el nú mero de sílabas en cada línea es igual al nú mero total de
sílabas en las dos líneas anteriores. Piensa en la primera línea (que tiene cero sílabas) como
un lugar para respirar profundamente antes de comenzar a leer tu poema.
4 Capter4.Divertidoacciones,Salbardilla,undABtr
4.3.2 palíndromos
La recursividad tambié n es ú til para muchos problemas que no involucran nú meros. Cifra
4.8 contiene una funció n, isPalindrome, que verifica si una cadena se lee de la
misma manera hacia adelante y hacia atrá s.
27Cuando dos expresiones con valores booleanos está n conectadas por "y", cada
expresió n se llama conjunto. Si está n conectados por “o”, se llaman disjuntos.
Capter4.Divertidoacciones,Salbardilla,undABtr 4
def
toChars(s):
s =
s.lower()
letras = ''
para c en
s:
si c en
'abcdefghijklmnopqrstuvwxyz':
letras = letras + c
devolver cartas
def es amigo(s):
print ' isPal llamó con', s
if len(s) <= 1:
print 'A punto de devolver True desde el
caso base' return True
demás:
respuesta = s[0] == s[-1] and isPal(s[1:-1])
print 'A punto de regresar', answer, 'for',
s return answer
return
isPal(toChars(s)) def
5 Capter4.Divertidoacciones,Salbardilla,undABtr
Divide y vencerá s es una idea muy antigua. Julio Cé sar practicó lo que los
romanos llamaban divide et impera (divide y vencerá s). Los britá nicos lo
practicaron brillantemente para controlar el subcontinente indio. Benjamin
Franklin era muy consciente de la experiencia britá nica en el uso de esta té cnica,
lo que lo llevó a decir en la firma de la Declaració n de Independencia de los EE.
UU.: "Debemos estar todos juntos, o seguramente todos seremos colgados por
separado".
Hasta ahora, todas las funciones que hemos escrito se comunican con su entorno
ú nicamente a travé s de sus pará metros y valores de retorno. En su mayor parte,
esto es exactamente como debería ser. Por lo general, conduce a programas que
son relativamente fá ciles de leer, probar y depurar. De vez en cuando, sin
embargo, las variables globales resultan ú tiles. Considere el có digo de la figura
4.10.
Capter4.Divertidoacciones,Salbardilla,undABtr 5
def fib(x):
"""Asume x an int >= 0
Devuelve Fibonacci de
x"""
global
numFibCalls
numFibCalls += 1
si x == 0 o x ==
1: devuelve 1
demás:
devolver fib(x-1) + fib(x-2)
def testFib(n):
for i in range(n+1):
global
numFibCalls
numFibCalls = 0
Figura 4.10 Uso de una variable global
En cada funció n, la línea de có digo global numFibCalls le dice a Python que el nombre
numCalls debe definirse en el á mbito má s externo del mó dulo (consulte la Secció n 4.5)
en el que aparece la línea de có digo en lugar de dentro del á mbito de la funció n en la que
la línea de có digo aparece, a pesar del hecho de que numFibCalls aparece en el lado
izquierdo de una instrucció n de asignació n tanto en fib como en testFib. (Si no
hubié ramos incluido el có digo global numFibCalls, el nombre numFibCalls habría sido
local para fib y testFib). Las funciones fib y testFib tienen acceso ilimitado al objeto al
que hace referencia la variable numFibCalls. La funció n testFib vincula numFibCalls a 0
cada vez que llama fib, y fib incrementa el valor de numFibCalls cada vez que se ingresa
fib.
Es con cierta inquietud que presentamos el tema de las variables globales. Desde la
dé cada de 1970, los científicos de la computació n que llevan la tarjeta han
vituperado contra ellos. El uso indiscriminado de variables globales puede generar
muchos problemas. La clave para hacer que los programas sean legibles es la
localidad. Uno lee un programa pieza por pieza, y cuanto menos contexto se necesite
para entender cada pieza, mejor. Dado que las variables globales se pueden
modificar o leer en una amplia variedad de lugares, el uso descuidado de ellas puede
destruir la localidad. Sin embargo, hay momentos en que son justo lo que se
necesita.
4.5 Módulos
Hasta ahora, hemos operado bajo la suposició n de que todo nuestro programa está
almacenado en un archivo. Esto es perfectamente razonable siempre que los
programas sean pequeñ os. Sin embargo, a medida que los programas se hacen má s
grandes, normalmente es má s conveniente almacenar diferentes partes de ellos en
archivos diferentes. Imagine, por ejemplo, que varias personas está n trabajando en el
mismo programa. Sería una pesadilla si todos intentaran actualizar el mismo archivo.
Los mó dulos de Python nos permiten construir fá cilmente un programa a partir del
có digo en varios archivos.
pi = 3.14159
def circunferencia(radio):
devuelve 2*pi*radio
def
superficieesfera(radio)
: return
4.0*area(radio)
imprimirá
3.14159
28.27431
18.84954
113.09724
A primera vista, el uso de la notació n de puntos puede parecer engorroso. Por otro
lado, cuando uno importa un mó dulo, a menudo no tiene idea de qué nombres
locales podrían haberse usado en la implementació n de ese mó dulo. El uso de la
notació n de puntos para calificar completamente los nombres evita la posibilidad de
quemarse por un choque accidental de nombres. Por ejemplo, la declaració n de
asignació n pi = 3.0 no cambia el valor de pi utilizado dentro del mó dulo de círculo.
Como hemos visto, un mó dulo puede contener sentencias ejecutables así como
definiciones de funciones. Por lo general, estas declaraciones se utilizan para
inicializar el mó dulo. Por esta razó n, las declaraciones en un mó dulo se ejecutan
solo la primera vez que se importa un mó dulo a un programa. En una nota
relacionada, un mó dulo se importa solo una vez por sesió n de inté rprete. Si
inicia IDLE, importa un mó dulo y luego cambia el contenido de ese mó dulo, el
inté rprete seguirá usando la versió n original del mó dulo. Esto puede conducir a
un comportamiento desconcertante durante la depuració n. Puede obligar al
inté rprete a recargar todos los mó dulos importados ejecutando reload().
Hay muchos mó dulos ú tiles que vienen como parte de la biblioteca está ndar de
Python. Por ejemplo, rara vez es necesario escribir sus propias
implementaciones de funciones matemá ticas o de cadenas comunes. Puede
encontrar una descripció n de esta biblioteca
enhttp://docs.python.org/2/library/.
4.6 archivos
Cada sistema informá tico utiliza archivos para guardar cosas de un cá lculo al
siguiente. Python proporciona muchas facilidades para crear y acceder a archivos.
Aquí ilustramos algunos de los bá sicos.
Cada sistema operativo (p. ej., Windows y MAC OS) viene con su propio sistema de
archivos para crear y acceder a archivos. Python logra la independencia del sistema
operativo al acceder a los archivos a travé s de algo llamado identificador de archivo.
El có digo
nameHandle = open('niños', 'w')
indica al sistema operativo que cree un archivo con el nombre kids y devuelva un
identificador de archivo para ese archivo. El argumento 'w' para abrir indica que el
archivo debe abrirse para escritura. El siguiente có digo abre un archivo, usa el
mé todo de escritura para escribir dos líneas y luego cierra el archivo. Es importante
recordar cerrar el archivo cuando el programa termine de usarlo. De lo contrario,
existe el riesgo de que algunas o todas las escrituras no se guarden.
nameHandle = open('kids', 'w')
for i in range(2):
nombre = raw_input('Ingrese el
nombre:')
nameHandle.write(nombre + '\n')
nombreManejador.close()
En una cadena, el cará cter "\" es un cará cter de escape que se utiliza para
indicar que el siguiente cará cter debe tratarse de manera especial. En este
ejemplo, la cadena '\n' indica un cará cter de nueva línea.
Capter4.Divertidoacciones,Salbardilla,undABtr 5
Andrea
La línea extra entre David y Andrea está ahí porque print comienza una nueva línea
cada vez que encuentra el '\n' al final de cada línea en el archivo. Podríamos haber
evitado imprimir eso escribiendo print line[:-1]. Ahora considera
identificador de nombre =
open('niños', 'w')
identificador de
nombre.write('Michael\n')
identificador de
nombre.write('Mark\n')
identificador de
nombre.close()
nameHandle = open('kids', 'r')
for line in nameHandle:
línea de
impresión[:-1]
nameHandle.close()
se imprimirá
miguel
marca
identificador de nombre =
open('niños', 'a')
identificador de
nombre.write('David\n')
identificador de
nombre.write('Andrea\n')
identificador de
nombre.close()
nameHandle = open('kids', 'r')
for line in nameHandle:
línea de
impresión[:-1]
nameHandle.close()
imprimirá
Michael
Marcos
David
Andrea
5 Capter4.Divertidoacciones,Salbardilla,undABtr
5.1 tuplas
Al igual que las cadenas, las tuplas son secuencias ordenadas de elementos. La
diferencia es que los elementos de una tupla no necesitan ser caracteres. Los
elementos individuales pueden ser de cualquier tipo y no es necesario que sean del
mismo tipo entre sí.
Los literales de tipo tupla se escriben encerrando entre paré ntesis una lista de
elementos separados por comas. Por ejemplo, podemos escribir
t1 = ()
t2 = (1, 'dos', 3)
imprimir
t1
imprimir
t2
Mirando este ejemplo, es posible que se le haga creer que la tupla que contiene el
valor ú nico 1 se escribiría (1). Pero, para citar a Richard Nixon, “eso estaría mal”.
Dado que los paré ntesis se utilizan para agrupar expresiones, (1) es simplemente
una forma detallada de escribir el nú mero entero 1. Para indicar la tupla singleton
que contiene este valor, escribimos (1,). Casi todos los que usan Python en un
momento u otro han omitido accidentalmente esa molesta coma.
Al igual que las cadenas, las tuplas se pueden concatenar, indexar y dividir. Considerar
t1 = (1, 'dos', 3)
t2 = (t1, 3,25)
imprimir t2
imprimir (t1 + t2)
imprimir (t1 + t2)
[3] imprimir (t1 +
t2)[2:5]
posible porque una tupla, como todo lo demá s en Python, es un objeto, por lo que
las tuplas pueden contener tuplas. Por lo tanto, la primera declaració n de impresió n
produce la salida,
((1, 'dos', 3), 3.25)
Se puede usar una instrucció n for para iterar sobre los elementos de una tupla.
Por ejemplo, el siguiente có digo imprime los divisores comunes de 20 y 100 y
luego la suma de todos los divisores.
def buscarDivisores (n1, n2):
"""Asume que n1 y n2 son enteros positivos
Devuelve una tupla que contiene todos los divisores comunes de
n1 y n2""" divisores = () #la tupla vacía
para i en el rango (1, min (n1, n2)
+ 1): si n1%i == 0 y n2%i == 0:
divisores = divisores +
(i,) devuelve divisores
divisores = findDivisors(20,
100) imprimir divisores
totales = 0
para d en
divisores:
total += d
imprimir total
se unirá minDivisora2yMaxDivisora100.
produce la salida,
lo hice todo
4
amar
En ocasiones, el hecho de que los corchetes se utilicen para literales de tipo lista,
indexació n en listas y segmentació n de listas puede generar cierta confusió n
visual. Por ejemplo, la expresió n [1,2,3,4][1:3][1], que da como resultado 3, usa
los corchetes de tres maneras diferentes. Esto rara vez es un problema en la
prá ctica, porque la mayoría de las veces las listas se crean de forma incremental
en lugar de escribirse como literales.
Las listas se diferencian de las tuplas en un aspecto muy importante: las listas son
mutables. Por el contrario, las tuplas y las cadenas son inmutables. Hay muchos
operadores que se pueden usar para crear objetos de estos tipos inmutables, y las
variables se pueden vincular a objetos de estos tipos. Pero los objetos de tipos
inmutables no se pueden modificar.
Por otro lado, los objetos de tipo lista se pueden modificar después de su creació n.
se ejecutan, el inté rprete crea dos nuevas listas y les vincula las variables
apropiadas, como se muestra a continuació n.
tambié n cree nuevas listas y vincule variables a ellas. Los elementos de estas listas son en sí
mismos listas. Las tres declaraciones impresas
print 'Univ =', Univ
print 'Univ1 =', Univ1
print Univ == Univ1
producir la salida
Universidad = [['MIT', 'Caltech'], ['Harvard', 'Yale', 'Brown']]
Univs1 = [['MIT', 'Caltech'], ['Harvard', 'Yale', 'Brown']]
Verdadero
Parece como si Univs y Univs1 estuvieran vinculados al mismo valor. Pero las
apariencias pueden engañ ar. Como ilustra la siguiente imagen, Univs y Univs1 está n
vinculados a valores bastante diferentes.
6 Capítulo 5. Tipos estructurados, mutabilidad y funciones de orden
Figura 5.2 Dos listas que parecen tener el mismo valor, pero no lo tienen
se imprime
Verda
dero
Falso
Id de Univs = 24499264
Id de Univs1 = 24500504
(No espere ver los mismos identificadores ú nicos si ejecuta este có digo. La
semá ntica de Python no dice nada sobre qué identificador está asociado con cada
objeto; simplemente requiere que no haya dos objetos que tengan el mismo
identificador).
Observe que en la figura 5.2 los elementos de Univs no son copias de las listas a las
que está n vinculados Techs e Ivys, sino que son las listas mismas. Los elementos de
Univs1 son listas que contienen los mismos elementos que las listas de Univs, pero
no son las mismas listas. Podemos ver esto ejecutando el có digo
que imprime
Id. de Univs[0] y Univs[1] 22287944 22286464 Id.
de Univs1[0] y Univs1[1] 22184184 22287984
¿Por qué importa esto? Importa porque las listas son mutables.
Considere el có digo
Techs.append('RPI')
Capítulo 5. Tipos estructurados, mutabilidad y funciones de orden 6
El mé todo append tiene un efecto secundario. En lugar de crear una nueva lista,
muta la lista Techs existente agregando un nuevo elemento, la cadena 'RPI', al final
de la misma.
Lo que tenemos aquí es algo llamado aliasing. Hay dos caminos distintos al
mismo objeto de lista. Una ruta es a travé s de la variable Techs y la otra a travé s
del primer elemento del objeto de lista al que está vinculado Univs. Uno puede
mutar el objeto a travé s de cualquier camino, y el efecto de la mutació n será
visible a travé s de ambos caminos. Esto puede ser conveniente, pero tambié n
puede ser traicionero.
La creació n de alias no intencional conduce a errores de programació n que a
menudo son enormemente difíciles de rastrear.
Al igual que con las tuplas, se puede usar una instrucció n for para iterar sobre los
elementos de una lista. Por ejemplo,
para e en Univs:
imprime 'Univs contiene',
e imprime 'que contiene'
para u en e:
imprimir'', tu
6 Capítulo 5. Tipos estructurados, mutabilidad y funciones de orden
imprimirá
Univs contiene ['MIT', 'Caltech', 'RPI']
que contiene
MIT
RPI de
Caltech
Univs contiene ['Harvard', 'Yale', 'Brown']
que contiene
Harvard
Yale
Marrón
imprimirá
L3 = [1, 2, 3, 4, 5, 6]
L1 = [1, 2, 3, 4, 5, 6]
L1 = [1, 2, 3, 4, 5, 6, [4, 5, 6]]
Tenga en cuenta que el operador + no tiene un efecto secundario. Crea una nueva
lista y la devuelve. Por el contrario, extienda y agregue cada L1 mutado.
L.añadir(e)agregaFigura
el objeto
5.4 al final deLasociados
miMétodos . a listas
L.cuenta(e)devuelve el nú mero de veces que aparece e enL.
L.insertar(i, e)inserta el objetomienLen el
índicei.L.extender(L1)agrega los elementos en la listaL1al
final deL.L.quitar(e)elimina la primera aparició n demideL.
índice L(e)devuelve el índice de la primera aparició n de e en L. Genera una
excepció n (consulte el Capítulo 7) si e no está en L.
L pop (i)elimina y devuelve el elemento en el índice i en L. Si se omite i, el valor
predeterminado es -1, para eliminar y devolver el ú ltimo elemento de L.
Ordenar L()ordena los elementos deL en orden ascendente.
L.reversa()invierte el orden de los elementos en L.
Capítulo 5. Tipos estructurados, mutabilidad y funciones de orden 6
5.2.1 Clonación
Aunque está permitido, por lo general es prudente evitar mutar una lista sobre la
que se está iterando. Consideremos, por ejemplo, el có digo
def removeDups(L1, L2):
"""Supone que L1 y L2 son listas.
Elimina cualquier elemento de L1 que también ocurra en
L2""" para e1 en L1:
si e1 en L2:
L1.eliminar(e1
) L1 = [1,2,3,4]
L2 = [1,2,5,6]
removeDups(L1, L2)
imprime 'L1 =', L1
Una forma de evitar este tipo de problema es usar el corte para clonar (es decir,
hacer una copia) de la lista y escribir para e1 en L1[:]. Observe que escribir newL1 =
L1 seguido de for e1 en newL1 no habría resuelto el problema. No habría creado una
copia de L1, sino que simplemente habría introducido un nuevo nombre para la lista
existente.
imprimirá la lista
[1, 4, 9, 16, 25, 36]
Figuraa5.5
def aplicar Aplicar
cada unaf):
uno (L, función a los elementos de una lista
"""Asume que L es una lista, función fa
Muta L reemplazando cada elemento, e, de L por f(e)"""
para i en range(len(L)):
L[i] = f(L[i])
Python tiene una funció n integrada de orden superior, map, que es similar, pero
má s general, que la funció n applyToEach definida en la Figura 5.5. En su forma má s
simple, el primer argumento para mapear es una funció n unaria (es decir, una
funció n que tiene solo un pará metro) y el segundo argumento es cualquier
colecció n ordenada de valores adecuados como argumentos para el primer
argumento. Devuelve una lista generada aplicando el primer argumento a cada
elemento del segundo argumento. Por ejemplo, la expresió n map(fact, [1, 2, 3])
tiene el valor [1, 2, 6].
De manera má s general, el primer argumento para mapear puede ser una funció n
de n argumentos, en cuyo caso debe ser seguido por n colecciones ordenadas
posteriores. Por ejemplo, el có digo
L1 = [1, 28, 36]
L2 = [2, 57, 9]
imprimir mapa(min, L1, L2)
imprime ellista
[1, 28, 9]
6 Capítulo 5. Tipos estructurados, mutabilidad y funciones de orden
Figura
secuencia[i] 5.6 Operaciones
devuelve comunes
la ielelemento en tipos de secuencia
en la secuencia.
len(siguiente)devuelve la longitud de la secuencia.
Algunas de sus otras similitudes y diferencias se resumen en la Figura 5.7.
sec1 + sec2devuelve la concatenació n de las dos secuencias.n
devuelveTipo
* seq.Tipo una de
secuencia que seEjemplos
elementos repite seqde
n literales Mudable
veces.seq[inicio:fin]devuelve un segmento de la
'','a','a B C'
secuencia.
calle caracteres No
e en secuenciaesVerdaderosimiestá contenido en la secuencia yFALSOde
tupla cualquier tipo (), (3,), ('abc', 4) No
lo contrario.e no en secuenciaesVerdaderosimino está en la secuencia
yFALSOdelista cualquier
lo contrario. paratipo
e en seq[],
itera[3],
sobre['abc', 4]
los elementos Síla
de
Los programadores de Python tienden a usar listas con mucha má s frecuencia que
tuplas. Dado que las listas son mutables, se pueden construir de forma incremental
durante un cá lculo.
Por ejemplo, el có digo siguiente genera de forma incremental una lista que contiene
todos los nú meros pares de otra lista.
evenElems = []
para e en L:
si e%2 == 0:
evenElems.append(e)
Una ventaja de las tuplas es que debido a que son inmutables, el alias nunca es
una preocupació n. Otra ventaja de que sean inmutables es que las tuplas, a
diferencia de las listas, pueden usarse como claves en los diccionarios, como
veremos en la siguiente secció n.
Dado que las cadenas solo pueden contener caracteres, son considerablemente
menos versá tiles que las tuplas o las listas. Por otro lado, cuando trabaja con una
cadena de caracteres, existen muchos mé todos integrados que facilitan la vida. La
figura 5.8 contiene breves descripciones de algunos de ellos. Tenga en cuenta que,
dado que las cadenas son inmutables, todas ellas devuelven valores y no tienen
efectos secundarios.
Capítulo 5. Tipos estructurados, mutabilidad y funciones de orden 6
5.5 Diccionarios
Los objetos de tipo dict (abreviatura de diccionario) son como listas, excepto que los
"índices" no necesitan ser nú meros enteros; pueden ser valores de cualquier tipo
inmutable. Como no está n ordenados, los llamamos claves en lugar de índices.
Piense en un diccionario como un conjunto de pares clave/valor. Los literales de
tipo dict se encierran entre llaves y cada elemento se escribe como una clave
seguida de dos puntos seguidos de un valor.
imprimirá
el tercer mes es mar
abril y enero tienen 3 meses de diferencia
El mé todo keys devuelve una lista que contiene las claves de un diccionario. El
orden en que aparecen las teclas no está definido. Así, por ejemplo, el có digo
imprimir numerosmes.keys()podría imprimir
Cuando se usa una instrucció n for para iterar sobre un diccionario, el valor asignado
a la variable de iteració n es una clave, no un par clave/valor. Por ejemplo, el có digo
claves = []
for e in monthNumbers:
keys.append(e)
keys.sort()
imprimir
claves
Los diccionarios son una de las mejores cosas de Python. Reducen en gran medida la
dificultad de escribir una variedad de programas. Por ejemplo, en la Figura 5.9
usamos diccionarios para escribir un programa (bastante horrible) para traducir
entre idiomas.
def traducirPalabra(palabra,
diccionario): if palabra en
diccionario.teclas():
return
diccionario[palabra] elif
palabra != '':
devolver '"' + palabra
+ '"' devolver palabra
Al igual que las listas, los diccionarios son mutables. Por lo tanto, hay que tener
cuidado con los efectos secundarios. Por ejemplo,
Capítulo 5. Tipos estructurados, mutabilidad y funciones de orden 6
FtoE['bois'] = 'madera'
print translate('Je bois du vin rouge.', dicts, 'Francés a Inglés')
imprimirá
Madera de vino tinto.
Al igual que con las listas, existen muchos mé todos ú tiles, incluidos algunos para
eliminar elementos, asociados con los diccionarios. No los enumeramos aquí,
pero los usaremos segú n convenga en ejemplos má s adelante en el libro. La
figura 5.10 contiene algunas de las operaciones má s ú tiles en los diccionarios.
Figura 5.10
prestar)devuelve el núAlgunas
mero deoperaciones
elementos encomunes
d. en dictados
d.teclas()devuelve una lista que contiene las claves
Los objetos de cualquier tipo inmutable, por ejemplo, tipo tupla, pueden usarse como
end.de
claves d.valores()devuelve una lista que contiene los
diccionario. Imagine, por ejemplo, usar una tupla de la forma
valores end.amable
(flightNumber, devoluciones
day) para Verdadero
representar si clave
vuelos de líneaskes
aéreas. Entonces sería fá cil
usarentales
d. tuplas como claves en un diccionario que implemente un mapeo desde los
devuelve
d[k]hasta
vuelos el artículo
los tiempos deen dcon llavek.
llegada.
d.obtener(k, v)devolucionesd[k]sikes end, yvde lo contrario.
La mayoría de los lenguajes de programació n no contienen un tipo incorporado
d[k] = vasocia el valor v con la clave k en d. Si ya hay un valor asociado con k,
que proporcione una asignació n de claves a valores. En su lugar, los
ese valor se reemplaza.
programadores utilizan otros tipos para proporcionar una funcionalidad similar.
del d[k]quita la llavekded.
Es, por ejemplo, relativamente fá cil implementar un diccionario utilizando una
lista en la que cada elemento es un par clave/valor. Entonces se puede escribir
una funció n simple que hace la recuperació n asociativa, por ejemplo,
def keySearch(L, k):
for elem in L:
si elemento[0] ==
k: devuelve
elemento[1]
volver Ninguno
Odiamos mencionar esto, pero el Dr. Pangloss estaba equivocado. No vivimos en “el
mejor de los mundos posibles”. Hay algunos lugares donde llueve muy poco y otros
donde llueve demasiado. Algunos lugares son demasiado fríos, otros demasiado
calurosos y otros demasiado calurosos en verano y demasiado fríos en invierno. A
veces, el mercado de valores baja mucho. Y, quizá s lo peor de todo, nuestros
programas no siempre funcionan correctamente la primera vez que los ejecutamos.
Se han escrito libros sobre có mo lidiar con este ú ltimo problema, y hay mucho que
aprender leyendo estos libros. Sin embargo, con el interé s de brindarle algunos
consejos que podrían ayudarlo a resolver el pró ximo problema a tiempo, este
capítulo proporciona una discusió n muy condensada del tema. Si bien todos los
ejemplos de programació n está n en Python, los principios generales son aplicables
para lograr que cualquier sistema complejo funcione.
El primer paso para lograr que un programa funcione es hacer que el sistema de
lenguaje acepte ejecutarlo, es decir, eliminar los errores de sintaxis y los errores
semá nticos está ticos que pueden detectarse sin ejecutar el programa. Si no ha
superado ese punto en su programació n, no está listo para este capítulo. Dedique un
poco má s de tiempo a trabajar en pequeñ os programas y luego regrese.
6.1 Pruebas
Lo má s importante que decir acerca de las pruebas es que su propó sito es mostrar
que existen errores, no mostrar que un programa está libre de errores. Para citar a
Edsger Dijkstra: “Las pruebas de programas se pueden usar para mostrar la
presencia de errores, ¡pero nunca para mostrar su ausencia!” un solo experimento
puede probar que estoy equivocado.
¿Por qué esto es tan? Incluso el má s simple de los programas tiene miles de
millones de entradas posibles. Considere, por ejemplo, un programa que pretende
cumplir con la especificació n:
def es más grande (x, y):
"""Asume que x e y son números enteros
Devuelve True si x es menor que y y False en caso contrario."""
Ejecutarlo en todos los pares de enteros sería, por decir lo menos, tedioso. Lo
mejor que podemos hacer es ejecutarlo en pares de enteros que tengan una
probabilidad razonable de producir una respuesta incorrecta si hay un error en el
programa.
Xpositivo,ypositivo
Xnegativo,ynegativo
Xpositivo,ynegativo
Xnegativo,ypositivo
x = 0, y = 0
x = 0,y≠0
X≠0,y = 0
Otra característica positiva de las pruebas de caja negra es que es só lida con
respecto a los cambios de implementació n. Dado que los datos de prueba se
generan sin conocimiento de la implementació n, no es necesario cambiarlos
cuando se cambia la implementació n.
Como dijimos anteriormente, una buena manera de generar datos de prueba de caja
negra es explorar caminos a travé s de una especificació n. Considere, la especificació n
def sqrt(x, epsilon): """Asume
x, epsilon flota
x >= 0
epsilon > 0
Devuelve un resultado
tal que
x-epsilon <= resultado*resultado <= x+epsilon"""
Parece que solo hay dos caminos distintos a través de esta especificació n: uno
correspondiente a x = 0 y otro correspondiente a x > 0. Sin embargo, el sentido
comú n nos dice que si bien es necesario probar estos dos casos, no es
suficiente.
Mirando el có digo, podemos ver que debido a la prueba si x <= 2, los valores 0, 1 y
2 se tratan como casos especiales y, por lo tanto, deben probarse. Sin mirar el
có digo, uno podría no probar isPrime(2) y, por lo tanto, no descubriría que la
llamada a la funció n isPrime(2) devuelve False, lo que indica erró neamente que 2
no es un nú mero primo.
Un conjunto de pruebas de caja de vidrio tiene una ruta completa si ejercita todas
las rutas potenciales a travé s del programa. Por lo general, esto es imposible de
lograr, ya que depende de la cantidad de veces que se ejecuta cada ciclo y la
profundidad de cada recursió n. Por ejemplo, una implementació n recursiva de
factorial sigue un camino diferente para cada entrada posible (porque el nú mero de
niveles de recursividad diferirá ).
Capítulo 6. Pruebas y 7
La especificació n sugiere que hay dos casos posibles, x es negativo o no lo es. Esto
sugiere que el conjunto de entradas {2, -2} es suficiente para explorar todas las rutas
de la especificació n. Este conjunto de pruebas tiene la agradable propiedad adicional
de forzar el programa a través de todas sus rutas, por lo que también parece un
conjunto completo de cajas de cristal. El ú nico problema es que este conjunto de
pruebas no expondrá el hecho de que abs(-1) devolverá -1.
A pesar de las limitaciones de las pruebas en caja de vidrio, existen algunas reglas
generales que generalmente vale la pena seguir:
estas dos fases, ya que las fallas durante las pruebas de integració n conducen a
realizar cambios en las unidades individuales.
Las pruebas de integració n son casi siempre má s desafiantes que las pruebas
unitarias. Una de las razones de esto es que el comportamiento previsto de un
programa completo suele ser considerablemente má s difícil de caracterizar que el
comportamiento previsto de cada una de sus partes. Por ejemplo, caracterizar el
comportamiento previsto de un procesador de textos es considerablemente má s
desafiante que caracterizar el comportamiento de una funció n que cuenta la
cantidad de caracteres en un documento. Los problemas de escala tambié n pueden
dificultar las pruebas de integració n. No es raro que las pruebas de integració n
tarden horas o incluso días en ejecutarse.
30O,para el caso, aquellos que califican conjuntos de problemas en cursos de programació n muy
extensos.
Capítulo 6. Pruebas y 7
6.2 depuración
Hay una encantadora leyenda urbana sobre có mo el proceso de corregir fallas
en el software llegó a conocerse como depuració n. La foto de abajo es de una
pá gina del 9 de septiembre de 1947 en un libro de laboratorio del grupo que
trabaja en la calculadora de relé s Mark II Aiken en la Universidad de Harvard.
El siguiente paso hacia el foso de la indeseabilidad son los errores que son
manifiestos pero intermitentes. Un sistema de control de trá fico aé reo que calcule la
ubicació n correcta de los aviones casi todo el tiempo sería mucho má s peligroso que
uno que cometa errores obvios todo el tiempo. Uno puede vivir en el paraíso de los
tontos por un período de tiempo, y tal vez llegar tan lejos como implementar un
sistema que incorpore el programa defectuoso, pero tarde o temprano el error se
manifestará . Si las condiciones que provocan que el error se manifieste son
fá cilmente reproducibles, a menudo es relativamente fá cil localizar y reparar el
problema. Si las condiciones que provocan el error no está n claras, la vida es mucho
má s difícil.
Los programas que fallan de forma encubierta suelen ser muy peligrosos. Dado
que aparentemente no son problemá ticos, las personas los usan y confían en que
hará n lo correcto. Cada vez má s, la sociedad confía en el software para realizar
cá lculos críticos que está n má s allá de la capacidad de los humanos para
realizarlos o incluso verificar su correcció n.
Comience por estudiar los datos disponibles. Esto incluye los resultados de la
prueba y el texto del programa. Estudia todos los resultados de las pruebas.
Examine no solo las pruebas que revelaron la presencia de un problema, sino
tambié n aquellas pruebas que parecían funcionar perfectamente. Tratar de
entender por qué una prueba funcionó y otra no, a menudo es esclarecedor. Al
mirar el texto del programa, tenga en cuenta que no lo entiende completamente.
Si lo hiciera, probablemente no habría un error.
Luego, formula una hipó tesis que creas que es consistente con todos los datos. La
hipó tesis podría ser tan limitada como "si cambio la línea 403 de x < y a x <= y, el
problema desaparecerá " o tan amplia como "mi programa no termina porque tengo
la condició n de salida incorrecta en algú n ciclo while". .”
33El1 de agosto de 2012, Knight Capital Group, Inc. implementó una nueva pieza de
software de negociació n de acciones. En cuarenta y cinco minutos, un error en ese
software hizo perder a la empresa
$440,000,000. Al día siguiente, el CEO de Knight comentó que el error provocó que el
8 software ingresara “una tonelada de pedidos, todos erró neos”.
Capítulo 6. Pruebas y
Capítulo 6. Pruebas y 8
realizado.
Cuando ha pasado muchas horas cambiando su có digo tratando de rastrear un error
esquivo, es fá cil olvidar lo que ya ha intentado. Si no tiene cuidado, es fá cil perder
demasiadas horas intentando el mismo experimento (o má s probablemente un
experimento que parece diferente pero le dará la misma informació n) una y otra
vez. Recuerde, como muchos han dicho, “locura es hacer lo mismo, una y otra vez,
pero esperando resultados diferentes.”34
Veamos un ejemplo artificial para ver có mo se podría depurar. Imagine que escribió
el có digo de verificació n del palíndromo en la figura 6.1 y que está tan seguro de sus
habilidades de programació n que lo publica en la Web, sin probarlo. Suponga
ademá s que recibe un correo electró nico que dice: “¡¡Probé su !!**! programa en la
siguiente entrada de 1000 cadenas, e imprimió Sí. Sin embargo, cualquier tonto
puede ver que no es un palíndromo. ¡Arreglalo!"
def tonto(n):
"""Supone que n es un
int > 0 Obtiene n
entradas del usuario
Imprime 'Sí' si la secuencia de entradas forma un
palíndromo; 'No' de lo contrario"""
para i en el
rango (n):
resultado =
[]
elem = raw_input('Ingrese el
8 Capítulo 6. Pruebas y
La buena noticia es que falla incluso en esta simple prueba, por lo que no tiene
que escribir mil cadenas. La mala noticia es que no tienes idea de por qué falló .
En este caso, el có digo es lo suficientemente pequeñ o como para que puedas mirarlo
y encontrar el error (o errores). Sin embargo, supongamos que es demasiado
grande para hacer esto y comencemos a reducir sistemá ticamente el espacio de
bú squeda.
Intenté moslo y veamos si el resultado tiene el valor correcto despué s del ciclo for.
Lo hace, pero desafortunadamente el programa todavía imprime Sí. Ahora, tenemos
razones para creer que hay un segundo error debajo de la declaració n de
Capítulo 6. Pruebas y 8
impresió n. Entonces, echemos un vistazo a isPal. La línea if temp == x: está
aproximadamente a la mitad de esa funció n. Entonces, insertamos el
8 Capítulo 6. Pruebas y
Ejecutamos la prueba nuevamente, y ahora parece que tanto temp como x tienen el
valor ['b', 'a']. Ahora hemos reducido el error a una línea. Parece que temp.reverse()
cambió inesperadamente el valor de x. Nos ha picado un error de alias: temp y x son
nombres para la misma lista, tanto antes como despué s de que la lista se invierta.
Una forma de solucionarlo es reemplazar la primera declaració n de asignació n en
isPal por temp = x[:], lo que hace que se haga una copia de x. La versió n corregida de
isPal es
def esAmigo(x):
"""Asume que x es una lista
Devuelve True si la lista es un palíndromo; Falso de lo
contrario""" temp = x[:]
temp.reverse()
si temp == x:
devolver
Verdadero más:
falso retorno
35Uno bien podría preguntarse por qué no hay un verificador está tico que detecte el
hecho de que la línea de có digo temp.reverse no hace que se realice ningú n cá lculo ú til y,
por lo tanto, es probable que sea un error.
36Tambié nse dice que le dijo a JFK: “No compre un solo voto má s de lo necesario. Que me
condenen si voy a pagar por un derrumbe”.
Capítulo 6. Pruebas y 8
Pregú ntese si este error explica todos los síntomas observados o si es solo la punta
del iceberg. Si es lo ú ltimo, puede ser mejor pensar en solucionar este error junto con
otros cambios. Supongamos, por ejemplo, que ha descubierto que el error es el
resultado de haber mutado accidentalmente una lista. Podría eludir el problema
localmente (quizá s haciendo una copia de la lista), o podría considerar usar una
tupla en lugar de una lista (ya que las tuplas son inmutables), quizá s eliminando
errores similares en otras partes del có digo.
Siempre asegú rese de que puede volver a donde está . No hay nada má s frustrante
que darse cuenta de que una larga serie de cambios te han dejado má s lejos de la
meta que cuando empezaste y no tener forma de volver a donde empezaste. El
espacio en disco suele ser abundante. Ú selo para almacenar versiones antiguas de
su programa.
7EXCEPCIONES Y AFIRMACIONES
Una "excepció n" generalmente se define como "algo que no se ajusta a la norma"
y, por lo tanto, es algo raro. No hay nada raro en las excepciones en Python.
Está n en todos lados. Prá cticamente todos los mó dulos de la biblioteca está ndar
de Python los usan, y Python mismo los generará en muchas circunstancias
diferentes. Ya has visto algunos de ellos.
Si sabe que una línea de có digo puede generar una excepció n cuando se ejecuta,
debe manejar la excepció n. En un programa bien escrito, las excepciones no
controladas deberían ser la excepció n.
Considere el có digo
SuccessFailureRatio = numÉxitos/float(numFailures) print
'La proporción de éxito/fracaso es', SuccessFailureRatio
print 'Ahora aquí'
La mayoría de las veces, este có digo funcionará bien, pero fallará si numFailures
resulta ser cero. El intento de dividir por cero hará que el sistema de tiempo de
ejecució n de Python genere una excepció n ZeroDivisionError, y nunca se alcanzará n
las declaraciones de impresió n.
Mejor aú n, esta funció n se puede generalizar para solicitar cualquier tipo de entrada,
def readVal(valType, requestMsg, errorMsg):
while True:
val = raw_input(requestMsg + ' ')
intente:
val = valType(val)
devuelve val
excepto ValueError:
print val, errorMsg
se ingresará el bloque de excepció n si se genera algú n tipo de excepció n dentro del intento
bloquear. Estas características se muestran en la Figura 7.1.
En muchos lenguajes de programació n, el enfoque está ndar para lidiar con los
errores es hacer que las funciones devuelvan un valor (a menudo algo aná logo al
Ninguno de Python) que indica que algo salió mal. Cada invocació n de funció n
tiene que comprobar si se ha devuelto ese valor. En Python, es má s comú n que
una funció n genere una excepció n cuando no puede producir un resultado que
sea consistente con la especificació n de la funció n.
El nombre de excepció n suele ser una de las excepciones integradas, por ejemplo,
ValueError. Sin embargo, los programadores pueden definir nuevas excepciones
creando una subclase (consulte el Capítulo 8) de la clase integrada Exception.
Diferentes tipos de excepciones pueden tener diferentes tipos de argumentos, pero
la mayoría de las veces el argumento es una sola cadena, que se usa para describir la
razó n por la que se genera la excepció n.
8 Capítulo 7. Excepciones y
intentar:
imprimir getRatios([1.0,2.0,7.0,6.0], [1.0,2.0,0.0,3.0])
imprimir obtenerRatios([], [])
imprime getRatios([1.0, 2.0], [3.0])
excepto ValueError, mensaje:
imprimir mensaje
huellas dactilares
[1.0, 1.0, nan, 2.0]
[]
getRatios llamado con malos argumentos
def
getGrades(fname
): prueba:
gradesFile = open(fname, 'r') #abrir archivo para
lectura excepto IOError:
aumentar ValueError('getGrades no pudo abrir' + fname)
calificaciones = []
para la línea en
gradesFile:
intente:
grades.append(float(line))
excepto:
aumentar ValueError('No se puede convertir la línea
en flotante') devolver calificaciones
intentar:
calificaciones =
getGrades('cuestionario1calificaciones.
txt') calificaciones.sort()
mediana =
grados[len(grados)//2] print
Figura 7.3 Obtener calificaciones
La funció n getGrades devuelve un valor o genera una excepció n con la que tiene
asociado un valor. Genera una excepció n ValueError si la llamada para abrir genera
un IOError. Podría haber ignorado el IOError y dejar que la parte del programa que
llama a getGrades se encargue de ello, pero eso habría proporcionado menos
informació n al có digo de llamada sobre lo que salió mal. El có digo que usa
getGrades usa el valor devuelto para calcular otro valor o maneja la excepció n e
imprime un mensaje de error ú til.
7.3 afirmaciones
La declaració n de afirmació n de Python proporciona a los programadores una forma
sencilla de confirmar que el estado del cálculo es el esperado. Una declaració n de
afirmació n puede tomar una de dos formas:
afirmarexpresión booleana
o
afirmarexpresión booleana,argumento
Las aserciones son una herramienta ú til de programació n defensiva. Se pueden usar
para confirmar que los argumentos de una funció n son de tipos apropiados. Tambié n
son una ú til herramienta de depuració n. Se puede usar, por ejemplo, para confirmar
que los valores intermedios tienen los valores esperados o que una funció n devuelve
un valor aceptable.
8CLASES Y PROGRAMACIÓN ORIENTADA A OBJETOS
Ahora dirigimos nuestra atenció n a nuestro ú ltimo tema principal relacionado con
la escritura de programas en Python: el uso de clases para organizar programas en
torno a mó dulos y abstracciones de datos.
Las ideas que subyacen a la programació n orientada a objetos tienen unos cuarenta
añ os y han sido ampliamente aceptadas y practicadas durante los ú ltimos veinte
añ os má s o menos. A mediados de la década de 1970, la gente comenzó a escribir
artículos que explicaban los beneficios de este enfoque de la programació n. Casi al
mismo tiempo, los lenguajes de programació n SmallTalk (en Xerox PARC) y CLU (en
MIT) brindaron apoyo lingü ístico a las ideas. Pero no fue hasta la llegada de C++ y
Java que realmente despegó en la prá ctica.
Las especificaciones de esas operaciones definen una interfaz entre el tipo de datos
abstracto y el resto del programa. La interfaz define el comportamiento de las
operaciones: lo que hacen, pero no có mo lo hacen. Por tanto, la interfaz
proporciona una barrera de abstracció n que aísla el resto del programa de las
estructuras de datos, algoritmos y có digo involucrados en proporcionar una
realizació n de la abstracció n de tipo.
Hemos estado usando tipos de datos abstractos (sin llamarlos así) a lo largo de
este libro. Hemos escrito programas utilizando nú meros enteros, listas, nú meros
de coma flotante, cadenas y diccionarios sin pensar en có mo se podrían
implementar estos tipos. Parafraseando a Bourgeois Gentilhomme de Moliè re,
“Par ma foi, il ya plus de quatre-vingt pages que nous avons utilisé ADTs, sans
que nous le sachions”.38
Una definició n de clase crea un objeto de tipo tipo y asocia con ese objeto un
conjunto de objetos de tipo mé todo de instancia. Por ejemplo, la expresió n
IntSet.insert hace referencia al mé todo insert definido en la definició n de la clase
IntSet. y el codigo
tipo de impresión (IntSet), tipo (IntSet.insertar)
imprimirá
38“Dios mío, durante má s de ochenta pá ginas hemos estado usando ADT sin saberlo”.
Capítulo 8. Clases y programación orientada a 9
clase IntSet(objeto):
"""Un intSet es un conjunto de enteros"""
#Información sobre la implementación (no la abstracción)
#El valor del conjunto está representado por una lista de enteros,
self.vals. #Cada int en el conjunto ocurre en self.vals exactamente
una vez.
def getMembers(self):
"""Devuelve una lista que contiene los elementos de self.
No se puede suponer nada sobre el orden de los elementos"""
return self.vals[:]
Cada definició n de clase comienza con la palabra reservada class seguida del nombre de
la clase y alguna informació n sobre có mo se relaciona con otras clases. En este caso, la
primera línea indica que IntSet es una subclase de objeto. Por ahora, ignore lo que
significa ser una subclase. Llegaremos a eso en breve.
se ejecuta, el intérprete creará una nueva instancia de tipo IntSet y luego llamará a
IntSet. init con el objeto recién creado como el pará metro real que está vinculado al
pará metro formal self. Cuando se invoca, IntSet. init crea vals, un objeto de tipo list, que
pasa a formar parte de la instancia recié n creada de tipo IntSet. (La lista se crea usando
la ya conocida notació n [], que es simplemente una abreviatura de list()). Esta lista se
denomina atributo de datos de la instancia de IntSet. Tenga en cuenta que cada objeto
de tipo IntSet tendrá una lista de valores diferente, como cabría esperar.
Como hemos visto, los métodos asociados con una instancia de una clase se
pueden invocar usando la notació n de puntos. Por ejemplo, el có digo,
s = IntSet()
s.insertar(3
)
imprimir en miembro(3)
crea una nueva instancia de IntSet, inserta el nú mero entero 3 en ese IntSet y
luego imprime True.
A primera vista, parece haber algo inconsistente aquí. Parece como si cada
método se llamara con un argumento demasiado pequeñ o. Por ejemplo, el
miembro tiene dos pará metros formales, pero parece que lo estamos llamando
con un solo pará metro real. Este es un artefacto de la notació n de puntos. El
objeto asociado con la expresió n que precede al punto se pasa implícitamente
como el primer pará metro del mé todo. A lo largo de este libro, seguimos la
convenció n de usar self como el nombre del pará metro formal al que está
ligado este pará metro real.
Los programadores de Python observan esta convenció n casi universalmente,
y le sugerimos encarecidamente que también la use.
Una clase no debe confundirse con instancias de esa clase, al igual que un objeto de
tipo lista no debe confundirse con el tipo de lista. Los atributos se pueden asociar
con una clase en sí o con instancias de una clase:
Cuando los atributos de datos está n asociados con una clase, los llamamos
variables de clase. Cuando está n asociadas con una instancia, las llamamos
variables de instancia. Por ejemplo,valses una variable de instancia porque
para cada instancia de claseIntSet,valsestá vinculado a una lista diferente.
Hasta ahora, no hemos visto una variable de clase. Usaremos uno en la
Figura 8.3.
La abstracció n de datos logra la independencia de la representació n. Piense en la
implementació n de un tipo abstracto como si tuviera varios componentes:
s = IntSet()
s.insertar(3
)
s.insertar(4
) imprimir s
imprimirá ,
{3,4}
clase Persona(objeto):
def __init__(self,
name): """Crear una
persona"""
self.name = name
try:
lastBlank = nombre.rindex(' ')
self.lastName =
nombre[lastBlank+1:]
excepto:
self.lastName =
nombre self.birthday =
Ninguno
def getNombre(self):
"""Devuelve el nombre
completo de uno mismo"""
return self.name
def getLastName(self):
"""Devuelve el apellido
propio""" return
self.lastName
def obtenerEdad(self):
"""Devuelve la edad actual de uno
mismo en días""" if self.birthday ==
Ninguno:
aumentar ValueError
volver (fechahora.fecha.hoy() - yo.cumpleaños).días
Tenga en cuenta que cada vez que se crea una instancia de Persona, se proporciona un
argumento a la
función de inicio. En general, cuando creamos una instancia de una
clase, necesitamos mirar el
9 Capítulo 8. Clases y programación orientada a
especificació n de la funció n init para esa clase para saber qué argumentos
proporcionar y qué propiedades deberían tener esos argumentos.
Despué s de ejecutar este có digo, habrá tres instancias de la clase Person. Luego se
puede acceder a la informació n sobre estas instancias utilizando los mé todos
asociados con ellas. Por ejemplo, him.getLastName() devolverá 'Obama'. La
expresió n him.lastName también devolverá 'Obama'; sin embargo, por razones que
se analizan má s adelante en este capítulo, escribir expresiones que acceden
directamente a variables de instancia se considera de mala forma y debe evitarse.
De manera similar, no existe una forma apropiada para que un usuario de la
abstracció n Person extraiga el cumpleañ os de una persona, a pesar de que la
implementació n contiene un atributo con ese valor. Sin embargo, existe una forma
de extraer informació n que depende del cumpleañ os de la persona, como se ilustra
en la ú ltima instrucció n impresa del có digo anterior.
primero imprimirá
Michael Guttag
Barack Hussein Obama
Madonna
y luego imprimir
Michael
GuttagMadonna
Barack Hussein Obama
Capítulo 8. Clases y programación orientada a 9
8.2 Herencia
Muchos tipos tienen propiedades en comú n con otros tipos. Por ejemplo, los tipos
list y str tienen funciones len que significan lo mismo. La herencia proporciona un
mecanismo conveniente para construir grupos de abstracciones relacionadas.
Permite a los programadores crear una jerarquía de tipos en la que cada tipo hereda
atributos de los tipos que está n por encima de é l en la jerarquía.
El objeto de clase está en la parte superior de la jerarquía. Esto tiene sentido, ya que
en Python todo lo que existe en tiempo de ejecució n es un objeto. Debido a que
Person hereda todas las propiedades de los objetos, los programas pueden vincular
una variable a Person, agregar una Person a una lista, etc.
En la jerga declase
la programació n orientada a objetos,MITPersonaes una subclase
MITPersona(Persona):
dePersona, y por lo tanto hereda los atributos de su superclase. Ademá s de lo que
nextIdNum = 0 #número de
hereda, la subclase puede:
identificación
Agregar nuevos def ejemplo,
atributos. Por __init__(self,
MITPersonaha agregado la
variable de clasesiguienteIdNum, la variable de instanciaidNúm, y
nombre):
el mé todogetIdNum.
Persona.__init__(self, nombre)
self.idNum
Anularatributos =
de la superclase. Por ejemplo, MITPersonaha anuladoen
esoylt. MITPerson.nextIdNum
MITPerson.nextIdNum += 1
El mé todoMITPerson. en esoprimero invocaPersona. en esopara inicializar la
def getIdNum(self):
variable de instancia heredada
return propio.nombre. Luego se inicializaself.idNum, una
variable de instancia que instancias de MITPersonatener pero instancias
self.idNum
dePersonano.
Considere el có digo
p1 = MITPersona('Bárbara Castor')
print str(p1) + 'El número de identificación de \'s es ' + str(p1.getIdNum())
(Recuerde que en una cadena, el cará cter “\” es un cará cter de escape que se usa
para indicar que el siguiente cará cter debe tratarse de manera especial. En la
cadena
'El número de identificación de \'s es '
el “\” indica que el apó strofe es parte de la cadena, no un delimitador que termina la
cadena).
Hemos creado cuatro personas virtuales, tres de las cuales se llaman Billy Bob Beaver.
Dos de los Billy Bobs son del tipo MITPerson y uno es simplemente una Persona. Si
ejecutamos las líneas de có digo
imprime 'p1 < p2 =', p1 <
p2 imprime 'p3 < p2 =', p3
< p2 imprime 'p4 < p1 =',
p4 < p1
el intérprete imprimirá
p1 < p2 =
Verdadero p3 <
p2 = Falso p4 <
p1 = Verdadero
Dado que p1, p2 y p3 son todos del tipo MITPerson, el intérprete utilizará el mé todo lt
definido en la clase MITPerson al evaluar las dos primeras comparaciones, por lo que el
orden se basará en nú meros de identificació n. En la tercera comparació n, el operador <
se aplica a operandos de diferentes tipos. Dado que el primer argumento de la expresió n
se usa para determinar qué método lt invocar, p4 < p1 es una abreviatura de p4. (p1).
Por tanto, el inté rprete utiliza el mé todo lt asociado al tipo de p4, Person, y las
“personas” se ordenará n por nombre.
estudiante de clase
(MITPerson): pase
clase UG(Estudiante):
def __init__(self, nombre, classYear):
MITPerson.__init__(self, nombre)
self.year = classYear
def getClass(self):
return self.año
clase Grad
(Estudiante):
pase
Figura 8.4 Dos clases de estudiantes
imprimirá
Buzz Aldrin es un estudiante de posgrado es cierto
Buzz Aldrin es un estudiante de pregrado es Falso
huellas dactilares
Buzz Aldrin es estudiante es Verdadero
Billy Beaver es estudiante es
Verdadero Billy Bob Beaver es
estudiante es Falso
def getOldSchool(self):
return self.fromSchool
A veces, la subclase anula los mé todos de la superclase, pero esto debe hacerse
con cuidado. En particular, los comportamientos importantes del supertipo
deben ser respaldados por cada uno de sus subtipos. Si el có digo del cliente
funciona correctamente utilizando una instancia del supertipo, tambié n debería
funcionar correctamente cuando se sustituye una instancia del subtipo por la
instancia del supertipo. Por ejemplo, debería
Capítulo 8. Clases y programación orientada a 1
ser posible escribir có digo de cliente usando la especificació n deAlumnoy hacer que
funcione correctamente en unEstudiante de intercambio.39
Por el contrario, no hay razó n para esperar que el có digo escrito funcione para
Estudiante de intercambiodebería funcionar para tipos arbitrarios deAlumno.
def obtenerEstudiantes(self):
"""Devolver una lista de los estudiantes en el libro de
calificaciones""" si no es self.isSorted:
self.students.sort()
self.isSorted = True
return self.students[:] #return copia de la lista de estudiantes
39Esteprincipio de sustitució n fue enunciado claramente por primera vez por Barbara
Liskov y Jeannette Wing en su artículo de 1994, "Una noció n conductual de la
subtipificació n".
1 Capítulo 8. Clases y programación orientada a
La figura 8.5 contiene una clase que se puede utilizar para realizar un
seguimiento de las calificaciones de un conjunto de estudiantes. Las instancias de
la clase Calificaciones se implementan mediante una lista y un diccionario. La
lista realiza un seguimiento de los estudiantes de la clase. El diccionario asigna el
nú mero de identificació n de un estudiante a una lista de calificaciones.
Observe que getGrades devuelve una copia de la lista de calificaciones asociadas con
un estudiante y getStudents devuelve una copia de la lista de estudiantes. El costo
computacional de copiar las listas podría haberse evitado simplemente devolviendo
las variables de instancia. Sin embargo, es probable que esto genere problemas.
Considere el có digo
todosEstudiantes =
curso1.getEstudiantes()allStudents.extend(curso2.ge
tStudents())
La figura 8.6 contiene una funció n que utiliza Class Grades para producir un
informe de calificaciones para algunos estudiantes que toman 6.00, el curso del
MIT para el cual se desarrolló este libro.
Capítulo 8. Clases y programación orientada a 1
def gradeReport(curso):
"""Asume que el curso es del tipo
Calificaciones""" report = ''
para s en
curso.getStudents(): tot
= 0.0
numCalificaciones = 0
para g en
curso.getGrades(s): tot
+= g
numGrades +=
1 intento:
promedio =
tot/numCalificaciones
informe = informe +
'\n'\
+ str(s) + 'La calificación media es ' +
str(promedio) excepto ZeroDivisionError:
informe = informe + '\n'\
+ str(s) + 'no tiene calificaciones'
informe de retorno
mejorar la eficiencia) sin preocuparse de que el cambio rompa el có digo que usa la
clase.
¿Por qué es esto desafortunado? Porque el có digo del cliente se basa en algo que no
forma parte de la especificació n de Person y, por lo tanto, está sujeto a cambios. Si se
cambiara la implementació n de Person, por ejemplo, para extraer el apellido cada
vez que se solicite en lugar de almacenarlo en una variable de instancia, entonces el
có digo del cliente ya no funcionaría.
Python no solo permite que los programas lean instancias y variables de clase
desde fuera de la definició n de clase, sino que tambié n permite que los
programas escriban estas variables. Entonces, por ejemplo, el có digo
Rafael.cumpleañ os = '21/8/50' es perfectamente legal. Esto conduciría a un error
de tipo de tiempo de ejecució n, si Rafael.getAge se invocara má s tarde en el
cá lculo. Incluso es posible crear variables de instancia desde fuera de la
definició n de clase. Por ejemplo, Python no se quejará si la declaració n de
asignació n me.age = Rafael.getAge() ocurre fuera de la definició n de la clase.
Si bien esta verificació n semá ntica está tica dé bil es una falla en Python, no es
una falla fatal. Un programador disciplinado puede simplemente seguir la regla
sensata de no acceder directamente a los atributos de los datos desde fuera de la
clase en la que está n definidos, como hacemos en este libro.
8.3.1 Generadores
Un riesgo percibido de la ocultació n de informació n es que evitar que los programas
cliente accedan directamente a las estructuras de datos críticos conduce a una
pé rdida de eficiencia inaceptable. En los primeros días de la abstracció n de datos,
muchos estaban preocupados por el costo de introducir llamadas a
funciones/métodos extrañ os. La tecnología de compilació n moderna hace que esta
preocupació n sea discutible. Un problema má s grave es que los programas cliente se
verá n obligados a utilizar algoritmos ineficientes.
def obtenerEstudiantes(self):
Figura 8.7 Nueva versión de obtenerEstudiantes
"""Regresar a los estudiantes en el libro de
Cualquier calificaciones unon que
definició n de funció a lacontenga
vez""" una
si no es self.isSorted:
declaració n de rendimiento se
self.students.sort
trata de manera especial. La presencia de yield le dice al sistema de Python que la
() self.isSorted =
funció n es un generador.
True Los generadores se usan normalmente junto con
parafor.40
declaraciones s en
self.students:
Al comienzo de la primera iteració n de un ciclo for, el intérprete comienza a ejecutar
el có digo en el cuerpo del generador. Se ejecuta hasta la primera vez que se ejecuta
una declaració n de rendimiento, momento en el que devuelve el valor de la
expresió n en la declaració n de rendimiento. En la siguiente iteració n, el generador
reanuda la ejecució n inmediatamente despué s del rendimiento, con todas las
variables locales vinculadas a los objetos a los que estaban vinculadas cuando se
ejecutó la declaració n de rendimiento, y nuevamente se ejecuta hasta que se ejecuta
una declaració n de rendimiento. Continú a haciendo esto hasta que se queda sin
có digo para ejecutar o ejecuta una declaració n de devolució n, momento en el que se
sale del ciclo.
huellas dactilares
julie
charlie
no tiene que modificarse para aprovechar la versió n de la clase Grades que contiene
la nueva implementació n de getStudents. El mismo bucle for puede iterar sobre los
valores proporcionados por getStudents independientemente de si getStudents
devuelve una lista de valores o genera un valor a la vez. Generando un valor en
un tiempo será má s eficiente, porque no se creará una nueva lista que contenga a los
estudiantes.
Al principio, las hipotecas eran bestias relativamente simples. Uno pidió dinero
prestado a un banco e hizo un pago fijo cada mes durante la vigencia de la hipoteca,
que generalmente oscilaba entre quince y treinta añ os. Al final de ese período, el
banco había devuelto el pré stamo inicial (el capital) má s los intereses, y el
propietario era dueñ o de la casa “libre y limpio”.
Hacia finales del siglo XX, las hipotecas comenzaron a ser mucho má s complicadas.
Las personas podían obtener tasas de interés má s bajas pagando "puntos" en el
momento en que adquirieron la hipoteca. Un punto es un pago en efectivo del 1% del
valor del préstamo. Las personas podían tomar hipotecas que eran de "solo interé s"
por un período de tiempo. Es decir, durante un nú mero de meses al inicio del
pré stamo el prestatario pagaba ú nicamente los intereses devengados y nada de
principal. Otros pré stamos involucraron tasas mú ltiples. Por lo general, la tasa inicial
(llamada "tasa teaser") era baja y luego aumentaba con el tiempo. Muchos de estos
pré stamos eran de tasa variable: la tasa a pagar despué s del período inicial variaría
dependiendo de algú n índice destinado a reflejar el costo para el prestamista de
tomar prestado en el mercado mayorista de cré dito.42
En principio, dar a los consumidores una variedad de opciones es algo bueno. Sin
embargo, los proveedores de préstamos sin escrú pulos no siempre tuvieron
cuidado de explicar completamente las posibles implicaciones a largo plazo de
las diversas opciones, y algunos prestatarios tomaron decisiones que resultaron
tener consecuencias nefastas.
Construyamos un programa que examine los costos de tres tipos de pré stamos:
41En este contexto, vale la pena recordar la etimología de la palabra hipoteca. El American
Heritage Dictionary of the English Language remonta la palabra a las antiguas palabras
francesas para muerto (mort) y prenda (gage). (Esta derivació n también explica por qué la "t"
en el medio de la hipoteca es muda).
42La tasa de oferta interbancaria de Londres (LIBOR) es probablemente el índice má s
utilizado.
Capítulo 8. Clases y programación orientada a 1
La figura 8.8 contiene la clase abstracta Hipoteca. Esta clase contiene mé todos
que son compartidos por cada una de las subclases, pero no está diseñ ado para
ser instanciado directamente.
Cuando su có digo incorpore fó rmulas que haya buscado, asegú rese de que:
La figura 8.9 contiene clases que implementan dos tipos de hipoteca. Cada una de
estas clases anula init y hereda los otros tres mé todos de Mortgage.
clase TwoRate(Hipoteca):
def __init__(self, préstamo, r, meses, teaserRate, teaserMonths):
Hipoteca.__init__(self, préstamo, teaserRate, meses)
self.teaserMonths = teaserMonths
self.teaserRate = teaserRate
self.nextRate = r/12.0
self.legend =
str(teaserRate*100)\
+ '% para ' + str(self.teaserMonths)\
+ ' meses, luego ' + str(r*100) +
'%' def realizarPago(auto):
if len(self.paid) == self.teaserMonths + 1:
self.rate = self.nextRate
auto.pago = findPayment(auto.deuda[-1], auto.tasa,
self.months - self.teaserMonths)
La figura 8.11 contiene una funció n que calcula e imprime el costo total de cada
tipo de hipoteca para un conjunto de pará metros de muestra. Comienza creando
una hipoteca de cada tipo. A continuació n, realiza un pago mensual de cada uno
durante un nú mero determinado de añ os. Finalmente, imprime el monto total
de los pagos realizados por cada pré stamo.
Fijo, 7,0%
Pagos totales = $479017
Fijo, 5.0%, 3.25 puntos
Pagos totales = $393011 4.5%
por 48 meses, luego 9.5%
Pagos totales = $551444
A primera vista, los resultados parecen bastante concluyentes. El pré stamo de tasa
variable es una mala idea (para el prestatario, no para el banco) y el préstamo de
tasa fija con puntos cuesta menos. Sin embargo, es importante tener en cuenta que
el costo total no es la ú nica medida por la cual se deben juzgar las hipotecas. Por
ejemplo, un prestatario que espera tener mayores ingresos en el futuro puede estar
dispuesto a pagar má s en los ú ltimos añ os para disminuir la carga de los pagos al
principio.
Esto sugiere que en lugar de mirar un solo nú mero, deberíamos mirar los pagos a
lo largo del tiempo. Esto, a su vez, sugiere que nuestro programa debería producir
parcelas diseñ adas para mostrar có mo se comporta la hipoteca a lo largo del
tiempo. Lo haremos en la Secció n 11.2.
9A INTRODUCCIÓN SIMPLISTA A LA ALGORITMICA
COMPLEJIDAD
Ahora que tenemos una forma má s abstracta de pensar sobre el significado del
tiempo, pasamos a la cuestió n de la dependencia del valor de la entrada. Nos
ocupamos de eso alejá ndonos de expresar la complejidad del tiempo como un solo
nú mero y en su lugar relacioná ndolo con los tamañ os de las entradas. Esto nos
permite comparar la eficiencia de dos algoritmos hablando de có mo crece el tiempo
de ejecució n de cada uno con respecto al tamañ o de las entradas.
43Un modelo má s preciso para las computadoras de hoy podría ser una má quina de
acceso aleatorio paralelo. Sin embargo, eso agrega una complejidad considerable al
Capter9.Una introducción simplista a la complejidad
aná lisis algorítmico y, a menudo, no hace una diferencia cualitativa importante en la
1
respuesta.
1 Capter9. ASsimplistaEntroducción
lo suficientemente bueno como para saber que "la mayoría de las veces" el sistema
de control de trá fico aé reo advierte sobre colisiones inminentes antes de que
ocurran.
El nú mero de pasos necesarios para ejecutar este programa es algo así como 2 (1
para la instrucció n de asignació n inicial y uno para la devolució n) + 5n (contando 1
paso para la prueba en el ciclo while, 2 pasos para la primera instrucció n de
asignació n en el ciclo while) y 2 pasos para la segunda declaració n de asignació n en
el ciclo). Entonces, por ejemplo, si n es 1000, la funció n ejecutará aproximadamente
5002 pasos.
Debería ser inmediatamente obvio que a medida que n crece, preocuparse por la
diferencia entre 5n y 5n+2 es un poco tonto. Por esta razó n, normalmente
ignoramos las constantes aditivas cuando razonamos sobre el tiempo de
ejecució n. Las constantes multiplicativas son má s problemá ticas. ¿Debería
importarnos si el cá lculo toma 1000 pasos o 5000 pasos? Los factores
multiplicativos pueden ser importantes.
Si un motor de bú squeda tarda medio segundo o 2,5 segundos en atender una
consulta puede ser la diferencia entre si las personas usan ese motor de bú squeda
o acuden a un competidor.
Por otro lado, cuando uno está comparando dos algoritmos diferentes, a menudo
ocurre que incluso las constantes multiplicativas son irrelevantes. Recuerde que en
el Capítulo 3 vimos dos algoritmos, enumeració n exhaustiva y bú squeda de
bisecció n, para encontrar una aproximació n a la raíz cuadrada de un nú mero de
coma flotante. Las funciones basadas en cada uno de estos algoritmos se muestran
en la Figura 9.1 y la Figura 9.2.
Figura
def9.1 Uso de la enumeración exhaustiva
squareRootExhaustive(x, epsilon): para aproximar la raíz cuadrada
"""Asume que x y epsilon son flotantes positivos & epsilon
< 1 Devuelve ay tal que y*y está dentro de epsilon de
x"""
paso = épsilon**2
respuesta = 0.0
while abs(ans**2 - x) >= épsilon y ans*ans <= x:
ans += paso
si ans*ans > x:
aumentar
Capter9.Una introducción simplista a la complejidad 1
Vimos que la enumeració n exhaustiva era tan lenta que resultaba poco prá ctica
para muchas combinaciones de x y é psilon. Por ejemplo, evaluar
squareRootExhaustive(100, 0.0001) requiere aproximadamente mil millones de
iteraciones del bucle. Por el contrario, la evaluació n de squareRootBi(100, 0.0001)
requiere aproximadamente veinte iteraciones de un ciclo while un poco má s
complejo. Cuando la diferencia en el nú mero de iteraciones es tan grande, en
realidad no importa cuá ntas instrucciones haya en el ciclo. Es decir, las constantes
multiplicativas son irrelevantes.
Si se supone que cada línea de có digo tarda una unidad de tiempo en ejecutarse, el
tiempo de ejecució n de esta funció n se puede describir como 1000 + x + 2x2. La
constante 1000 corresponde al nú mero de veces que se ejecuta el primer bucle. El
té rmino x corresponde al nú mero de veces que se ejecuta el segundo bucle.
Finalmente, el término 2x2 corresponde al tiempo empleado en ejecutar las dos
sentencias en el bucle for anidado. En consecuencia, la llamada f(10) imprimirá
Número de adiciones hasta ahora
1000 Número de adiciones hasta
ahora 1010 Número de adiciones
hasta ahora 1210
Claramente, podemos obtener una noció n significativa de cuá nto tiempo tardará
este có digo en ejecutarse con entradas muy grandes considerando solo el ciclo
interno, es decir, el componente cuadrá tico. ¿Deberíamos preocuparnos por el hecho
de que este ciclo toma 2x2 pasos en lugar de x2 pasos? Si su computadora ejecuta
aproximadamente 100 millones de pasos por segundo, evaluar f tomará alrededor
de 5,5 horas. Si pudiéramos reducir la complejidad a x2 pasos, tomaría alrededor de
2,25 horas. En cualquier caso, la moraleja es la misma: probablemente deberíamos
buscar un algoritmo má s eficiente.
Este tipo de aná lisis nos lleva a utilizar las siguientes reglas generales para describir
la complejidad asintó tica de un algoritmo:
La notació n asintó tica má s comú nmente usada se llama notació n “Big O”.44 La
notació n Big O se usa para dar un límite superior en el crecimiento asintó tico (a
menudo llamadoel orden de crecimiento) de una funció n. Por ejemplo, la fó rmula f(x)
O(x2) significa que la funció n f no crece má s rá pido que el polinomio cuadrá tico x2, en un
sentido asintó tico.
44Lafrase "Big O" fue introducida en este contexto por el científico informá tico Donald
Knuth en la década de 1970. Eligió la letra griega Omicron porque los teó ricos de los
nú meros habían usado esa letra desde finales del siglo XIX. elsiglo para denotar un
concepto relacionado.
Capter9.Una introducción simplista a la complejidad 1
45Los miembros má s pedantes de la comunidad informá tica utilizan Big Theta, Θ, en lugar
de Big O para esto.
1 Capter9. ASsimplistaEntroducción
def intToStr(i):
"""Supone que i es un int no negativo
Devuelve una representación de cadena decimal de
i""" dígitos = '0123456789'
si i == 0:
devuelve
'0'
resultado =
'' mientras i
> 0:
resultado = dígitos[i%10] +
resultado i = i//10
resultado devuelto
Dado que no hay llamadas a funciones o mé todos en este có digo, sabemos que solo
tenemos que observar los bucles para determinar la clase de complejidad. Solo hay
un bucle, por lo que lo ú nico que debemos hacer es caracterizar el nú mero de
iteraciones. Eso se reduce a la cantidad de veces que uno puede dividir i entre 10.
Entonces, la complejidad de intToStr es O(log(i)).
Por supuesto, un programa no necesita tener un ciclo para tener complejidad lineal.
Capter9.Una introducción simplista a la complejidad 1
Considerar
factorial def(x):
"""Asume que x es un int positivo
Devuelve x!"""
si x == 1:
devuelve
1
demás:
devuelve x*factorial(x-1)
No hay bucles en este có digo, por lo que para analizar la complejidad necesitamos
averiguar cuá ntas llamadas recursivas se realizan. La serie de llamadas es
simplementefactoriales(x),factoriales(x-1),factoriales(x-2),...,
factoriales(1). La longitud de esta serie, y por lo tanto la complejidad de la funció n,
esBuey).
Figura 9.4
intersección Implementación
def (L1, L2): de la intersección de listas
"""Supone: L1 y L2 son listas
El tiempo de ejecució
Devuelve n deuna
la parte
listaque crea
que eslala
lista que puede contener
intersección de L1 yduplicados
L2"""
es claramente O(len(L1)*len(L2)).
#Construir una lista Aque
primera vista,elementos
contenga parece quecomunes
la parte del có digo
que crea la tmp
lista =sin
[]duplicados es lineal en la longitud de tmp, pero no lo es. La
para e1 en L1:
prueba e not inpara
resulte2involucra
en potencialmente mirar cada elemento en result, y
por lo tanto es L2:
O(len(result)); en consecuencia, la segunda parte de la
implementació n essiO(len(tmp)*len(resultado)).
e1 == e2: Dado que las longitudes de result y
tmp.append(e
tmp está n limitadas por la longitud del menor de L1 y L2, y dado que ignoramos los
1)
té rminos aditivos,
#Construirla complejidad
una listadesin
intersect es O(len(L1)*len(L2)).
resultado de duplicados = []
9.3.6 Complejidad
para e en tmp: Exponencial
si e no está en
Como veremos má s adelante en este libro, muchos problemas importantes son
inherentemente exponenciales, es decir, resolverlos por completo puede
requerir un tiempo que es exponencial en el tamañ o de la entrada. Esto es
desafortunado, ya que rara vez vale la pena escribir un programa que tenga una
probabilidad razonablemente alta de tomar un tiempo exponencial para
ejecutarse.
Capter9.Una introducción simplista a la complejidad 1
La grá fica de abajo ya la izquierda muestra que hay una diferencia significativa
entre O(n) y O(n log(n)). Dada la lentitud con la que crece log(n), esto puede parecer
un poco sorprendente, pero tenga en cuenta que es un factor multiplicativo.
También tenga en cuenta que en la mayoría de las situaciones prá cticas, O(n log(n))
es lo suficientemente rá pido como para ser ú til.
Capter9.Una introducción simplista a la complejidad 1
Por otro lado, como sugiere el diagrama de abajo ya la derecha, hay muchas
situaciones en las que una tasa de crecimiento cuadrá tica es prohibitiva. La curva
cuadrá tica crece tan rá pidamente que es difícil ver que la curva logarítmica lineal
es pareja en el grá fico.
En el grá fico de la izquierda, los nú meros a la izquierda del eje y van de 0,0 a 1,2.
Sin embargo, la notació n x1e301 en la parte superior izquierda significa que cada
marca en el eje y debe multiplicarse por 10301. Por lo tanto, los valores de y
trazados van de 0 a aproximadamente 1,1*10301. Sin embargo, parece casi como
si no hubiera curvas en el grá fico de la izquierda. Esto se debe a que una funció n
exponencial crece tan rá pidamente que, en relació n con el valor y del punto má s
alto (que determina la escala del eje y), los valores y de los puntos anteriores de
la curva exponencial (y todos los puntos de la curva cuadrá tica) son casi
indistinguible de 0.
El grá fico de la derecha aborda este problema mediante el uso de una escala
logarítmica en el eje y. Uno puede ver fá cilmente que los algoritmos exponenciales
no son prá cticos para todas las entradas excepto para las má s pequeñ as.
Observe, por cierto, que cuando se grafica en una escala logarítmica, una curva
exponencial aparece como una línea recta. Tendremos má s que decir sobre esto en
capítulos posteriores.
1 Capter9. ASsimplistaEntroducción
Aunque dedicamos una buena cantidad de pá ginas de este libro a hablar sobre la
eficiencia, el objetivo no es convertirlo en un experto en el diseñ o de programas
eficientes. Hay muchos libros largos (e incluso algunos buenos libros largos)
dedicados exclusivamente a ese tema.46 En el Capítulo 9, presentamos algunos de
los conceptos bá sicos que subyacen al aná lisis de complejidad. En este capítulo
usamos esos conceptos para observar la complejidad de algunos algoritmos
clá sicos. El objetivo de este capítulo es ayudarlo a desarrollar algunas intuiciones
generales sobre có mo abordar las cuestiones de eficiencia. Para cuando termine
este capítulo, debería comprender por qué algunos programas se completan en un
abrir y cerrar de ojos, por qué algunos necesitan ejecutarse durante la noche y por
qué algunos no se completará n en su vida.
Luego analizamos algunos problemas (p. ej., encontrar una aproximació n a las
raíces de un polinomio) donde el espacio de bú squeda era demasiado grande
para que la fuerza bruta fuera prá ctica. Esto nos llevó a considerar algoritmos
má s eficientes como la bú squeda de bisecció n y Newton-Raphson. El punto
principal fue que la clave de la eficiencia es un buen algoritmo, no trucos de
codificació n inteligentes.
Los algoritmos eficientes son difíciles de inventar. Los científicos informá ticos
profesionales exitosos podrían inventar tal vez un algoritmo durante toda su
carrera, si tienen suerte. La mayoría de nosotros nunca inventamos un algoritmo
novedoso. Lo que hacemos en cambio es aprender a reducir los aspectos má s
complejos de los problemas a los que nos enfrentamos a problemas previamente
resueltos. Má s específicamente, nosotros
Este capítulo contiene algunos ejemplos destinados a brindarle cierta intuició n sobre el
diseñ o de algoritmos. Muchos otros algoritmos aparecen en otras partes del libro.
En esta secció n, examinaremos dos algoritmos para buscar en una lista. Cada uno
cumple con las especificaciones
def search(L, e): """Asume
que L es una lista.
Devuelve True si e está en L y False en caso contrario"""
Por supuesto, sabemos que las listas de Python pueden contener objetos de tipos
distintos a int, y que la misma lista puede contener objetos de muchos tipos y
tamañ os diferentes. Podría pensar que esto presentaría un problema, pero no es
así.
En Python, una lista se representa como una longitud (el nú mero de objetos en la
lista) y una secuencia de punteros48 de tamañ o fijo a los objetos. La figura 10.1
ilustra el uso de estos punteros. La regió n sombreada representa una lista que
contiene cuatro elementos.
El cuadro sombreado má s a la izquierda contiene un puntero a un nú mero entero
que indica la longitud de la lista. Cada uno de los otros cuadros sombreados contiene
un puntero a un objeto de la lista.
47El
nú mero de bits que se utilizan para almacenar un nú mero entero, a menudo llamado
tamañ o de palabra, generalmente lo dicta el hardware de la computadora.
48De tamañ o 32 bits en algunas implementaciones y 64 bits en otras.
49Mi diccionario define "indirecció n" como "falta de franqueza y apertura: engañ o". De hecho,
la palabra generalmente tuvo una implicació n peyorativa hasta alrededor de 1950, cuando los
informá ticos se dieron cuenta de que era la solució n a muchos problemas.
1 Capítulo 10. Algunos algoritmos simples y estructuras
a la cosa inicialmente buscada. Esto es lo que sucede cada vez que usamos una
variable para referirnos al objeto al que está vinculada esa variable. Cuando
usamos una variable para acceder a una lista y luego una referencia almacenada
en esa lista para acceder a otro objeto, estamos pasando por dos niveles de
direccionamiento indirecto.50
Pero supongamos que sabemos algo sobre el orden en que se almacenan los
elementos, por ejemplo, supongamos que sabemos que tenemos una lista de
nú meros enteros almacenados en orden ascendente. Podríamos cambiar la
implementació n para que la bú squeda se detenga cuando llegue a un nú mero mayor
que el nú mero que está buscando:
búsqueda de definición (L, e):
"""Asume que L es una lista, cuyos elementos están en orden
ascendente.
Devuelve True si e está en L y False de lo
contrario """ para i en range(len(L)):
si L[i] == e:
devuelve
True si L[i] >
e:
volver Falso
volver Falso
Sin embargo, podemos obtener una mejora considerable en la complejidad del peor
de los casos mediante el uso de un algoritmo, la bú squeda binaria, que es similar al
algoritmo de bú squeda de bisecció n utilizado en el Capítulo 3 para encontrar una
aproximació n a la raíz cuadrada de un nú mero de punto flotante. Allí confiamos en
el hecho de que existe un orden total intrínseco en los nú meros de punto flotante.
Aquí nos basamos en la suposició n de que la lista está ordenada.
La idea es sencilla:
Si este fuera un libro sobre algoritmos, ahora nos sumergiríamos en un aná lisis
cuidadoso usando algo llamado relació n de recurrencia. Pero como no lo es,
adoptaremos un enfoque mucho menos formal que comienza con la pregunta
"¿Có mo sabemos que el programa termina?" Recuerde que en el Capítulo 3 hicimos
la misma pregunta sobre un ciclo while. Respondimos a la pregunta
proporcionando una funció n decreciente para el ciclo. Hacemos lo mismo aquí. En
este contexto, la funció n decreciente tiene las propiedades:
1. Asigna los valores a los que está n vinculados los pará metros
formales a un nú mero entero no negativo.
2. cuando su valor es0, la recursividad termina.
3. Para cada llamada recursiva, el valor de la funció n decreciente es menor
que el valor de la funció n decreciente al ingresar a la instancia de la
funció n que realiza la llamada.
La funció n decreciente para bSearch es alta-baja. La instrucció n if en la bú squeda
asegura que el valor de esta funció n decreciente sea al menos 0 la primera vez que se
llama a bSearch (propiedad de funció n decreciente 1).
La funció n bSearch contiene dos llamadas recursivas. Una llamada usa argumentos
que cubren todos los elementos a la izquierda del medio, y la otra llamada usa
argumentos que cubren todos los elementos a la derecha del medio. En cualquier
caso, el valor de alto-bajo se reduce a la mitad (satisfaciendo la propiedad 3 de la
funció n decreciente).
51Recuerda que cuando miras ó rdenes de crecimiento, la base del logaritmo es irrelevante.
Capítulo 10. Algunos algoritmos simples y estructuras 1
Sea O(sortComplexity(L)) la complejidad de ordenar una lista. Dado que sabemos que
siempre podemos buscar una lista en tiempo O(len(L)), la cuestió n de si primero
debemos ordenar y luego buscar se reduce a la pregunta, es (sortComplexity(L) +
log(len(L) )) < largo(L)? La respuesta, lamentablemente, es no. No se puede ordenar
una lista sin mirar cada elemento de la lista al menos una vez, por lo que no es posible
ordenar una lista en tiempo sublineal.
¿Significa esto que la bú squeda binaria es una curiosidad intelectual sin importancia
prá ctica? Felizmente, no. Supongamos que uno espera buscar en la misma lista
muchas veces. Bien podría tener sentido pagar los gastos generales de clasificar la
lista una vez y luego amortizar el costo de la clasificació n en muchas bú squedas. Si
esperamos buscar en la lista k veces, la pregunta relevante es: ¿es
(sortComplexity(L) + k*log(len(L))) menor que k*len(L)? A medida que k se vuelve
grande, el tiempo requerido para ordenar la lista se vuelve cada vez má s irrelevante.
El tamañ o de k debe ser depende del tiempo que se tarde en ordenar una lista. Si,
por ejemplo, la clasificació n fuera exponencial en el tamañ o de la lista, k tendría que
ser bastante grande.
La observació n clave hecha por von Neumann es que dos listas ordenadas pueden
fusionarse eficientemente en una sola lista ordenada. La idea es mirar el primer
elemento de cada lista y mover el má s pequeñ o de los dos al final de la lista de
resultados. Cuando una de las listas está vacía, todo lo que queda es copiar los
elementos restantes de la otra lista. Considere, por ejemplo, fusionar las dos listas
[1,5,12,18,19,20] y [2,3,4,17]:
resultado
= [] i, j
= 0, 0
while i < len(izquierda) y j <
len(derecha): if compare(left[i],
right[j]):
resultado.append(izq
uierda[i]) i += 1
demás:
resultado.append(derec
ha[j]) j += 1
while (i <
len(izquierda)):
resultado.append(izq
uierda[i]) i += 1
while (j <
len(derecha)):resulta
do.append(derecha[j])
j += 1
operador de
importación de
resultados
devueltos
eleses mucho mejor que el tipo de selecció n O(len(L)2). Por ejemplo, si L tiene 10.000
mielementos, largo(L)2 escien millones perolargo(L)*log2(largo(L)) esacerca de130,000.
imprimirá
[2, 3, 5]
[3, 5, 2]
[2, 3, 5] ['a',
'b', 'c']
Rastreo (llamadas recientes más última):
Archivo "/current/mit/Teaching/600/book/10-
AlgorithmsChapter/algorithms.py", línea 168, en <módulo>
D.ordenar()
AttributeError: el objeto 'dict' no tiene atributo 'sort'
Tanto el método list.sort como la funció n sorted pueden tener dos pará metros
adicionales. El pará metro clave juega el mismo papel que comparar en nuestra
implementació n de clasificació n por fusió n: se utiliza para proporcionar la funció n de
comparació n que se utilizará . El pará metro inverso especifica si la lista debe ordenarse
en orden ascendente o descendente. Por ejemplo, el có digo
54Timsort fue inventado por Tim Peters en 2002 porque no estaba satisfecho con el
algoritmo anterior utilizado en Python.
Capítulo 10. Algunos algoritmos simples y estructuras 1
Esto es bueno, pero aú n podemos preguntar, ¿es logarítmico lo mejor que podemos
hacer para la bú squeda cuando estamos dispuestos a hacer algú n
preprocesamiento?
Cuando introdujimos el tipo dict en el Capítulo 5, dijimos que los diccionarios usan
una técnica llamada hashing para hacer la bú squeda en el tiempo que es casi
independiente del tamañ o del diccionario. La idea bá sica detrá s de una tabla hash es
simple. Convertimos la clave en un nú mero entero y luego usamos ese nú mero
entero para indexar en una lista, lo que se puede hacer en tiempo constante. En
principio, los valores de cualquier tipo inmutable se pueden convertir fá cilmente en
un nú mero entero. Despué s de todo, sabemos que la representació n interna de cada
objeto es una secuencia de bits, y cualquier secuencia de bits puede verse como la
representació n de un nú mero entero. Por ejemplo, la representació n interna de 'abc'
es la cadena de bits 011000010110001001100011, que puede verse como una
representació n del entero decimal 6,382,179. Por supuesto,
¿Qué pasa con las situaciones en las que las claves ya son nú meros enteros?
Imagine, por el momento, que estamos implementando un diccionario cuyas
claves son nú meros de la Seguridad Social de EE. UU.55 Si representá ramos el
diccionario mediante una lista con 109 elementos y usá ramos los nú meros de la
Seguridad Social para indexar la lista, podríamos hacer bú squedas en tiempo
constante Por supuesto, si el diccionario contuviera entradas de só lo diez
mil(104) personas, esto desperdiciaría bastante espacio.
Lo que nos lleva al tema de las funciones hash. Una funció n hash asigna un gran
espacio de entradas (p. ej., todos los nú meros naturales) a un espacio má s
pequeñ o de salidas (p. ej., los nú meros naturales entre 0 y 5000). Las funciones
hash se pueden usar para convertir un gran espacio de claves en un espacio má s
pequeñ o de índices enteros.
55Un nú mero de Seguro Social de los Estados Unidos es un nú mero entero de nueve dígitos.
1 Capítulo 10. Algunos algoritmos simples y estructuras
La figura 10.6 usa una funció n hash simple (recuerde que i%j devuelve el resto
cuando el entero i se divide por el entero j) para implementar un diccionario con
enteros como claves.
La idea bá sica es representar una instancia de la clase intDict mediante una lista de
cubos hash, donde cada cubo es una lista de pares clave/valor. Al hacer que cada
cubo sea una lista, manejamos las colisiones almacenando todos los valores que
generan hash en el mismo cubo en la lista.
clase intDict(objeto):
"""Un diccionario con claves enteras"""
def
__str__(auto
): resultado
= '{'
para b en
El siguiente có digo primero construye un intDict con veinte entradas. Los valores de las
entradas son los nú meros enteros del 0 al 19. Las claves se eligen al azar de nú meros
enteros en el rango de 0 a 105 - 1. (Discutimos el mó dulo aleatorio en el Capítulo 12). El
có digo luego imprime el intDict usando el método str definido en la clase. Finalmente,
imprime los cubos de hash individuales iterando sobre D.cubos. (Esta es una terrible
violació n de la ocultació n de informació n, pero pedagó gicamente ú til).
importar al azar #un módulo de biblioteca estándar
D = intDict(29)
para i en el rango (20):
#elegir un int aleatorio entre 0 y 10**5
clave = random.randint(0, 10**5)
D.addEntry(clave, i)
imprime 'El valor del intDict es:'
imprime D
print '\n', 'Los cubos son:'
para hashBucket en D.buckets: #violates abstracción barrera
print ' ', hashBucket
1 Capítulo 10. Algunos algoritmos simples y estructuras
56Dado que los nú meros enteros se eligieron al azar, probablemente obtendrá resultados
diferentes si lo ejecuta.
11 PLOTEO Y MÁS SOBRE CLASES
Comencemos con un ejemplo simple que usa pylab.plot para producir dos grá ficos.
ejecutando
importar pylab
57http://www.mathworks.com/products/matlab/description1.html?s_cid=ML_b1008_desintro
1 Capítulo 11. Trazado y más sobre las
La barra en la parte superior contiene el nombre de la ventana, en este caso “Figura 1”.
58En algunos sistemas operativos, pylab.show() hace que el proceso que ejecuta Python se
suspenda hasta que se cierra la figura (haciendo clic en el botó n rojo redondo en la esquina
superior izquierda de la ventana). Esto es desafortunado. La solució n habitual es asegurarse de
que pylab.show() sea la ú ltima línea de có digo que se ejecutará .
59Para aquellos de ustedes que son demasiado jó venes para saberlo, el icono representa un
"disquete". Los disquetes fueron introducidos por primera vez por IBM en 1971. Tenían 8
pulgadas de diá metro y contenían 80,000 bytes. A diferencia de los disquetes posteriores, en
realidad eran disquetes. La PC IBM original tenía una ú nica unidad de disquete de 5,5 pulgadas y
160 Kbytes. Durante la mayor parte de las dé cadas de 1970 y 1980, los disquetes fueron el
dispositivo de almacenamiento principal para las computadoras personales. La transició n a los
gabinetes rígidos (como se representa en el ícono que inició esta digresió n) comenzó a mediados
de la dé cada de 1980 (con Macintosh), lo que no impidió que la gente siguiera llamá ndolos
disquetes.
Capítulo 11. Trazado y más sobre las 1
El có digo
pylab.figure(1) #crear la figura 1
pylab.plot([1,2,3,4], [1,2,3,4]) #dibujar en la
figura 1 pylab.figure(2) #crear la figura 2
pylab .plot([1,4,2,3], [5,6,7,8]) #dibujar en la
figura 2 pylab.savefig('Figura-Addie') #guardar la
figura 2 pylab.figure(1) #ir volver a trabajar en
la figura 1 pylab.plot([5,6,10,3]) #dibujar de
nuevo en la figura 1 pylab.savefig('Figura-Jane')
#guardar la figura 1
este caso.
contenido deFigure-Jane.pngContenido de Figura-Addie.png
PyLab tiene una noció n de "cifra actual". Ejecutar pylab.figure(x) establece la figura
actual en la figura numerada x. Las llamadas ejecutadas posteriormente a las funciones
de trazado se refieren implícitamente a esa figura hasta que se produce otra invocació n
de pylab.figure. Esto explica por qué la figura escrita en el archivo Figure-Addie.png fue
la segunda figura creada.
Si observamos el có digo, podemos deducir que se trata de un grá fico que muestra el
crecimiento de una inversió n inicial de $10 000 a una tasa de interés compuesta anual
del 5 %.
Sin embargo, esto no se puede inferir fá cilmente mirando solo la trama en sí. Eso es
algo malo. Todas las parcelas deben tener títulos informativos y todos los ejes deben
estar etiquetados.
60Para mantener el precio bajo, decidimos publicar este libro en blanco y negro. Eso planteó un
dilema: ¿deberíamos discutir có mo usar el color en las tramas o no? Llegamos a la conclusió n de
que el color es demasiado importante para ignorarlo. Si desea ver có mo se ven los grá ficos en
color, ejecute el có digo.
Capítulo 11. Trazado y más sobre las 1
61El punto es una medida utilizada en tipografía. Es igual a 1/72 de pulgada, que es
0,3527 mm.
1 Capítulo 11. Trazado y más sobre las
Si está viendo grá ficos en una pantalla a color, tendrá pocas razones para
personalizar esta configuració n. Personalizamos la configuració n que usamos
para que fuera má s fá cil leer los grá ficos cuando los redujimos y los convertimos a
blanco y negro. Para obtener una discusió n completa sobre có mo personalizar la
configuració n,
consultehttp://matplotlib.sourceforge.net/users/customizing.html.
Los mé todos plotPayments y plotBalance son sencillos, pero usan una forma de pylab.plot
que aú n no hemos visto. Cuando una figura contiene varias parcelas, es ú til generar una
clave que identifique lo que se pretende que represente cada parcela. En la Figura 11.1,
cada invocació n de pylab.plot usa el argumento de la palabra clave label para asociar una
cadena con la trama producida por esa invocació n. (Este y otros argumentos de palabra
clave deben seguir cualquier cadena de formato). Luego se puede agregar una clave a la
figura llamando a la funció n pylab.legend, como se muestra en la Figura 11.3.
62Esuna aproximació n porque no realiza un cá lculo del valor presente neto para tener en
cuenta el valor del efectivo en el tiempo.
Capítulo 11. Trazado y más sobre las 1
clase Hipoteca(objeto):
"""Clase abstracta para construir diferentes tipos de hipotecas"""
def realizarPago(auto):
"""Hacer un pago"""
auto.pago.append(auto.pago)
reducción = auto.pago - auto.deuda[-1]*auto.tarifa
auto.deuda.append(auto.deuda[-1] - reducción)
def getTotalPaid(self):
"""Devolver el monto total pagado hasta
el momento""" devolver la suma (pago
propio)
definitivamente __str__(self):
return
self.leyenda
tipo, matriz, que PyLab hereda de NumPy.63 La invocació n pylab.array hace esto
explícito. Hay varias formas convenientes de manipular arreglos que no está n
disponibles para las listas. En particular, las expresiones se pueden formar utilizando
matrices y operadores aritméticos. Consideremos, por ejemplo, el có digo
a1 = pylab.array([1, 2, 4])
imprime 'a1 =', a1
a2 = a1*2
imprimir 'a2 =', a2
imprime 'a1 + 3 =', a1 + 3
imprime '3 - a1 =', 3 - a1
imprime 'a1 - a2 =', a1 -
a2 imprime 'a1*a2 =',
a1*a2
a1 = [1 2 4]
a2 = [2 4 8]
a1 + 3 = [4 5 7]
3 - a1 = [ 2 1 -1]
a1 - a2 = [-1 -2 -4]
a1*a2 = [2 8 32]
Hay varias formas de crear matrices en PyLab, pero la forma má s comú n es crear
primero una lista y luego convertirla.
La figura 11.2 repite las tres subclases de Hipoteca deCapítulo 8. Cada uno tiene un
distintivoen esoque anula elen esoenHipoteca. la subclaseDosTasatambién anula
lahacer el pagométodo deHipoteca.
clase Fijo(Hipoteca):
def __init__(self, préstamo, r, meses):
Hipoteca.__init__(self, préstamo, r, meses)
self.legend = 'Fixed, ' + str(r*100) + '%'
clase FixedWithPts(hipoteca):
def __init__(self, préstamo, r, meses, pts):
Hipoteca.__init__(self, préstamo, r, meses)
self.pts = pts
self.paid = [prestamo*(pts/100.0)]
self.leyenda = 'Fijo, ' + str(r*100) + '%, '\
+ str(pts) + 'puntos'
clase TwoRate(Hipoteca):
def __init__(self, préstamo, r, meses, teaserRate, teaserMonths):
Hipoteca.__init__(self, préstamo, teaserRate, meses)
self.teaserMonths = teaserMonths
self.teaserRate = teaserRate
self.nextRate = r/12.0
self.legend =
str(teaserRate*100)\
+ '% para ' + str(self.teaserMonths)\
+ ' meses, luego ' + str(r*100) + '%'
def hacerPago(auto):
if len(self.paid) == self.teaserMonths + 1:
self.rate = self.nextRate
auto.pago = findPayment(auto.deuda[-1], auto.tasa,
self.months - self.teaserMonths)
La figura 11.3 contiene funciones que se pueden usar para generar grá ficos
destinados a brindar informació n sobre los diferentes tipos de hipotecas.
La llamada
compareMortgages(amt=200000, years=30, fixedRate=0.07,
pts = 3,25, ptsRate=0,05,
tasavar1=0,045, tasavar2=0,095, mesesvar=48)
Capítulo 11. Trazado y más sobre las 1
produce parcelas que arrojan algo de luz sobre las hipotecas discutidas en el Capítulo 8.
La trama siguiente fue producida por invocaciones de plotTotPd. Arroja algo de luz
sobre el costo de cada tipo de hipoteca al trazar los costos acumulados en los que se
ha incurrido al comienzo de cada mes. Toda la trama está a la izquierda, y una
ampliació n de la parte izquierda de la trama está a la derecha.
Los siguientes dos grá ficos muestran la deuda restante (a la izquierda) y el costo
neto total de tener la hipoteca (a la derecha).
12 PROGRAMAS ESTOCÁSTICOS, PROBABILIDAD Y
ESTADÍSTICAS
Hay algo muy reconfortante en la mecá nica newtoniana. Empujas hacia abajo un
extremo de una palanca y el otro extremo sube. Lanzas una pelota al aire; recorre
una trayectoria parabó lica y desciende. 𝐹 = 𝑚𝑎. En resumen, todo sucede por una
razó n. El mundo físico es completamente predecible.
lugar: todos los estados futuros de un sistema físico pueden derivarse del
conocimiento sobre su estado actual.
Durante siglos, esta fue la sabiduría científica predominante; luego llegaron la
mecá nica cuá ntica y la Doctrina de Copenhague. Los defensores de la doctrina,
encabezados por Bohr y Heisenberg, argumentaron que, en su nivel má s
fundamental, no se puede predecir el comportamiento del mundo físico. Uno
puede hacer enunciados probabilísticos de la forma "es muy probable que
ocurra x", pero no enunciados de la forma "es seguro que ocurra x". Otros físicos
distinguidos, sobre todo Einstein y Schrö dinger, discreparon vehementemente.
Este libro trata sobre el uso de la computació n para resolver problemas. Hasta
ahora, hemos centrado nuestra atenció n en problemas que pueden resolverse
mediante un cá lculo determinista predecible. Dichos cá lculos son muy ú tiles,
pero claramente no son suficientes para abordar algunos tipos de problemas.
Muchos aspectos del mundo en
64Por supuesto, esto no impide que las personas crean que lo son y pierdan mucho dinero en
base a esa creencia.
Capítulo 12. Programas estocásticos, probabilidad y 1
que vivimos se puede modelar con precisió n só lo como procesos estocá sticos65.
Un proceso es estocá stico si su pró ximo estado depende tanto de los estados
previos como de algú n elemento aleatorio.
Esto sería problemá tico, ya que permite que la implementació n devuelva el mismo
nú mero cada vez que se llama, lo que haría que el juego fuera bastante aburrido.
Sería mejor especificar querodarDie“devuelve un int elegido aleatoriamente
entre 1 y 6.”
sesenta y cincoLa
palabra proviene de la palabra griega stokhastikos, que significa algo así
como "capaz de adivinar". Un programa estocá stico, como veremos, está orientado a
obtener un buen resultado, pero no se garantizan los resultados exactos.
66Una tirada es justa si cada uno de los seis resultados posibles es igualmente probable.
67De hecho, la funció n no es realmente aleatoria. Es lo que los matemá ticos llaman
pseudoaleatorio. Para casi todos los propó sitos prá cticos fuera de la criptografía, esta
distinció n no es relevante y la ignoraremos.
1 Capítulo 12. Programas estocásticos, probabilidad y
importar al azar
def rodarMorir():
"""Devuelve un int aleatorio entre 1 y 6"""
return random.choice([1,2,3,4,5,6])
def rollN(n):
resultado = ''
para i en el rango (n):
resultado = resultado +
str(rollDie()) imprimir resultado
Volvamos a nuestro dado de seis caras. ¿Cuá ntas secuencias diferentes hay de
longitud 10? 610. Entonces, la probabilidad de sacar diez unos consecutivos es
1/610. Menos de uno entre sesenta millones. Bastante baja, pero no má s baja que
la probabilidad de cualquier otra secuencia en particular, por ejemplo,
5442462412, de diez rollos.
Supongamos que queremos saber la probabilidad de tirar el dado diez veces sin
obtener un solo 1. Una forma de responder a esta pregunta es transformarla en la
pregunta de cuá ntas de las 610 secuencias posibles no contienen un 1.
Capítulo 12. Programas estocásticos, probabilidad y 1
Supongamos que Harvey Dent (también conocido como Two-Face) lanza una
moneda y sale cara. No se inferiría de esto que el pró ximo lanzamiento también
saldría cara. Supongamos que lo volteó dos veces y salió cara en ambas ocasiones.
Podría razonar que la probabilidad de que esto sucediera para una moneda justa (es
decir, una moneda en la que cara y cruz son igualmente probables) era de 0,25, por
lo que aú n no había razó n para suponer que el pró ximo lanzamiento sería cara.
Supongamos, sin embargo, que 100 de cada 100 lanzamientos salieron cara. 1/2100
es un nú mero bastante pequeñ o, por lo que puede sentirse seguro al inferir que la
moneda tiene cara en ambos lados.
La funció n flip de la figura 12.2 simula lanzar una moneda justa numFlips veces y
devuelve la fracció n de lanzamientos que dieron cara. Para cada lanzamiento,
random.random() devuelve un nú mero de punto flotante aleatorio entre 0,0 y 1,0.
Los nú meros menores o mayores que 0,5 se tratan como cara o cruz,
respectivamente. Al valor 0,5, se le asigna arbitrariamente el valor colas. Dada la
gran cantidad de valores de punto flotante entre 0,0 y 1,0, es muy poco probable
que esto afecte el resultado.
1 Capítulo 12. Programas estocásticos, probabilidad y
def
flip(numFlips
): cabezas =
0.0
para i en el rango (numFlips):
si random.random() <
0.5: cabezas += 1
devolver cabezas/numFlips
Intente ejecutar la funció n flipSim(100, 1) un par de veces. Esto es lo que vimos las dos
primeras veces que lo probamos:
>>> flipSim(100, 1)
0.44
>>> flipSim(100, 1)
0.57999999999999996
Parece que sería inapropiado suponer mucho (aparte de que la moneda tiene
cara y cruz) de cualquier prueba de 100 lanzamientos. Es por eso que
normalmente estructuramos nuestras simulaciones para incluir mú ltiples
ensayos y comparar los resultados. Intentemos flipSim(100, 100):
>>> flipSim(100, 100)
0.4993
>>> flipSim(100, 100)
0.4953
68Aunque la ley de los grandes nú meros había sido discutida en el 16elsiglo por Cardano, la
primera prueba fue publicada por Jacob Bernoulli a principios del 18 elsiglo. No tiene relació n
con el teorema sobre diná mica de fluidos llamado teorema de Bernoulli, que fue probado por
el sobrino de Jacob, Daniel.
Capítulo 12. Programas estocásticos, probabilidad y 1
Vale la pena señ alar que la ley de los grandes nú meros no implica, como muchos
parecen pensar, que si ocurren desviaciones del comportamiento esperado, es
probable que estas desviaciones sean compensadas por desviaciones opuestas en el
futuro. Esta mala aplicació n de la ley de los grandes nú meros se conoce como la
falacia del jugador. 69
La figura 12.3 contiene una funció n, flipPlot, que produce algunas grá ficas
destinadas a mostrar la ley de los grandes nú meros en acció n. La línea
random.seed(0) cerca de la parte inferior asegura que el generador de nú meros
pseudoaleatorios utilizado por random.random generará la misma secuencia de
nú meros pseudoaleatorios cada vez que se ejecute este có digo. Esto es
conveniente para la depuració n.
También podemos indicarle a PyLab que use una escala logarítmica en uno o ambos
de losXyyejes llamando a las funcionespylab.semilogxypylab.semilogía. Estas
funciones siempre se aplican a la figura actual.
Ambos grá ficos utilizan una escala logarítmica en el eje x. Dado que los valores
de x generados por flipPlot son 2minExp, 2minExp+1, .., 2maxExp, el uso de un
eje x logarítmico hace que los puntos esté n espaciados uniformemente a lo largo
del eje x, lo que proporciona la má xima separació n entre puntos. La siguiente
grá fica de la izquierda tambié n usa una escala logarítmica en el eje y. Los valores
de y en este grá fico oscilan entre casi 0 y casi 1000. Si el eje y tuviera una escala
lineal, sería difícil ver las diferencias relativamente pequeñ as en los valores de y
en el lado izquierdo del grá fico. Por otro lado, en el grá fico de la derecha, los
Estos grá ficos son má s fá ciles de interpretar que los grá ficos anteriores. El grá fico
de la derecha sugiere con bastante fuerza que la proporció n de caras y cruces
converge a 1,0 a medida que aumenta el nú mero de lanzamientos. El significado de
la trama de la izquierda es un poco menos claro. Parece que la diferencia absoluta
crece con el nú mero de lanzamientos, pero no es del todo convincente.
Nunca es posible lograr una precisió n perfecta a travé s del muestreo sin
muestrear a toda la població n. No importa cuá ntas muestras examinemos, nunca
podemos estar seguros de que el conjunto de muestras sea típico hasta que
1 Capítulo 12. Programas estocásticos, probabilidad y
examinemos cada elemento.
Capítulo 12. Programas estocásticos, probabilidad y 1
¿Cuá ntas muestras necesitamos mirar antes de que podamos tener una confianza
justificada en nuestra respuesta? Esto depende de la varianza en la distribució n
subyacente.
En té rminos generales, la varianza es una medida de cuá nta dispersió n hay en los
diferentes resultados posibles.
definido como 𝜎 X 1
=2 |K|
𝑥𝗀K(𝑥 − 𝜇)2, donde |X| es el tamaño de la colección y 𝜇
def runTrial(numFlips):
numHeads = 0
para n en el rango (numFlips):
si random.random() < 0.5:
numHeads += 1
numTails = numFlips - numHeads
return (numHeads, numTails)
Figura 12.6
title = 'Mean abs(#Heads Diferencias
- #Tails)' absolutas
+ numTrialsString
makePlot(xEje, diffsMeans, title,
'Número de lanzamientos', 'Abs medias (#caras -
#cruces)', 'bo', logX = Verdadero, logY = Verdadero)
title = 'SD abs(#Heads - #Tails)' + numTrialsString
makePlot(xEje, diffsSDs, title,
'Número de vueltas', 'Desviación estándar',
'bo', logX = Verdadero, logY = Verdadero)
1 Capítulo 12. Programas estocásticos, probabilidad y
Como era de esperar, la diferencia absoluta entre el nú mero de caras y cruces crece
con el nú mero de lanzamientos. Ademá s, dado que estamos promediando los
resultados de veinte ensayos, la grá fica es considerablemente má s fluida que
cuando trazamos los resultados de una sola prueba. Pero, ¿qué pasa con la ú ltima
trama? La desviació n está ndar crece con el nú mero de vueltas. ¿Significa esto que a
medida que aumenta el nú mero de lanzamientos deberíamos tener menos confianza
en la estimació n del valor esperado de la diferencia entre cara y cruz?
La figura 12.8 contiene una versió n de flipPlot1 que traza los coeficientes de variació n.
En este caso, vemos que la grá fica del coeficiente de variació n de la relació n
cara/cruz no es muy diferente de la grá fica de la desviació n está ndar. Esto no es
sorprendente, ya que la ú nica diferencia entre los dos es la divisió n por la media, y
dado que la media está cerca de 1, la diferencia es pequeñ a.
Por otro lado, la grá fica del coeficiente de variació n de la diferencia absoluta entre
cara y cruz es una historia diferente. Se necesitaría una persona valiente para
argumentar que está tendiendo en cualquier direcció n. Parece estar fluctuando
ampliamente. Esto sugiere que la dispersió n en los valores de abs (cara – cruz) es
independiente del nú mero de lanzamientos. No está creciendo, como la
desviació n está ndar podría habernos hecho creer, pero tampoco está
disminuyendo. Tal vez aparecería una tendencia si intentá ramos 1000 intentos en
lugar de 20. Veamos.
12.3 Distribuciones
Un histograma es una grá fica diseñ ada para mostrar la distribució n de valores en un
conjunto de datos. Primero se ordenan los valores y luego se dividen en un nú mero
fijo de contenedores de igual ancho. Luego se dibuja una grá fica que muestra el
nú mero de elementos en cada contenedor. Consideremos, por ejemplo, el có digo
vals = [1, 200] #garantiza que los valores oscilarán entre 1 y
200 para i en el rango (1000):
num1 = random.choice(rango(1, 100))
num2 = random.choice(rango(1, 100))
vals.append(num1+num2)
pylab.hist(valores, contenedores = 10)
A estas alturas debes estar terriblemente aburrido de lanzar monedas. Sin embargo,
vamos a pedirle que observe una simulació n má s de lanzamiento de monedas. La
simulació n en la Figura 12.9 ilustra má s de las capacidades de trazado de PyLab y
nos brinda la oportunidad de obtener una noció n visual de lo que significa la
desviació n está ndar.
def flip(numFlips):
cabezas = 0.0
para i en el rango (numFlips):
si random.random() < 0.5:
cabezas += 1
devolver cabezas/numFlips
aleatorio.seed(0)hacerPlots(100,100
0,100000)
Observe que, si bien las medias en ambas grá ficas son casi iguales, las desviaciones
está ndar son bastante diferentes. La distribució n de los resultados es mucho má s
estrecha cuando lanzamos la moneda 1000 veces por intento que cuando lanzamos
la moneda 100 veces por intento.
Para aclarar esto, hemos usado pylab.xlim para forzar los límites del eje x en la
segunda grá fica para que coincidan con los de la primera grá fica, en lugar de dejar
que PyLab elija los límites. También hemos usado pylab.xlim y pylab.ylim para elegir
un conjunto de coordenadas para mostrar un cuadro de texto con la media y la
desviació n está ndar.
1 (𝑥!𝜇)2
ƒ𝑥= ∗𝑒 ! 2𝜎2
𝜎 2𝜋
En lugar de estimar un pará metro desconocido por un solo valor (p. ej., la media de
un conjunto de ensayos), un intervalo de confianza proporciona un rango que
probablemente contenga el valor desconocido y un grado de confianza de que el
valor desconocido se encuentra dentro de ese rango. Por ejemplo, una encuesta
política podría indicar que es probable que un candidato obtenga el 52 % de los
votos ±4 % (es decir, el intervalo de confianza es de tamañ o 8) con un nivel de
1 Capítulo 12. Programas estocásticos, probabilidad y
confianza del 95 %. Lo que esto significa es que la encuestadora cree que el 95% de
las veces el candidato recibirá entre el 48% y el 56% de los votos. Juntos, el intervalo
de confianza y el nivel de confianza indican la confiabilidad del
Capítulo 12. Programas estocásticos, probabilidad y 1
Suele ser ú til visualizar los intervalos de confianza usando barras de error. El có digo
de la figura 12.10 llama a la versió n de flipSim de la figura 12.9 y luego usa
pylab.barra de error(xVals, significa, yerr = 2*pylab.array(sds))
71Estosvalores son aproximaciones. Por ejemplo, el 95% de los datos caerá n dentro de 1.96
desviaciones está ndar de la media; 2 desviaciones está ndar es una aproximació n conveniente.
1 Capítulo 12. Programas estocásticos, probabilidad y
Sin embargo, es importante recordar que no todas las distribuciones son normales.
random.seed(0)
probOfSuccess =
0.5
numTrials = 5000
Figura 12.12 Una distribución geométrica
Por ejemplo, esta ley predice que la probabilidad de que el primer dígito sea 1 es de
alrededor del 30 %. Sorprendentemente, muchos conjuntos de datos reales
parecen observar esta ley. Es posible demostrar que la sucesió n de Fibonacci, por
ejemplo, la satisface perfectamente. Eso es algo plausible, ya que la secuencia es
generada por una fó rmula. Es menos fá cil entender por qué conjuntos de datos tan
diversos como los có digos de acceso del iPhone, el nú mero de seguidores de
Twitter por usuario, la població n de los países o la distancia de las estrellas a la
Tierra se aproximan mucho a la ley de Benford.74
73La ley lleva el nombre del físico Frank Benford, quien publicó un artículo en 1938 que
mostraba que la ley se basaba en más de 20.000 observaciones extraídas de veinte dominios
diferentes. Sin embargo, fue postulado por primera vez en 1881 por el astró nomo Simon
Newcomb.
74http://testingbenfordslaw.com/
1 Capítulo 12. Programas estocásticos, probabilidad y
Casi todos los meses de octubre, dos equipos de las Grandes Ligas de Béisbol de
Estados Unidos se encuentran en algo llamado Serie Mundial. Juegan entre sí
repetidamente hasta que uno de los equipos ha ganado cuatro juegos, y ese equipo
se llama (no del todo apropiado) "el campeó n mundial".
Dejando de lado la cuestió n de si hay motivos para creer que uno de los
participantes en la Serie Mundial es de hecho el mejor equipo del mundo, ¿qué
probabilidades hay de que una contienda que puede durar como má ximo siete
juegos determine cuá l de los dos participantes es mejor?
Una hipó tesis nula. Esta hipó tesis describe el resultado que se obtendría si
los resultados se determinaran completamente por casualidad. En este
caso, la hipó tesis nula sería que los equipos tienen el mismo talento, por lo
que si los dos equipos jugaran un nú mero infinito de series de siete juegos,
cada uno ganaría la mitad de las veces.
Una observació n. Datos recopilados ya sea observando lo que sucede o
ejecutando una simulació n que uno cree que proporciona un modelo
preciso de lo que sucedería.
La figura 12.13 contiene có digo que puede proporcionarnos una idea de esa
pregunta. La funció n simSeries tiene un argumento, numSeries, un entero positivo
que describe el nú mero de series de siete juegos que se van a simular. Traza la
probabilidad de que el mejor equipo gane la serie contra la probabilidad de que ese
equipo gane un solo juego. Varía la probabilidad de que el mejor equipo gane un solo
juego de 0,5 a 1,0 y produce una grá fica.
Capítulo 12. Programas estocásticos, probabilidad y 1
def
simSeries(numSeries
): prob = 0.5
fracWon =
[] probs =
[]
while prob <=
1.0:
seriesWon =
0.0
for i in
range(numSeries): if
playSeries(7, prob):
seriesWon += 1
fracWon.append(seriesWon/numSeries)
probs.append(prob)
probabilidad += 0.01
pylab.plot(probs, fracWon, linewidth = 5)
pylab.xlabel('Probabilidad de ganar un juego')
Figura 12.13 Simulación de la Serie Mundial
Supongamos que asumimos que estos porcentajes ganadores son reflejos precisos
de las fortalezas relativas de estos dos equipos. ¿Cuá ntos juegos debe durar el
1 Capítulo 12. Programas estocásticos, probabilidad y
Serie Mundial para que obtengamos resultados que nos permitan rechazar la
hipó tesis nula, es decir, la hipó tesis de que los equipos está n parejos?
1. Asumir:
a. El rango de la funció n hash es1anorte,
b. El nú mero de inserciones esk, y
c. La funció n hash produce una distribució n perfectamente uniforme
de las claves utilizadas en las inserciones, es decir, para todas las
claves,llave, y para nú meros enteros,i, en el rango1anorte, la
probabilidad de queclave hash)esies1/n.
2. ¿Cuá l es la probabilidad de que ocurra al menos una colisió n?
Para obtener la probabilidad de tener al menos una colisió n, restamos este valor de
1, es decir, la probabilidad es
1 − (𝑛 − 1 ∗ 𝑛 − 2 ∗ …∗)𝑛 − 𝐾 − 1
𝑛𝑛𝑛
Dado el tamañ o de la tabla hash y el nú mero de inserciones esperadas, podemos
usar esta fó rmula para calcular la probabilidad de al menos una colisió n. Si K
fuera razonablemente grande, digamos 10 000, sería un poco tedioso calcular la
probabilidad con lá piz y papel. Eso deja dos opciones, matemá ticas y
programació n. Los matemá ticos han utilizado algunas té cnicas bastante
avanzadas para encontrar una manera de aproximar el valor de esta serie. Pero a
menos que K sea muy grande, es má s fá cil ejecutar algú n có digo para calcular el
valor exacto de la serie:
1 Capítulo 12. Programas estocásticos, probabilidad y
Los resultados
def de la simulació nnumInserciones,
findProb(númÍndices, son reconfortantemente similares a lo que derivamos
numPruebas):
analíticamente.
colisiones = 0.0
para t en el rango (número de ensayos):
¿Debería la alta probabilidad
colisiones de una colisió n hacernos pensar
+= simInsertions(numIndices, que las tablas hash
numInsertions)
return colisiones/numTrials
tienen que ser enormes para ser ú tiles? No. La probabilidad de que haya al menos
una colisió n nos dice poco sobre el tiempo de bú squeda esperado. El tiempo
esperado para buscar un valor depende de la longitud promedio de las listas que
implementan los cubos que contienen los valores que chocaron. Esto es simplemente
el nú mero de inserciones dividido por el nú mero de cubos.
13 PASEOS ALEATORIOS Y MÁS SOBRE DATOS
VISUALIZACIÓN
En 1827, el botá nico escocé s Robert Brown observó que las partículas de polen
suspendidas en el agua parecían flotar al azar. No tenía una explicació n plausible
para lo que llegó a conocerse como movimiento browniano y no intentó
modelarlo matemá ticamente.75 Un modelo matemá tico claro del fenó meno se
presentó por primera vez en 1900 en la tesis doctoral de Louis Bachelier, La
teoría de la especulació n. Sin embargo, dado que esta tesis abordaba el entonces
desacreditado problema de comprender los mercados financieros, fue ignorada
en gran medida por acadé micos respetables. Cinco añ os má s tarde, un joven
Albert Einstein trajo este tipo de pensamiento estocá stico al mundo de la física
con un modelo matemá tico casi igual al de Bachelier y una descripció n de có mo
podría usarse para confirmar la existencia de los á tomos.76 Por alguna razó n, la
gente parecía pensar que comprender la física era má s importante que ganar
dinero, y el mundo empezó a prestar atenció n. Los tiempos eran ciertamente
diferentes.
77Para ser honesto, la persona que se muestra aquí es un actor profesional que se hace pasar por un
granjero.
al tipo de cosas que aparecen en la situació n que estamos tratando de modelar. Tres
tipos obvios son Ubicació n, Campo y Borracho. A medida que observamos las clases
que proporcionan estos tipos, vale la pena pensar en lo que cada una podría
implicar sobre los tipos de modelos de simulació n que nos permitirá n construir.
Empecemos conUbicación.
Figura 13.1Ubicaciónclase
clase Ubicación (objeto):
Esta es una clase simple, pero incorpora dos decisiones importantes. Nos dice
def __init__(self, x, y):
que la simulació n involucrará
"""x e y son como má ximo dos dimensiones. Por ejemplo, la
simulació n no modelará losself.x
flotantes""" cambios= de altitud. Esto es consistente con las
x
imá genes de arriba. Ademá s, dado que los valores de deltaX y deltaY son
self.y = y
flotantes en lugar de nú meros enteros, no existe una suposició n incorporada en
esta clase sobre
def el conjunto
move(self, de direcciones
deltaX, deltaY): en las que podría moverse un
borracho. Esta es una generalizació
"""deltaX y deltaY n del modelo informal en el que cada paso
son
flotantes"""
tenía una longitud de uno y era paralelo al eje x o al eje y.
volver Ubicación(self.x + deltaX, self.y + deltaY)
Class Field también es bastante simple, pero también incorpora decisiones notables.
def getX(self):
Simplemente mantiene un mapeo de borrachos a ubicaciones. No impone
return self.x
restricciones en las ubicaciones, por lo que presumiblemente un campo tiene un
tamañ o ilimitado. Permite agregar mú ltiples borrachos a un campo en ubicaciones
def getY(self):
aleatorias. Noreturn self.y
dice nada sobre los patrones en los que se mueven los borrachos, ni
prohíbe que varios borrachos ocupen el mismo lugar o se muevan a travé s de
def distFrom(yo, otro):
espacios ocupados por otros borrachos.
ox = otro.x
oy = otro.y
xDist = self.x -
ox yDist = self.y
- oy
retorno (xDist**2 + yDist**2)**0.5
1 Capítulo 13. Paseos aleatorios y más sobre visualización de
clase Campo(objeto):
def
__init__(auto):
yo.borrachos = {}
Figura 13.2Campoclase
Las clases Drunk y UsualDrunk definen las formas en que un borracho puede
deambular por el campo. En particular, el valor de stepChoices en UsualDrunk
restaura la restricció n de que cada paso tiene una longitud de uno y es paralelo al eje
x o al eje y. Tambié n captura la suposició n de que cada tipo de paso es igualmente
probable y no está influenciado por los pasos anteriores. Un poco má s adelante
veremos las subclases de Drunk con diferentes tipos de comportamientos.
clase Borracho
Habitual(Borracho)
Capítulo 13. Paseos aleatorios y más sobre la visualización de 1
La funció n pruebaborracha tambié n tiene un pará metro, dClase, de tipo clase. Se usa
dos veces, una en la llamada a simWalks y otra en la primera declaració n de impresió n.
En la declaració n de impresió n, el nombre de atributo de clase incorporado se usa para
obtener una cadena con el nombre de la clase. La funció n pruebaEbrio calcula el
coeficiente de variació n de la distancia desde el origen usando la funció n CV definida
en la Figura 12.7.
Lo primero que debe hacer en este punto es ejecutar la simulació n en valores para
los que ya creemos que sabemos la respuesta, y asegurarse de que lo que produce la
simulació n coincida con el resultado esperado. Probemos caminatas de cero pasos
(para las cuales las distancias media, mínima y má xima desde el origen deben ser
todas 0) y un paso (para las cuales las distancias media, mínima y má xima desde el
origen deben ser todas 1).
¿Có mo diablos puede ser má s de 9 la distancia media de una caminata de cero pasos?
79en el 19elsiglo, se convirtió en una prá ctica está ndar para los plomeros probar los sistemas
cerrados de tuberías para detectar fugas llenando el sistema con humo. Má s tarde, los
ingenieros electró nicos adoptaron el término para cubrir la primera prueba de una pieza
electró nica: encender la energía y buscar humo. Aú n má s tarde, los desarrolladores de
software comenzaron a usar el té rmino para una prueba rá pida para ver si un programa hizo
algo ú til.
Capítulo 13. Paseos aleatorios y más sobre la visualización de 1
Ahora veamos un grá fico de las distancias medias desde el origen. Para dar una idea
de qué tan rá pido está creciendo la distancia, hemos colocado en la grá fica una línea
que muestra la raíz cuadrada del nú mero de pasos (y aumentamos el nú mero de
pasos a 1,000,000).81
80Dadoque la media era cero, el coeficiente de variació n no está definido. Por lo tanto, nuestra
implementació n de CV devolvió el valor especial de punto flotante "no es un nú mero".
81La grá fica que muestra la raíz cuadrada del nú mero de pasos versus la distancia desde el
origen es una línea recta porque usamos una escala logarítmica en ambos ejes.
1 Capítulo 13. Paseos aleatorios y más sobre visualización de
class
Figura 13.5 Subclases de la clase base Borracho
ColdDrunk(borrach
cuando o):
corrimos
def simAll((borracho
dar un habitual, borracho frío, borracho EWD),
(100, 1000), 10)imprimió
paso(self):
opciones de paso = [(0.0,1.0), (0.0,-2.0), (1.0, 0.0), (-1.0,
UsualDrunk caminata aleatoria de 100 pasos
0.0)]
Media =return
8,37073251526 CV = 0,482770539323
random.choice(opcionespaso)
Máx. = 14,7648230602 Mín. = 1,41421356237
Caminata aleatoria habitual borracha de
class
1000 pasos Media = 21.0385788624 CV =
EWDrunk(borracho)
0.5489414497
: def dar un
Máx. = 36,6878726557
paso(uno mismo): Mín. = 3,16227766017
ColdDrunk caminata
opciones de aleatoria de 100
paso = [(1.0, pasos
0.0), (-1.0, 0.0)]
Media = 23,9034750714 CV = 0,401318542296
Máx. = 37,1214223865 Mín. = 5,83095189485
ColdDrunk caminata aleatoria de 1000 pasos
Media = 238,833279891 CV = 0,125076661085
Máx. = 288,140590684 Mín. = 182,024723595
EWDrunk caminata aleatoria de
100 pasos Media = 8.6 CV =
0.58879018145
Máx. = 18,0 Mín. = 0,0
EWDrunk paseo aleatorio de 1000
pasos Media = 27,0 CV =
0,726719143346
Máx. = 74,0 Mín. = 2,0
Capítulo 13. Paseos aleatorios y más sobre la visualización de 1
Esto es un poco de salida para digerir. Parece que nuestro borracho que busca
calor se aleja del origen má s rá pido que los otros dos tipos de borracho. Sin
embargo, no es fá cil digerir toda la informació n en este resultado.
Una vez má s, es hora de alejarse de la salida textual y comenzar a usar grá ficos.
Lo primero que hace plotLocs es inicializar styleChoice con tres estilos diferentes de
marcadores. Luego usa pylab.plot para colocar un marcador en una ubicació n
correspondiente al final de cada
ensayo. La llamada a pylab.plot
establece el color y la forma del
marcador que se trazará usando los
valores devueltos por el iterador
styleIterator. La llamada
plotLocs ((borracho habitual,
borracho frío, borracho EWD),
100, 200)produce la trama de la
derecha. Lo primero que hay que decir
es que nuestros borrachos parecen
comportarse como se anuncia.
Elborrachotermina en el eje x,
elfrioborrachoparecen progresar
hacia el sur, y
elnormalborrachaparece haber
vagado sin rumbo fijo.
1 Capítulo 13. Paseos aleatorios y más sobre visualización de
Pero, ¿por qué parece haber muchos menos marcadores circulares que
triangulares o marcadores +? Porque muchas de las caminatas de EWDrunk
terminaron en el mismo lugar.
Esto no es sorprendente, dado el
pequeñ o nú mero de posibles
criterios de valoració n.
(200) para el EWDrunk. Ademá s, los
marcadores circulares parecen estar
espaciados de manera bastante
uniforme a lo largo del eje x, lo que es
consistente con el coeficiente de
variació n relativamente alto que
notamos anteriormente.
Ninguna de estas simulaciones es interesante por derecho propio. (En el pró ximo
capítulo, veremos má s simulaciones intrínsecamente interesantes). Pero hay
algunos puntos que vale la pena mencionar:
82Estetipo de agujero de gusano es un concepto hipoté tico inventado por físicos teó ricos.
Proporciona atajos a travé s del continuo tiempo/espacio.
1 Capítulo 13. Paseos aleatorios y más sobre visualización de
Claramente, cambiar las propiedades del campo ha tenido un efecto dramá tico. Sin
embargo, ese no es el punto de este ejemplo. Los puntos principales son:
Stanislaw Ulam y Nicholas Metropolis acuñ aron el término simulació n Monte Carlo
en 1949 en homenaje a los juegos de azar que se jugaban en el casino del Principado
de Mó naco. Ulam, mejor conocido por diseñ ar la bomba de hidró geno con Edward
Teller, describió la invenció n del modelo de la siguiente manera:
Los primeros pensamientos e intentos que hice para practicar [el Método
Monte Carlo] fueron sugeridos por una pregunta que se me ocurrió en 1946
mientras me recuperaba de una enfermedad y jugaba solitarios. La
pregunta era ¿cuáles son las posibilidades de que un solitario Canfield con
52 cartas salga con éxito? Después de pasar mucho tiempo tratando de
estimarlos mediante cálculos combinatorios puros, me preguntaba si un
método más práctico que el "pensamiento abstracto" no sería exponerlo,
digamos, cien veces y simplemente observar y contar el número de jugadas
exitosas. Esto ya era posible de prever con el comienzo de la nueva era de
las computadoras rápidas,83e inmediatamente pensé en problemas de
difusión de neutrones y otras cuestiones de física matemática, y más
generalmente en cómo cambiar procesos descritos por ciertas ecuaciones
diferenciales en una forma equivalente interpretable como una sucesión de
operaciones aleatorias. Más tarde
… [en 1946,] describí la idea a John von Neumann y comenzamos a
planificar los cálculos reales.84
La técnica se usó con eficacia durante el Proyecto Manhattan para predecir lo que
sucedería durante una reacció n de fisió n nuclear, pero realmente no despegó hasta
la dé cada de 1950, cuando las computadoras se volvieron má s comunes y má s
poderosas.
83"Rá pidoes un termino relativo. Ulam probablemente se estaba refiriendo al ENIAC, que
podría realizar alrededor de 103adiciones un segundo (y pesaba má s de 25 toneladas).
Las computadoras actuales realizan alrededor de 10 9adiciones un segundo (y pesan tal
vez 10-3montones).
84Eckhardt,Roger (1987). "Stan Ulam, John von Neumann y el mé todo Monte Carlo", Los
Alamos Science, edició n especial (15), 131-137.
1 Capítulo 14. Simulación Monte
def checkPascal(numTrials):
"""Asume numTrials an int > 0
Imprime
85Las excavaciones una estimación
arqueoló gicas sugierendeque
lalosprobabilidad de ganar"""
dados son el instrumento de juego má s
antiguo numWins
de la raza = 0.0 El dado de seis caras "moderno" má s antiguo que se conoce
humana.
for i indel 600 a. C., pero las tumbas egipcias que datan de dos milenios antes
data de alrededor
del nacimientorange(numTrials):
de Cristo contienen artefactos que se asemejan a dados. Por lo general,
estos primerosfor j in
dados range(24):
estaban hechos de huesos de animales; en los círculos de juego, la
d1frase
gente todavía usa la = "hacer rodar los huesos".
tirarDie()
86Al igual que con nuestros aná lisis anteriores, esto es cierto só lo bajo la suposició n de que
d2 =
cada dado es justo, es decir, el resultado de una tirada es verdaderamente aleatorio y cada
tirarDie()
uno de los seis resultados
si d1 == es 6igualmente
y d2 == probable.
6: Esto no siempre se debe dar por
sentado. Las excavaciones de Pompeya descubrieron dados "cargados" en los que se
habían insertado pequeñ os pesos de plomo para sesgar el resultado de una tirada. Má s
recientemente, el sitio de un vendedor en línea dijo: “¿Tienes una mala suerte inusual
cuando se trata de tirar los dados? Invertir en un par de dados que sean má s confiables
podría ser justo lo que necesita”.
Capítulo 14. Simulación Monte 1
La figura 14.2 contiene el corazó n de tal simulació n. Los valores de las variables de
instancia de una instancia de la clase CrapsGame registran el rendimiento de las
líneas de pase y no pase desde el inicio del juego. Los mé todos de observador
passResults y dpResults devuelven estos valores. El método playHand simula una
“mano”87 de un juego. La mayor parte del có digo en playHand es simplemente una
descripció n algorítmica de las reglas establecidas anteriormente. Observe que hay
un bucle en la clá usula else correspondiente a lo que sucede después de que se
establece un punto. Se sale usando una declaració n de ruptura cuando se lanza un
siete o el punto.
87Una mano comienza cuando el tirador está "saliendo", el té rmino que se usa en los dados
para referirse a una tirada antes de que se establezca un punto. Una mano termina cuando
el tirador ha ganado o perdido su apuesta inicial.
1 Capítulo 14. Simulación Monte
clase
CrapsGame(objeto):
def
__init__(self):
self.passWins, self.passLosses = (0,0)
self.dpWins, self.dpLosses, self.dpPushes =
(0,0,0)
def playHand(self):
tirar = tirarDie() +
tirarDie() si tirar == 7 o
tirar == 11:
self.passWins += 1
self.dppérdidas += 1
elif throw == 2 o throw == 3 o throw == 12:
self.passLosses += 1
if throw == 12:
self.dpEmpuja +=
1
demás:
self.dpGana += 1
demás:
punto =
tirar
mientras es
Verdadero:
tirar = tirarDie() +
tirarDie() if tirar ==
apuntar:
self.passWins += 1
self.dpLosses +=
1 descanso
elif throw == 7:
self.passLosses +=
1
Figura 14.2juego de dadosclase
La figura 14.3 contiene una funció n que utiliza la clase CrapsGame. Su estructura
es típica de muchos programas de simulació n:
1. Ejecuta varios juegos (piense en cada juego como una prueba en nuestras
simulaciones anteriores) y acumula los resultados. Cada juego incluye varias
manos, por lo que hay un bucle anidado.
2. Luego produce y almacena estadísticas para cada juego.
3. Finalmente, produce y genera estadísticas resumidas. En este caso,
imprime el retorno esperado de la inversió n (ROI) o cada tipo de línea de
apuestas y la desviació n está ndar de ese ROI.
El retorno de la inversió n se define mediante la ecuació n
𝑔𝑎i𝑛 ƒ𝑟𝑝
𝑅𝑂𝐼 =𝑜ƒ i𝑛𝑣𝑒𝑠𝑡𝑚𝑒𝑛𝑡
𝑐𝑜𝑠𝑡
Dado que las líneas de pase y de no pase pagan dinero parejo (si apuesta $1 y gana,
gana $1), el ROI es
��
𝑅𝑂𝐼 =
𝑛𝑢𝑚𝑏𝑒𝑟 𝑜ƒ 𝑏𝑒𝑡𝑠
Capítulo 14. Simulación Monte 1
Por ejemplo, si hizo 100 apuestas de línea de pase y ganó la mitad de ellas, su ROI
sería
50 − 50 = 0
100
Tenga en cuenta que en crapsSim usamos xrange en lugar de range en los bucles for en
previsió n de ejecutar simulaciones grandes. Recuerde que en Python 2.7 range(n) crea
una secuencia con n elementos, mientras que xrange(n) genera los valores solo cuando
los necesita el bucle for.
Parece que sería una buena idea evitar la línea de paso, donde el retorno esperado
de la inversió n es una pérdida del 7%. Pero la línea de no pase parece una muy
buena apuesta. ¿O sí?
Mirando las desviaciones está ndar, parece que quizá s la línea de no pase no sea
una buena apuesta despué s de todo. Recuerde que bajo el supuesto de que la
distribució n es normal, el intervalo de confianza del 95 % está comprendido por
dos desviaciones está ndar a cada lado de la media. Para la línea de no pase, el
intervalo de confianza del 95 % es [4,0 – 2*23,5372, 4,0 + 2*23,5372],
aproximadamente [-43 %, +51 %]. Eso ciertamente no sugiere que apostar a la
línea de no pase sea algo seguro.
Ahora podemos estar bastante seguros al asumir que ninguno de estos es una buena
apuesta. Parece que la línea de no pase puede ser un poco menos mala. Sin embargo,
debido a que el intervalo de confianza del 95 % [-1,486, -1,3572] para la línea de
aprobació n se superpone con el de la línea de no aprobació n [-1,4247, -1,2911], no
podemos decir con un 95 % de confianza que la línea de no aprobació n la línea es
una mejor apuesta.
Este cambio relativamente pequeñ o en el dado hace una diferencia dramá tica en las
probabilidades,
>>> dadosSim(1000000, 10)
Aprobado: ROI medio = 6,7066 % std. desarrollo = 0.0208%
No pase: ROI medio = -9,4824 % Desv estándar = 0,02 %
¡No es de extrañ ar que los casinos se tomen muchas molestias para asegurarse de
que los jugadores no introduzcan sus propios dados en el juego!
Capítulo 14. Simulación Monte 1
def playHand(self):
#Una implementación alternativa y más rápida de playHand
pointsDict = {4:1/3.0, 5:2/5.0, 6:5/11.0, 8:5/11.0,
9:2/5.0, 10:1/3.0}
tirar = tirarDie() +
tirarDie() si tirar == 7 o
tirar == 11:
self.passWins += 1
self.dppérdidas += 1
elif throw == 2 o throw == 3 o throw == 12:
self.passLosses += 1
if throw == 12:
self.dpEmpuja +=
1
demás:
self.dpGana += 1
demás:
if random.random() <= pointsDict[throw]: # punto antes de 7
self.passWins += 1
self.dppérdidas += 1
demás:# 7 antes del punto
14.4 Encontrar
Es fá cil ver có mo la simulació n de Monte Carlo es ú til para abordar problemas en
los que el no determinismo juega un papel. Curiosamente, sin embargo, la
simulació n de Monte Carlo (y los algoritmos aleatorios en general) se pueden
utilizar para resolver problemas que no son inherentemente estocá sticos, es
decir, para los que no hay incertidumbre sobre los resultados.
Considere π.
Durante miles de añ os, la gente ha sabido que existe una constante, llamada π (pi)
desde el siglo XVIII, tal que la circunferencia de un círculo es igual a π*diá metro y el
á rea del círculo es igual a π*radio2. Lo que no sabían era el valor de esta constante.
Enmide las primeras estimaciones, 4*(8/9)2 = 3,16, se puede encontrar en el papiro egipcio
Rhind, alrededor de 1650 a. Má s de mil añ os después, el Antiguo Testamento implicaba un
valor diferente para π al dar las especificaciones de uno de los proyectos de construcció n del
rey Salomó n,
medido desde el exterior de la pared y el diá metro desde el interior, o tal vez es solo
una licencia poética. Dejamos que el lector decida.
Arquímedes de Siracusa (287-212 a. C.) derivó los límites superior e inferior del
valor de π mediante el uso de un polígono de alto grado para aproximarse a una
forma circular.
Utilizando un polígono de 96 lados, concluyó que 223/71 < π < 22/7. Dar límites
superiores e inferiores era un enfoque bastante sofisticado para la é poca. Ademá s,
si tomamos su mejor estimació n como el promedio de sus dos límites, obtenemos
3,1418, un error de alrededor de 0,0002. ¡Nada mal!
Mucho antes de que se inventaran las computadoras, los matemá ticos franceses
Buffon (1707-1788) y Laplace (1749-1827) propusieron usar una simulació n
estocá stica para estimar el valor de π.90 Piensa en inscribir un círculo en un
cuadrado con lados de longitud 2, así que que el radio, r, del círculo es de
longitud 1.
Por la definició n de π, á rea = πr2. Como r es 1, π = á rea. Pero, ¿cuá l es el á rea del
círculo? Buffon sugirió que podía estimar el á rea de un círculo dejando caer una gran
cantidad de agujas (que, segú n é l, seguirían un camino aleatorio al caer) en las
cercanías del cuadrado. La relació n entre el nú mero de agujas con puntas dentro del
cuadrado y el nú mero de agujas con puntas dentro del círculo podría usarse para
estimar el á rea del círculo.
∗ 𝑛𝑒𝑒𝑑𝑙𝑒𝑠 i𝑛 𝑐i𝑟𝑐𝑙𝑒
𝑎𝑟𝑒𝑎 𝑜ƒ 𝑐i𝑟𝑐𝑙𝑒 = 𝑛𝑒𝑒𝑑𝑙𝑒𝑠 i𝑛 𝑠𝑞𝑢𝑎𝑟𝑒
4 ∗ 𝑛𝑒𝑒𝑑𝑙𝑒𝑠 i𝑛 𝑐i𝑟𝑐𝑙𝑒
𝑎𝑟𝑒𝑎 𝑜ƒ 𝑐i𝑟𝑐𝑙𝑒 = 𝑛𝑒𝑒𝑑𝑙𝑒𝑠 i𝑛 𝑠𝑞𝑢𝑎𝑟𝑒
1. Elija una regió n envolvente,mi, tal que el á rea demies fá cil de calcular yRse
encuentra completamente dentromi.
2. Elija un conjunto de puntos aleatorios que se encuentran dentro mi.
3. DejarFsea la fracció n de los puntos que caen dentro R.
4. Multiplica el á rea demiporF.
90Buffonpropuso primero la idea, pero había un error en su formulació n que luego fue
corregido por Laplace.
2 Capítulo 14. Simulación Monte
Si prueba el experimento de Buffon, pronto se dará cuenta de que los lugares donde
aterrizan las agujas no son realmente aleatorios. Ademá s, incluso si pudiera
soltarlas al azar, se necesitaría una gran cantidad de agujas para obtener una
aproximació n de π tan buena como la de la Biblia. Afortunadamente, las
computadoras pueden dejar caer agujas simuladas al azar a un ritmo feroz.
La funció n throwNeedles simula dejar caer una aguja usando primero random.random
para obtener un par de coordenadas cartesianas positivas (valores x e y). Luego usa el
teorema de Pitá goras para calcular la hipotenusa del triá ngulo rectá ngulo con base x y
altura y. Esta es la distancia de la punta de la aguja desde el origen (el centro del
cuadrado). Como el radio del círculo es 1, sabemos que la aguja se encuentra dentro
del círculo si y solo si la distancia desde el origen no es mayor que 1. Usamos este
hecho para contar el nú mero de agujas en el círculo.
La funció n estPi llama a getEst con un nú mero cada vez mayor de agujas hasta que
getEst devuelve una estimació n que, con una confianza del 95 %, está dentro de la
precisió n del valor real. Lo hace llamando a throwNeedles con un nú mero cada vez
mayor de agujas, hasta que la desviació n está ndar de los resultados de las pruebas
numTrials no sea mayor que precision/2.0. Bajo el supuesto de que los errores se
distribuyen normalmente, esto asegura que el 95% de los valores se encuentran dentro
de la precisió n de la media.
Capítulo 14. Simulación Monte 2
def tirarAgujas(númAgujas):
enCírculo = 0
para Agujas en xrange(1, numNeedles +
1): x = random.random()
y = aleatorio.aleatorio()
si (x*x + y*y)**0.5 <=
1.0: enCirculo += 1
#Contar agujas en un solo cuadrante, así que multiplique
por 4 y devuelva 4*(inCircle/float(numNeedles))
def getEst(númAgujas,
númPruebas): estimaciones =
[]
para t en el rango (número de ensayos):
piGuess =
throwNeedles(numNeedles)
estimaciones.append(piGuess)
sDev = stdDev(estimaciones)
curEst =
sum(estimaciones)/len(estimaciones) print
'Est. = ' + str(ronda(actual, 5)) +\
', Est. desarrollador = ' + str(ronda(sDev, 5))\
+ ', Agujas = ' + str(númAgujas)
return (curEst, sDev)
def estPi(precisión,
numTrials): numNeedles =
1000
sDev = precisión
Como era de esperar, las desviaciones está ndar disminuyeron monó tonamente a
medida que aumentamos el nú mero de muestras. Al principio, las estimaciones del
valor de π también mejoraron de manera constante. Algunos estaban por encima del
valor real y otros por debajo, pero cada aumento en numNeedles llevó a una
estimació n mejorada. Con 1000 muestras por prueba, la estimació n de la simulació n
ya era mejor que las de la Biblia y el Papiro Rhind.
noció n. No es suficiente producir una buena respuesta. Tenemos que tener una razó n
vá lida para estar seguros de que, de hecho, es una buena respuesta. Y cuando
dejamos caer un nú mero suficientemente grande de agujas, la pequeñ a desviació n
está ndar nos da razones para estar seguros de que tenemos una respuesta correcta.
¿Bien?
Veamos qué pasa si nos equivocamos en algo. Supongamos, por ejemplo, que
reemplazamos el 4 en la ú ltima línea de throwNeedles por un 2 y nuevamente
ejecutamos estPi (0.01, 100). Esta vez se imprime
Est. = 1,57422, estándar. desarrollador = 0.02394,
Agujas = 1000 Est. = 1,56959, estándar. desarrollador
= 0.01775, Agujas = 2000 Est. = 1,57054, estándar.
desarrollador = 0.01356, Agujas = 4000 Est. =
1,57072, estándar. desarrollador = 0.0084, Agujas =
8000 Est. = 1,57068, estándar. desarrollador =
0.00685, Agujas = 16000 Est. = 1,57066, estándar.
desarrollador = 0.00424, Agujas = 32000
La desviació n está ndar de apenas 32 000 agujas sugiere que deberíamos tener
bastante confianza en la estimació n. Pero, ¿qué significa esto realmente? Significa
que podemos estar razonablemente seguros de que si tuviéramos que extraer má s
muestras de la misma distribució n, obtendríamos un valor similar. No dice nada
acerca de si este valor está o no cerca del valor real de π. Una conclusió n
estadísticamente vá lida no debe confundirse con una conclusió n correcta.
A medida que avanzaba el siglo XX, las limitaciones de este enfoque se hicieron cada
vez má s claras. Las razones para esto incluyen:
En un modelo discreto, los valores de las variables pertinentes son enumerables, por
ejemplo, son nú meros enteros. En un modelo continuo, los valores de las variables
pertinentes oscilan entre conjuntos no enumerables, por ejemplo, los nú meros
reales. Imagine analizar el flujo de trá fico a lo largo de una carretera. Podríamos
optar por modelar cada automó vil individual, en cuyo caso tenemos un modelo
discreto. Alternativamente, podríamos optar por tratar el trá fico como un flujo,
donde los cambios en el flujo pueden describirse mediante ecuaciones diferenciales.
Esto conduce a un modelo continuo. En este ejemplo, el modelo discreto se parece
má s a la situació n física (nadie conduce medio automó vil, aunque algunos
automó viles tienen la mitad del tamañ o de otros), pero es má s complejo desde el
punto de vista computacional que uno continuo. En la prá ctica, los modelos suelen
tener componentes discretos y continuos. Por ejemplo,
15 ENTENDIENDO LOS DATOS EXPERIMENTALES
Este capítulo tiene que ver con la comprensió n de los datos experimentales.
Haremos un uso extensivo de la representació n grá fica para visualizar los datos
y volveremos al tema de qué es y qué no es una conclusió n estadística vá lida.
Tambié n hablaremos sobre la interacció n entre los experimentos físicos y
computacionales.
En 1676, el físico britá nico Robert Hooke formuló la ley de elasticidad de Hooke:
Ut tensio, sic vis, en inglé s, F = -kx. En otras palabras, la fuerza, F, almacenada en
un resorte está linealmente relacionada con la distancia, x, que el resorte ha sido
comprimido (o estirado). (El signo menos indica que la fuerza ejercida por el
resorte tiene la direcció n opuesta al desplazamiento). La ley de Hooke se cumple
para una amplia variedad de materiales y sistemas, incluidos muchos sistemas
bioló gicos. Por supuesto, no es vá lido para una fuerza arbitrariamente grande.
Todos los resortes tienen un límite elá stico, má s allá del cual falla la ley. Aquellos
de ustedes que han estirado demasiado un Slinky saben esto muy bien.
Comenzamos con un resorte sin peso y medimos la distancia hasta la parte inferior
del resorte desde la parte superior del soporte. Luego colgamos una masa conocida
en el resorte, esperamos a que deje de moverse y nuevamente medimos la
distancia desde la parte inferior del resorte hasta la parte superior del soporte. La
diferencia entre las dos distancias se convierte entonces en el valor de x en la ley de
Hooke.
Segú n este cá lculo, se necesitará n 98,1 Newtons92 de fuerza para estirar el resorte
un metro.
Podíamos estar seguros de que está bamos operando por debajo del
límite elá stico del resorte.
92El Newton, escrito N, es la unidad internacional está ndar para medir la fuerza. Es la cantidad
de fuerza necesaria para acelerar una masa de un kilogramo a razó n de un metro por segundo
por segundo. Un Slinky, por cierto, tiene una constante de resorte de aproximadamente 1N/m.
Capítulo 15. Comprensión de los datos 2
CuandoplotData('springData.txt')
se ejecuta, produce el grá fico de la
izquierda.
Aú n así, sería bueno ver una línea que represente nuestra mejor suposició n de
dó nde habrían estado los puntos si no hubiéramos tenido un error de medició n. La
forma habitual de hacer esto es ajustar una línea a los datos.
(𝑜𝑏𝑠𝑒𝑟𝑣𝑒𝑑 − 𝑝𝑟𝑒𝑑i𝑐𝑡𝑒𝑑 yo )2
i!0 yo
Elevar al cuadrado la diferencia entre los puntos observados y predichos hace que
las grandes diferencias entre los puntos observados y predichos sean
relativamente má s importantes que las pequeñ as diferencias. Al elevar al cuadrado
la diferencia, tambié n se descarta informació n sobre si la diferencia es positiva o
negativa.
¿Có mo podríamos encontrar el mejor ajuste por mínimos cuadrados? Una forma de
hacer esto sería usar un algoritmo de aproximació n sucesiva similar al algoritmo de
Newton-Raphson del capítulo 3. Como alternativa, existe una solució n analítica que
suele ser aplicable. Pero no tenemos que usar ninguno de los dos, porque PyLab
proporciona una funció n integrada, polyfit, que encuentra el mejor ajuste por
mínimos cuadrados.
Capítulo 15. Comprensión de los datos 2
La llamada
pylab.polyfit(valoresXobservados, valoresYobservados, n)
El algoritmo utilizado por polyfit se llama regresió n lineal. Esto puede parecer un
poco confuso, ya que podemos usarlo para ajustar curvas que no sean líneas. El
mé todo es lineal en el sentido de que el valor de la variable dependiente es una
funció n lineal de las variables independientes y los coeficientes encontrados por la
regresió n. Por ejemplo, cuando ajustamos una cuadrá tica, obtenemos un modelo de
la forma y = ax2 + bx + c. En tal modelo, el valor de la variable dependiente y es lineal
en las variables independientes x2, x1 y x0 y los coeficientes a, b y c. 93
93Una funció n es lineal si las variables aparecen solo en primer grado, se multiplican por
constantes y se combinan mediante sumas y restas.
2 Capítulo 15. Comprensión de los datos
La llamada
fitData('springData.txt')
produce el gráfico de la
derecha. Es interesante
observar que muy pocos
puntos se encuentran
realmente en el ajuste de
mínimos cuadrados. Esto es
plausible porque estamos
tratando de minimizar la
suma de los errores al
cuadrado, en lugar de
maximizar la cantidad de
puntos que se encuentran en
la línea. Aún así, no
parece un gran ajuste.
Probemos un ajuste cúbico
sumando a
datos de ajuste
En la literatura té cnica,
con frecuencia se ven
diagramas como este que
incluyen tanto datos sin
procesar como un ajuste
de curva a los datos. Con
demasiada frecuencia, sin
embargo, los autores
Capítulo 15. Comprensión de los datos 2
luego
2 Capítulo 15. Comprensión de los datos
Recuerde que comenzamos con una teoría de que debería haber una relació n lineal
entre los valores de x e y, no uno
cú bico. Veamos qué sucede si
usamos nuestro ajuste cú bico
para predecir dó nde estaría el
punto correspondiente a 1,5 kg.
El resultado se muestra en el
grá fico de la izquierda.
determinar qué línea se ajusta mejor a los datos, pero eso no viene al caso. Esta no es
una pregunta que pueda responderse con estadísticas. Después de todo, podríamos
descartar todos los datos excepto dos puntos cualesquiera y saber que polyfit
encontraría una línea que se ajustaría perfectamente a esos dos puntos. Nunca se
deben descartar resultados experimentales simplemente para obtener un mejor
ajuste.94 Aquí justificamos descartar los puntos má s a la derecha apelando a la
teoría subyacente a la ley de Hooke, es decir, que los resortes tienen un límite
elá stico. Esa justificació n no se podría haber utilizado adecuadamente para eliminar
puntos en otras partes de los datos.
La primera columna contiene las distancias del proyectil desde el objetivo. Las
otras columnas contienen la altura del proyectil a esa distancia para cada una de
las cuatro pruebas. Todas las medidas está n en pulgadas.
El có digo de la figura 15.4 se usó para trazar la altitud media del proyectil frente a
la distancia desde el punto de lanzamiento. También traza los mejores ajustes
lineales y cuadrá ticos a los puntos. (En caso de que haya olvidado el significado de
multiplicar una lista por un nú mero entero, la expresió n [0]*len(distancias)
produce una lista de len(distancias) 0).
def getTrajectoryData(fileName):
dataFile = open(fileName, 'r')
distancias = []
alturas1, alturas2, alturas3, alturas4 = [],[],[],[] descartar
encabezado = archivo de datos.readline()
para línea en archivo de datos:
d, h1, h2, h3, h4 = line.split()
distancias.append(float(d))
heights1.append(float(h1))
heights2.append(float(h2))
heights3.append(float(h3) )
alturas4.append(float(h4))
archivo de datos.cerrar()
volver (distancias, [alturas1, alturas2, alturas3, alturas4])
96No se deje engañ ar por este complot pensando que el proyectil tenía un á ngulo de ascenso
pronunciado. Solo se ve así debido a la diferencia de escala entre los ejes vertical y horizontal
en el grá fico.
2 Capítulo 15. Comprensión de los datos
𝑅2 = 1 − i(𝑦i − 𝑝i)2
i(𝑦i − 𝜇)2
Al comparar los errores de estimació n (el numerador) con la variabilidad de los
valores originales (el denominador), R2 pretende capturar la proporció n de
variabilidad en un conjunto de datos que se explica por el modelo estadístico
proporcionado por el ajuste. Cuando el modelo que se evalú a se produce mediante
una regresió n lineal, el valor de R2 siempre se encuentra entre 0 y 1. Si R2 = 1, el
modelo explica toda la variabilidad de los datos. Si R2 = 0, no hay relació n entre los
valores predichos por el modelo y los datos reales.
y
print 'RSquare of quadratic fit =', rSquared(meanHeights, altitudes)
En té rminos generales, esto nos dice que el modelo lineal puede explicar menos
del 2 % de la variació n en los datos medidos, pero el modelo cuadrá tico puede
explicar má s del 98 % de la variació n.
1. Sabemos que la trayectoria del proyectil está dada por una fó rmula de la
formay = hacha2+ bx + c,es decir, es una pará bola. Como toda pará bola es
simé trica alrededor de su vé rtice, sabemos que su pico se encuentra a
medio camino entre el punto de lanzamiento y el objetivo; llama a este
valorxMid. La altura del pico,𝑦𝑃𝑒𝑎𝑘, por lo tanto viene dada por𝑦𝑃𝑒𝑎𝑘 = 𝑎 ∗
𝑥𝑀i𝑑2 + 𝑏 ∗ 𝑥𝑀i𝑑 + 𝑐.
El ajuste es claramente bueno, para estos puntos de datos. Sin embargo, veamos
lo que predice el modelo para 220. Cuando agregamos el có digo
pred2to20 = a*(20**4) + b*(20**3) + c*(20**2)+ d*20 + e
print 'El modelo predice que 2**20 es aproximadamente',
round(pred2to20) print 'El valor real de 2**20 es', 2**20
Dios mío, a pesar de ajustarse a los datos, el modelo producido por polyfit
aparentemente no es bueno. ¿Es porque cuatro no era el grado correcto? No. Es
porque ningú n polinomio se ajusta bien a una distribució n exponencial. ¿Significa
esto que no podemos usar polyfit para construir un modelo de una distribució n
exponencial?
Afortunadamente, no es así, porque podemos usar polyfit para encontrar una curva
que se ajuste a los valores independientes originales y al logaritmo de los valores
dependientes.
Considere la secuencia[1, 2, 4, 8, 16, 32, 64, 128, 256, 512]. Si tomamos la base logarítmica2
de cada valor. obtenemos la secuencia [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], es decir, una
secuencia que crece linealmente. De hecho, si una funció n y = f(x), exhibe un
crecimiento exponencial, el logaritmo (en cualquier base) de f(x) crece linealmente.
Esto se puede visualizar trazando una funció n exponencial con un
eje y logarítmico. El có digo
xVals, yVals = [], []
para i en el rango
(10):
xVals.append(i)yVal
s.append(2**i)
pylab.plot(xVals, yVals)
pylab.semilogy()
importar matematicas
Este método de usar polyfit para encontrar un modelo para datos funciona cuando la
relació n se puede describir mediante una ecuació n de la forma y = baseax+b. Si se usa
con datos que no se pueden describir de esta manera, arrojará resultados erró neos.
Para ver esto, intentemos reemplazar el cuerpo de la funció n f por,
devuelve 3*(2**(1.2*x)) + x
Ahora imprime,
f(20) = 50331668.0
Predicho f (20) = 44846543.4909
“Si no puede probar lo que quiere probar, demuestre otra cosa y finja que
son lo mismo. En el aturdimiento que sigue a la colisión de las estadísticas
con la mente humana, casi nadie notará la diferencia”. 100
Desde entonces, la gente ha utilizado las estadísticas tanto para engañ ar como para
informar. Algunos han utilizado deliberadamente las estadísticas para engañ ar;
otros simplemente han sido incompetentes. En este capítulo analizamos algunas
formas en las que se puede engañ ar a la gente para que haga inferencias
inapropiadas a partir de datos estadísticos. Confiamos en que usará esta
informació n solo para el bien, es decir, para convertirse en un mejor consumidor y
un proveedor má s honesto de informació n estadística.
El mensaje aquí es simple. Si los datos de entrada tienen fallas graves, ninguna
cantidad de masaje estadístico producirá un resultado significativo.
El censo de Estados Unidos de 1840 mostró que la locura entre los negros y
mulatos libres era aproximadamente diez veces má s comú n que entre los
negros y mulatos esclavizados. La conclusió n era obvia. Como dijo el senador
estadounidense (y exvicepresidente y futuro secretario de Estado) John C.
Calhoun: “Los datos sobre locura revelados en este censo son intachables. De
ella nuestra nació n debe concluir que la abolició n de la esclavitud sería una
maldició n para los africanos”. No importa que pronto quedó claro que el
censo estaba plagado de errores. Como supuestamente explicó Calhoun a John
Quincy Adams, “hubo tantos errores que
Estos dos grá ficos muestran exactamente los mismos datos, pero transmiten
impresiones muy diferentes.
El primer grá fico fue diseñ ado para dar la impresió n de que los precios de la
vivienda se habían mantenido estables. En el eje Y, el diseñ ador usó una escala
logarítmica que va desde el absurdamente bajo precio promedio de una casa de
$10,000 hasta el increíblemente alto precio promedio de $1 milló n. Esto minimizó la
cantidad de espacio dedicado al á rea donde los precios está n cambiando, dando la
impresió n de que los cambios fueron relativamente pequeñ os. El grá fico de arriba y
de la derecha fue diseñ ado para dar la impresió n de que los precios de la vivienda se
movieron de manera errá tica y luego se desplomaron. El
101Debemos tener en cuenta que Calhoun estuvo en el cargo hace má s de 150 añ os. No hace
falta decir que ningú n político contemporá neo encontraría formas de abusar de las estadísticas
para respaldar una posició n.
2 Capítulo 16. Mentiras, malditas mentiras y
El diseñ ador usó una escala lineal y un rango estrecho de precios, por lo que se
exageraron los tamañ os de los cambios.
El có digo de la figura 16.1 produce los dos grá ficos que vimos anteriormente y un
grá fico destinado a dar una impresió n precisa del movimiento de los precios de la
vivienda.
La llamadaplotVivienda('justa')produce la trama
Cuando dos cosas está n correlacionadas,103 existe la tentació n de asumir que una
ha causado la otra. Considere la incidencia de la gripe en América del Norte. El
nú mero de casos sube y baja en un patró n predecible. Casi no hay casos en el
verano, la cantidad de casos comienza a aumentar a principios del otoñ o y luego
comienza a disminuir a medida que se acerca el verano. Ahora considere el nú mero
de niñ os que asisten a la escuela. Hay muy pocos niñ os en la escuela en el verano, la
inscripció n comienza a aumentar a principios del otoñ o y luego cae a medida que se
acerca el verano.
102Los estadísticos, como los abogados y los médicos, a veces usan el latín sin otra razó n
obvia que la de parecer eruditos. Esta frase significa, “con esto, por lo tanto a causa de
esto”.
103La correlació n es una medida del grado en que dos variables se mueven en la misma
direcció n. Si cuando x sube y sube, las variables está n correlacionadas positivamente. Si
se mueven en direcciones opuestas, se correlacionan negativamente. Si no hay relació n, la
correlació n es 0. Las alturas de las personas se correlacionan positivamente con las
alturas de sus padres. La correlació n entre las horas dedicadas a los videojuegos y el
promedio de calificaciones es negativa.
2 Capítulo 16. Mentiras, malditas mentiras y
El nuevo aná lisis de algunos de los estudios anteriores mostró que las mujeres
que se sometían a TRH probablemente pertenecían a grupos con regímenes de
dieta y ejercicio mejores que el promedio. Tal vez las mujeres que se sometieron
a la TRH eran, en promedio, má s conscientes de su salud que las otras mujeres
del estudio, por lo que tomar la TRH y mejorar la salud cardíaca fueron efectos
coincidentes de una causa comú n.
104Stephen R. Johnson, "El problema con QSAR (o có mo aprendí a dejar de preocuparme y aceptar
la falacia)", J. Chem. informació n Modelo., 2008.
105Nelson HD, Humphrey LL, Nygren P, Teutsch SM, Allan JD. Terapia de reemplazo hormonal
Capítulo 16. Mentiras, malditas mentiras y
posmenopá usica: revisió n científica. JAMA. 2002;288:872-881.
2
2 Capítulo 16. Mentiras, malditas mentiras y
X y X y X y X y
10.0 8.04 10.0 9.14 10.0 7.46 8.0 6.58
8.0 6.95 8.0 8.14 8.0 6.77 8.0 5.76
13.0 7.58 13.0 8.74 13.0 12.74 8.0 7.71
9.0 8.81 9.0 8.77 9.0 7.11 8.0 8.84
11.0 8.33 11.0 9.26 11.0 7.81 8.0 8.47
14.0 9.96 14.0 8.10 14.0 8.84 8.0 7.04
6.0 7.24 6.0 6.13 6.0 6.08 8.0 5.25
4.0 4.26 4.0 3.10 4.0 5.39 19.0 12.50
12.0 10.84 12.0 9.13 12.0 8.15 8.0 5.56
7.0 4.82 7.0 7.26 7.0 6.42 8.0 7.91
5.0 5.68 5.0 4.74 5.0 5.73 8.0 6.89
¿Significa esto que no hay una manera obvia de distinguir estos conjuntos de
datos entre sí? No, uno simplemente necesita graficar los datos para ver que los
conjuntos de datos no se parecen en nada.
¿Qué tiene de malo esto? No inspeccionaron los aviones que no regresaron de las
misiones porque habían sido derribados por fuego antiaé reo. Quizá s estos
aviones no examinados no pudieron regresar precisamente porque fueron
alcanzados en los lugares donde el fuego antiaé reo causaría el mayor dañ o. Este
error particular se llama sesgo de no respuesta. Es bastante comú n en las
encuestas. En muchas universidades, por ejemplo, se les pide a los estudiantes
durante una de las conferencias al final del trimestre que completen un
formulario que califica la calidad de las conferencias del profesor. Aunque los
resultados de tales encuestas a menudo son poco halagadores, podrían ser
peores. Aquellos estudiantes que piensan que las conferencias son tan malas que
no vale la pena asistirlas no está n incluidos en la encuesta.106
El sitio web del Family Research Institute contiene una tabla con la siguiente
informació n:
106Elcambio a las encuestas en línea, que permite que los estudiantes que no asisten a clase
participen en la encuesta, no es un buen augurio para los egos de los profesores.
107http://www.familyresearchinst.org/2012/01/how-long-do-homosexuals-live/
2 Capítulo 16. Mentiras, malditas mentiras y
Este mé todo produce una muestra que podría no ser representativa de la població n
homosexual o no homosexual (o de ambas) por un gran nú mero de razones. Por
ejemplo, parece inferir que alguien es gay o lesbiana si y solo si su obituario aparece
en un “diario homosexual”, y que alguien no es gay si su obituario aparece en un
“perió dico convencional”. Tambié n parece suponer que las muertes para las que
aparecen los obituarios son representativas de todas las muertes. ¿Có mo se hace
para evaluar una muestra así? Una té cnica es comparar los datos compilados de la
muestra con los datos compilados en otros lugares. Por ejemplo, uno podría
comparar la proporció n de hombres homosexuales a hombres heterosexuales en el
estudio de obituario con otros estudios que informan los tamañ os relativos de esas
dos poblaciones.
A los que se oponen a las iniciativas del gobierno para reducir la prevalencia de
las armas en los EE. UU. les gusta citar la estadística de que aproximadamente el
99,8 % de las armas de fuego en los EE. UU. no se utilizará n para cometer un
delito violento en un añ o determinado. ¿Significa esto que no hay mucha
violencia armada en los Estados Unidos? La Asociació n Nacional del Rifle informa
que hay aproximadamente 300 millones de armas de fuego de propiedad privada
en los EE. UU.: el 0,2% de 300 millones son 600.000.
estudiantes
Pero eso no es lo que hicieron. En cambio, miraron los datos y luego, imitando
al francotirador de Texas, dibujaron un círculo alrededor de junio. La pregunta
estadística correcta que se debe haber hecho es cuá l es la probabilidad de que
haya habido al menos un mes (de 12) en el que nacieran al menos 48 bebé s. El
programa de la figura 16.3 responde a esa pregunta.
Figura 16.3 Probabilidad de que nazcan 48 anoréxicas en algún mes
def
anyProb(numTrials
): anyMonth48 = 0
para prueba en rango
(numTrials): meses = [0] *
12
for i in range(446):
meses[random.randint(0,11)] +=
1
si max(meses) >=
48: anyMonth48
Capítulo 16. Mentiras, malditas mentiras y 2
La llamadacualquierProb(10000)impreso
Probabilidad de al menos 48 nacimientos en algún mes = 0,446
Parece que, despué s de todo, no es tan improbable que los resultados informados en
el estudio reflejen una ocurrencia fortuita en lugar de una asociació n real entre el
mes de nacimiento y la anorexia. Uno no tiene que venir de Texas para ser víctima de
la falacia del francotirador de Texas.
¿Qué pró ximos pasos podría haber tomado el grupo de Aberdeen para probar su
nueva hipó tesis? Una posibilidad es realizar un estudio prospectivo. En un estudio
prospectivo, uno comienza con un conjunto de hipó tesis y luego recopila datos con
el potencial de refutar o confirmar la hipó tesis. Si el grupo realizara un nuevo
estudio y obtuviera resultados similares, uno podría estar convencido.
Los porcentajes pueden ser particularmente engañ osos cuando se aplican a una
base pequeñ a. Es posible que lea acerca de un medicamento que tiene el efecto
secundario de aumentar la incidencia de alguna enfermedad en un 200 %. Pero si la
incidencia base de la enfermedad es muy baja, digamos uno en 1,000,000, bien
podría decidir que el riesgo de tomar el medicamento fue má s que contrarrestado
por los efectos positivos del medicamento.
Supongamos, por ejemplo, que un ladró n que tiene una mochila109 que puede
contener como má ximo 20 libras de botín irrumpe en una casa y encuentra los
artículos de la figura 17.1. Claramente, no podrá meterlo todo en su mochila, por
lo que debe decidir qué llevar y qué dejar atrá s.
109Para aquellos de ustedes que son demasiado jó venes para recordar, una "mochila" es
una bolsa simple que la gente solía llevar en la espalda, mucho antes de que las "mochilas"
se pusieran de moda. Si por casualidad has estado en la exploració n, quizá s recuerdes las
palabras del “Caminante feliz”, “Me encanta andar deambulando, A lo largo del sendero de
la montañ a, Y mientras avanzo, Me encanta cantar, Con la mochila a la espalda. ”
110Probablementehaya alguna lecció n moral profunda que se pueda extraer de este hecho, y
probablemente no sea "la codicia es buena".
2 Capítulo 17. Problemas de optimización de gráfico y
def pesoInverso(elemento):
return
1.0/elemento.obtenerPeso(
)
def buildItems():
nombres = ['reloj', 'pintura', 'radio', 'jarrón', 'libro',
'computadora'] valores = [175,90,20,50,10,200]
Capítulo 17. Problemas de optimización de gráfico y 2
artículo
def testGreedys(maxWeight =
20): items = buildItems()
imprimir 'Usar greedy por valor para llenar mochila de tamaño',
maxWeight testGreedy(items, maxWeight, value)
V[i]* I[i].valor
i 0
V[i]* I[i].pesoc w
i 0
La figura 17.4 contiene una implementació n directa de este enfoque de fuerza bruta
para resolver el problema de la mochila 0/1. Utiliza las clases y funciones definidas
en la Figura 17.2 y la Figura 17.3, y la funció n genPowerset definida en la Figura
9.5.
def testBest(maxWeight =
20): items =
buildItems()
pset = genPowerset(elementos)
Tenga en cuenta que esta solució n es mejor que cualquiera de las soluciones encontradas
por los algoritmos codiciosos. La esencia de un algoritmo codicioso es hacer lo mejor (segú n
lo definido por
2 Capítulo 17. Problemas de optimización de gráfico y
alguna métrica) elecció n local en cada paso. Hace una elecció n que es
localmente ó ptima. Sin embargo, como ilustra este ejemplo, una serie de
decisiones localmente ó ptimas no siempre conduce a una solució n
globalmente ó ptima.
Los grá ficos se utilizan normalmente para representar situaciones en las que
existen relaciones interesantes entre las partes. El primer uso documentado de
grá ficos en matemá ticas fue en 1735 cuando el matemá tico suizo Leonhard
Euler utilizó lo que se conoce como teoría de grá ficos para formular y resolver el
problema de los puentes de Kö nigsberg.
La gran idea de Euler fue que el problema podría simplificarse enormemente al ver
cada masa de tierra separada como un punto (piense en un "nodo") y cada puente
como una línea (piense en un "borde") que conecta dos de estos puntos. El mapa de
la ciudad podría entonces ser representado por el grá fico a la derecha del mapa.
Euler luego razonó que si un camino atravesara cada borde exactamente una vez,
debería ser el caso de que cada nodo en el medio del camino (es decir, cualquier
nodo excepto el primero y el ú ltimo nodo visitado) debe tener un nú mero par de
bordes para que está conectado. Dado que ninguno de los nodos de este grá fico
tiene un nú mero par de aristas, Euler concluyó que es imposible atravesar cada
Por ejemplo, solo se necesita una pequeñ a extensió n del tipo de grá fico utilizado por
Euler para modelar el sistema de carreteras de un país. Si se asocia un peso con cada
borde de un grá fico (o dígrafo), se denomina grá fico ponderado. Usando grá ficos
ponderados, el sistema de carreteras se puede representar como un grá fico en el
que las ciudades está n representadas por nodos y las carreteras que las conectan
como bordes, donde cada borde está etiquetado con la distancia entre los dos nodos.
Má s generalmente, uno
2 Capítulo 17. Problemas de optimización de gráfico y
puede representar cualquier hoja de ruta (incluidas las que tienen calles de un
solo sentido) mediante un dígrafo ponderado.
También hay muchos usos menos obvios de los grá ficos. Los bió logos usan grá ficos
para modelar cosas que van desde la forma en que las proteínas interactú an entre sí
hasta las redes de expresió n génica. Los físicos usan grá ficos para describir las
transiciones de fase. Los epidemió logos usan grá ficos para modelar trayectorias de
enfermedades. Etcétera.
Tener una clase para los nodos puede parecer una exageració n. Después de todo,
ninguno de los métodos de la clase Node realiza ningú n cá lculo interesante.
Introdujimos la clase simplemente para darnos la flexibilidad de decidir, quizá s en
algú n momento posterior, introducir una subclase de Nodo con propiedades
adicionales.
Nodo de clase (objeto):Figura 17.5 Nodos y aristas
def __init__(self, nombre):
"""Asume que el nombre es
una cadena""" self.name =
nombre
def getName(self):
return
self.nombre
def __str__(self):
return
self.nombre
Otra representació n comú n es una lista de adyacencia, que usamos aquí. Class
Digraph tiene dos variables de instancia. Los nodos variables son una lista de
Python que contiene los nombres de los nodos en el Digraph. La conectividad de los
nodos se representa mediante una lista de adyacencia implementada como un
diccionario. Los bordes variables son un diccionario que mapea cada Nodo en el
Digraph a una lista de los hijos de ese Nodo.
Class Graph es una subclase de Digraph. Hereda todos los métodos de
Digraph excepto addEdge, que anula. (Esta no es la forma más
eficiente en cuanto a espacio para implementar Graph, ya que almacena
cada borde dos veces, una para cada dirección en Digraph. Pero tiene
la virtud de la simplicidad).
clase Digraph(objeto):
#nodes es una lista de los nodos en el gráfico
#edges es un dictado que asigna cada nodo a una lista de sus
hijos def __init__(self):
self.nodos = []
self.bordes =
{}
def addNode(self, node):
if node in self.nodes:
aumentar ValueError('Nodo
duplicado') de lo contrario:
self.nodes.append(nodo)
self.edges[nodo] = []
def addEdge(self, edge):
src = edge.getSource()
destino = borde.obtenerDestino()
si no (src en self.nodes y dest en self.nodes):
aumentar ValueError('Node not in graph')
self.edges[src].append(destino
) def childrenOf(self, nodo):
return
self.bordes[nodo] def
hasNode(self, nodo):
devolver nodo en
self.nodes def __str__(self):
resultado = ''
para src en self.nodes:
para destino en self.edges[src]:
resultado = resultado + src.getName() + '->'\
+ dest.getName() + '\n'
devuelve resultado[:-1] #omitir nueva
línea final
Es posible que desee detenerse un minuto y pensar por qué Graph es una subclase
de Digraph, y no al revé s. En muchos de los ejemplos de subclases que hemos visto,
la subclase agrega atributos a la superclase. Por ejemplo, la clase WeightedEdge
agregó un atributo de peso a la clase Edge.
Ruta ponderada más corta. Esto es como el camino má s corto, excepto que
en lugar de elegir la secuencia de aristas má s corta que conecta dos nodos,
definimos alguna funció n sobre los pesos de las aristas en la secuencia (por
ejemplo, su suma) y minimizamos ese valor. Este es el tipo de problema que
solucionan Mapquest y Google Maps cuando se les pide que calculen las
direcciones de conducció n entre dos puntos.
camarillas. Encuentre un conjunto de nodos tal que haya una ruta (o, a
menudo, una ruta que no exceda una longitud má xima) en el grá fico entre
cada par de nodos en el conjunto.115
corte mínimo. Dados dos conjuntos de nodos en un grá fico, un corte es
un conjunto de bordes cuya eliminació n elimina todos los caminos de
cada nodo en un conjunto a cada nodo en el otro. El corte mínimo es el
conjunto má s pequeñ o de bordes cuya eliminació n logra esto.
“cercano” o “casual”.
¿Es posible que todos los casos provinieran de un solo paciente “índice”?
Má s formalmente, ¿hay un nodo,norte, tal que hay un camino desdenortea
todos los demá s nodos del grá fico con una etiqueta de TB activa?117La
respuesta es “casi”. Hay una ruta desde el nodo en el medio del grá fico
hasta cada nodo de TB activo, excepto los nodos en el círculo negro de la
derecha. Curiosamente, la investigació n posterior reveló que la persona
en el centro del círculo negro había sido previamente un vecino del
supuesto paciente índice y, por lo tanto, debería haber habido un borde
de contacto casual que vinculara a los dos.
Una pregunta menos hipoté tica es la distancia usando la relació n de “amigo” entre
pares de personas en Facebook. Por ejemplo, puede preguntarse si tiene un amigo
que tiene un amigo que tiene un amigo que es amigo de Mick Jagger. Pensemos en
diseñ ar un programa para responder a tales preguntas.
def imprimirRuta(ruta):
"""Asume que la ruta es una lista
de nodos""" resultado = ''
para i en el rango (len (ruta)):
resultado = resultado +
str(ruta[i]) si i !=
len(ruta) - 1:
resultado =
resultado + '->' devolver
resultado
Por supuesto, hay otras formas de recorrer un grá fico ademá s de la profundidad.
Otro enfoque comú n es la bú squeda primero en amplitud (BFS). En un recorrido
primero en anchura, primero se visitan todos los hijos del nodo de inicio. Si
ninguno de ellos es el nodo final, se visitan todos los hijos de cada uno de esos
nodos. Etcé tera. A diferencia de la bú squeda primero en profundidad, que
generalmente se implementa de forma recursiva, la bú squeda primero en
amplitud generalmente se implementa iterativamente. BFS explora muchas rutas
simultá neamente, agregando un nodo a cada ruta en cada iteració n. Dado que
genera los caminos en orden ascendente de longitud, se garantiza que el primer
camino encontrado con el objetivo como su ú ltimo nodo tenga un nú mero
mínimo de aristas.
La figura 17.10 contiene có digo que usa una bú squeda primero en anchura para
encontrar la ruta má s corta en un grá fico dirigido. La variable pathQueue se utiliza
para almacenar todas las rutas que se está n explorando actualmente. Cada iteració n
comienza eliminando una ruta de pathQueue y asignando esa ruta a tmpPath. Si el
ú ltimo nodo en tmpPath es end, se devuelve tmpPath. De lo contrario, se crea un
conjunto de rutas nuevas, cada una de las cuales extiende tmpPath agregando uno de
sus elementos secundarios. Luego, cada una de estas nuevas rutas se agrega a
pathQueue.
2 Capítulo 17. Problemas de optimización de gráfico y
Como se mencionó anteriormente, BFS es una forma conveniente de buscar una ruta
con la menor cantidad de bordes porque la primera vez que se encuentra una ruta,
se garantiza que será tal ruta.
Intentemos averiguar por qué esta implementació n lleva tanto tiempo. Dada la
pequeñ a cantidad de có digo en el cuerpo de fib, está claro que el problema debe
ser la cantidad de veces que fib se llama a sí mismo. Como ejemplo, observe el
á rbol de llamadas asociado con la invocació n fib(6).
cib(6)
cib(5) cib(4)
cib(2)cib(1)cib(1)cib(0)cib(1)cib(0)cib(1)cib(0)
cib(1)cib(0)
Observe que estamos calculando los mismos valores una y otra vez. Por ejemplo,
fib se llama con 3 tres veces, y cada una de estas llamadas provoca cuatro
llamadas de fib adicionales. No hace falta ser un genio para pensar que sería una
buena idea registrar el valor devuelto por la primera llamada y luego buscarlo en
lugar de calcularlo cada vez que sea necesario. Esto se llama memorizació n y es
la idea clave detrá s de la programació n diná mica.
Si intenta ejecutar fastFib, verá que es bastante rá pido: fib(120) regresa casi
instantá neamente. ¿Cuá l es la complejidad de fastFib? Llama a fib exactamente
una vez para cada valor de 0 a n. Por lo tanto, bajo el supuesto de que la
bú squeda en el diccionario se puede realizar en tiempo constante, la complejidad
temporal de fastFib(n) es O(n).120
a 6 3
b 7 3
C 8 2
d 9 5
121Puede parecer extrañ o poner la raíz de un á rbol en la parte superior, pero esa es la
forma en que los matemá ticos y los informá ticos suelen dibujarlos. Tal vez sea evidencia
de que esas personas no dedican suficiente tiempo a contemplar la naturaleza.
122Los á rboles de decisió n, que no necesitan ser binarios, proporcionan una forma
estructurada de explorar las consecuencias de tomar una serie de decisiones secuenciales.
Se utilizan ampliamente en muchos campos.
2 Capítulo 18. Programación
La raíz del á rbol (nodo 0) tiene una etiqueta <{}, [a,b,c,d], 0, 5>, que indica que no se
han tomado elementos, quedan todos los elementos por considerar, el valor de la
elementos tomados es 0, y todavía hay disponible un peso de 5. El nodo 1 indica que
se ha tomado a, [b, c, d] quedan por considerar, el valor de los artículos tomados es 6
y la mochila puede contener otras 2 libras. No hay ningú n nodo a la izquierda del
nodo 1, ya que el artículo b, que pesa 3 libras, no cabría en la mochila.
En la figura 18.5, los nú meros que preceden a los dos puntos en cada nodo indican
un orden en el que se pueden generar los nodos. Este orden particular se llama
primero a la izquierda primero en profundidad. En cada nodo intentamos generar
un nodo izquierdo. Si eso es imposible, intentamos generar un nodo derecho. Si eso
tambié n es imposible, hacemos una copia de seguridad de un nodo (al padre) y
repetimos el proceso. Eventualmente, nos encontramos generando todos los
descendientes de la raíz, y el proceso se detiene. Cuando el proceso se detiene, se ha
generado cada combinació n de elementos que podrían caber en la mochila, y
cualquier nodo hoja con el mayor valor representa una solució n ó ptima. Observe
que para cada nodo hoja,
def pruebapequeña():
nombres = ['a', 'b', 'c',
'd'] val = [6, 7, 8, 9]
pesos = [3, 3, 2, 5]
Elementos = []
for i in range(len(vals)):
Items.append(Item(names[i], vals[i], weights[i]))
val, tomado =
maxVal(Artículos, 5) para el
artículo tomado:
elemento de impresión
print 'Valor total de los artículos tomados =', val
Cuando se ejecuta smallTest (que utiliza los valores de la Figura 18.4), imprime un
resultado que indica que el nodo 8 de la Figura 18.5 es una solució n ó ptima:
<c, 8.0, 3.0>
<b, 7.0, 2.0>
Valor total de los artículos tomados = 15.0
Si ejecuta este có digo en cualquiera de los ejemplos que hemos visto, encontrará
que produce una respuesta ó ptima. De hecho, siempre producirá una respuesta
ó ptima, si llega a producir alguna respuesta.
Piense en qué problema se está resolviendo en cada nodo. El problema que se está
resolviendo es encontrar los elementos ó ptimos para tomar de los que quedan por
considerar, dado el peso disponible restante. El peso disponible depende del peso
total de los artículos tomados, pero no de los artículos tomados o del valor total de
los artículos tomados. Entonces, por ejemplo, en la Figura 18.5, los nodos 2 y 7 en
realidad está n resolviendo el mismo problema: decidir qué elementos de [c,d]
deben tomarse, dado que el peso disponible es 2.
Capítulo 18. Programación 2
8 6 337
dieciséis 9 1,493
32 12 3,650
64 19 8,707
128 27 18.306
256 40 36,675
Este algoritmo cae en una clase de complejidad llamada pseudo polinomio. Una
explicació n cuidadosa de este concepto está má s allá del alcance de este libro. En
té rminos generales, fastMaxVal es exponencial en la cantidad de bits necesarios
para representar los posibles valores de disponibilidad.
Para ver qué sucede cuando los pesos se eligen de un espacio enorme, podemos
elegir los pesos posibles de los reales positivos en lugar de los enteros positivos.
Para hacer esto, reemplace la línea,
items.append(Item(str(i),
random.randint(1, maxVal),
random.randint(1, maxWeight)))
enconstruirMuchosItemspor la línea
items.append(Item(str(i),
random.randint(1, maxVal),
random.randint(1, maxWeight)*random.random()))
El aprendizaje automá tico es difícil de definir. Una de las primeras definiciones fue
propuesta por el ingeniero elé ctrico e informá tico estadounidense Arthur
Samuel,126 quien la definió como un “campo de estudio que otorga a las
computadoras la capacidad de aprender sin ser programadas explícitamente”. Por
supuesto, en cierto sentido, todo programa ú til aprende algo. Por ejemplo, una
implementació n del método de Newton aprende las raíces de un polinomio.
Cuando los informá ticos hablan de aprendizaje automá tico, con mayor frecuencia se
refieren al campo de la escritura de programas que aprenden automá ticamente a
hacer inferencias ú tiles a partir de patrones implícitos en los datos. Por ejemplo, la
regresió n lineal (consulte el Capítulo 15) aprende una curva que es un modelo de
una colecció n de ejemplos. Ese modelo se puede usar para hacer predicciones sobre
ejemplos nunca antes vistos.
126Samuel es probablemente mejor conocido como el autor del programa que jugaba a
las damas. El programa, en el que comenzó a trabajar en la dé cada de 1950 y continuó
trabajando en la dé cada de 1970, fue impresionante para su é poca, aunque no
particularmente bueno para los está ndares modernos. Sin embargo, mientras trabajaba
en é l, Samuel inventó varias té cnicas que todavía se utilizan en la actualidad. Entre otras
Capítulo 18. Programación 2
cosas, el programa de Samuel para jugar a las damas fue muy posiblemente el primer
programa jamá s escrito que mejoró en base a la "experiencia".
Capítulo 19. Una mirada rápida al 2
Suponga, por ejemplo, que le dieron los siguientes dos conjuntos de personas:
A: {Abraham Lincoln, George Washington, Charles de Gaulle}
B: {Benjamin Harrison, James Madison, Louis Napoleón}
Con base en esta informació n incompleta sobre estas figuras histó ricas, podría
inferir que el proceso que asignó estos ejemplos al conjunto etiquetado como A o al
conjunto etiquetado como B implicó separar a los presidentes altos de los má s
bajos.
Hay una gran cantidad de enfoques diferentes para el aprendizaje automá tico, pero
todos intentan aprender un modelo que es una generalizació n de los ejemplos
proporcionados. Todos tienen tres componentes:
127Gran parte de la literatura sobre aprendizaje automá tico utiliza la palabra "clase" en
lugar de "etiqueta". Dado que usamos la palabra "clase" para otra cosa en este libro, nos
2 limitaremos a usar "etiqueta" para este concepto.
Capítulo 19. Una mirada rápida al
Capítulo 19. Una mirada rápida al 2
128A menos que tu cena sea extremadamente aburrida. En cuyo caso, la conversació n de su
2 Capítulo 19. Una mirada rápida al
cita para cenar se convierte en el ruido y la conversació n en la mesa de al lado en la señ al.
Capítulo 19. Una mirada rápida al 2
Ahora, supongamos que se nos pide que decidamos si una boa constrictor es un
reptil. Podríamos responder "no", porque una boa constrictor no es venenosa ni
pone huevos. Pero esta sería la respuesta incorrecta. Por supuesto, no es
sorprendente que intentar generalizar a partir de dos ejemplos pueda llevarnos
por mal camino. Una vez que incluyamos la boa constrictor en nuestros datos de
entrenamiento, podríamos formular la nueva regla de que un animal es un reptil
si tiene escamas, es de sangre fría y no tiene patas. Al hacerlo, estamos
descartando las características de puesta de huevos y venenosas como
irrelevantes para el problema de clasificació n.
Si hubié semos incluido el hecho de que los huevos de los reptiles tienen
amnios,129 podríamos idear una regla que separe a los reptiles de los peces.
Desafortunadamente, en la mayoría de las aplicaciones prá cticas de aprendizaje
automá tico no es posible construir vectores de características que permitan
una discriminació n perfecta.
¿Significa esto que debemos rendirnos porque todas las funciones disponibles son
mero ruido? No. En este caso los rasgos escamados y la sangre fría son condiciones
necesarias para ser reptil, pero no condiciones suficientes. La regla tiene escamas y
es de sangre fría, no dará falsos negativos, es decir, cualquier animal clasificado
como no reptil no será reptil. Sin embargo, arrojará algunos falsos positivos, es
decir, algunos de los animales clasificados como reptiles no será n reptiles.
El primer paso para hacer este tipo de comparació n es convertir las características
de cada animal en una secuencia de nú meros. Si decimos Verdadero = 1 y Falso = 0,
obtenemos los siguientes vectores de características:
Serpiente de cascabel: [1,1,1,1,0]
Boa constrictora: [0,1,0,1,0]
Rana dardo: [1,0,1,0,4]
El pará metro p define los tipos de caminos que se pueden seguir al recorrer la
distancia entre los vectores 𝑉1 y 𝑉2. Esto se puede visualizar
fá cilmente en su mayoría si los vectores tienen una longitud
de dos y representan coordenadas cartesianas. Considere la
imagen de la izquierda. ¿El círculo en la esquina inferior
izquierda está má s cerca de la cruz o de la estrella? Eso
depende. Si podemos viajar en línea recta, la cruz está má s
cerca. El Teorema de Pitá goras nos dice que la cruz es la raíz
cuadrada de 8 unidades del círculo, unas 2,8 unidades,
mientras que podemos
129Los amnios son capas externas protectoras que permiten que los huevos se pongan en la
tierra en lugar del agua.
130Estapregunta no es tan tonta como parece. Un naturalista y un toxicó logo (o alguien que
busca mejorar la efectividad de un dardo de soplado) podría dar respuestas diferentes a esta
pregunta.
2 Capítulo 19. Una mirada rápida al
vea fá cilmente que la estrella está a 3 unidades del círculo. Estas distancias se
denominan distancias euclidianas y corresponden al uso de la distancia de
Minkowski con p = 2. Pero imagine que las líneas en la imagen corresponden a
calles y que uno tiene que permanecer en las calles para ir de un lugar a otro. En
ese caso, la estrella permanece a 3 unidades del círculo, pero la cruz ahora está a
4 unidades. Estas distancias se denominan distancias de Manhattan,131 y
corresponden al uso de la distancia de Minkowski con p = 1.
El có digo utiliza una funció n de trazado de PyLab que no hemos utilizado anteriormente:
table.
La funció n de tabla produce una grá fica que (¡sorpresa!) parece una tabla. Los
argumentos de palabra clave rowLabels y colLabels se utilizan para proporcionar
las etiquetas (en este ejemplo, los nombres de los animales) para las filas y
columnas. El argumento de palabra clave cellText se utiliza para proporcionar los
valores que aparecen en las celdas de la tabla. En el ejemplo, cellText está vinculado
a tableVals, que es una lista de listas de cadenas. Cada elemento de tableVals es una
lista de los valores de las celdas en una fila de la tabla. El argumento de palabra
clave loc se usa para especificar en qué lugar de cada celda debe aparecer el texto, y
el argumento de palabra clave loc se usa para especificar en qué parte de la figura
debe aparecer la tabla. El ú ltimo pará metro de palabra clave utilizado en el ejemplo
es colWidths. Está vinculado a una lista de flotantes que dan el ancho (en pulgadas)
de cada columna de la tabla. El có digo table.scale(1, 2.
2 Capítulo 19. Una mirada rápida al
Si ejecutamos el có digo
serpiente de cascabel = Animal('serpiente de
cascabel', [1,1,1,1,0]) boa = Animal('boa\
nconstrictor', [0,1,0,1,0]) ranadardo =
Animal('rana dardo' , [1,0,1,0,4]) animales =
[serpiente de cascabel, boa, rana dardo]
compareAnimals(animales, 3)
produce la mesa
19.3 Agrupación
Agrupaciónse puede definir como el proceso de organizar objetos en grupos
cuyos miembros son similares de alguna manera. Una cuestió n clave es definir el
significado de “similar”.
Una buena medida de qué tan cerca está n los ejemplos dentro de un solo grupo, c,
entre sí es la varianza. Para calcular la varianza de los ejemplos dentro de un
conglomerado,
2 Capítulo 19. Una mirada rápida al
2
𝑣𝑎𝑟$𝑎𝑛𝑐𝑒 𝑐 = 𝑑i𝑠𝑡𝑎𝑛𝑐𝑒(𝑚𝑒𝑎𝑛 𝑐 , 𝑒)2
𝑒∈𝑐
𝑑i𝑠𝑠i𝑚i𝑙𝑎𝑟i𝑡𝑦 𝐶 = 𝑣𝑎𝑟$𝑎𝑛𝑐𝑒(𝑐)
𝑐∈𝐶
Observe que dado que no dividimos la varianza por el tamañ o del conglomerado, un
conglomerado incoherente grande aumenta el valor de la disimilitud (C) má s que un
conglomerado incoherente pequeñ o.
def getFeatures(self):
return
self.features[:]
def getLabel(self):
return
self.label
def getName(self):
return
self.nombre
2 Capítulo 19. Una mirada rápida al
def getCentroid(self):
return
self.centroid
def computarCentroide(auto):
dim =
self.ejemplos[0].dimensionalidad()
totVals = pylab.array([0.0]*dim)
para e en self.examples:
totVals +=
e.getFeatures()
centroide = self.ejemploTipo('centroide',
totVals/float(len(self.examples)))
centroide de retorno
def
varianza(auto
): totDist =
0.0
para e en self.ejemplos:
totDist += (e.distance(self.centroid))**2
return totDist**0.5
def
__str__(self
Figura 19.6 ClaseGrupo
Capítulo 19. Una mirada rápida al 2
La figura 19.7 contiene una traducció n directa del pseudocó digo que describe k-
means a Python. Utiliza random.sample(examples, k) para obtener los centroides
iniciales. Esta invocació n devuelve una lista de k elementos distintos elegidos
aleatoriamente de la lista de ejemplos.
La figura 19.8 contiene una funció n, trykmeans, que llama a kmeans varias veces y
selecciona el resultado con la disimilitud má s baja.
Capítulo 19. Una mirada rápida al 2
def disimilitud(clusters):
totDist = 0.0
para c en grupos:
totDist +=
c.varianza() return
totDist
def plotSamples(muestras,
marcador): xVals, yVals =
[], []
para s en muestras:
x = s.getFeatures()
[0] y =
s.getFeatures()[1]
pylab.annotate(s.getName(), xy = (x, y),
xytext = (x+0.13, y-
0.07), tamaño de fuente
= 'x-grande')
xVals.append(
x)
yVals.append(
y)
pylab.plot(xVals, yVals, marcador)
e impreso
Iteración 1
El clúster con centroide [ 4.57800047 5.35921276] contiene:
1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 2.0, 2.1, 2.2, 2.3,
2.4, 2.5, 2.6, 2.7, 2.8, 2.9
El clúster con centroide [ 3.79646584 2.99635148]
contiene: 1.9
Iteración 2
El clúster con centroide [4.80105783 5.73986393] contiene:
1.1, 1.2, 1.4, 1.5, 1.6, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7,
2.8, 2.9
El clúster con centroide [ 3.75252146 3.74468698]
contiene: 1.0, 1.3, 1.7, 1.8, 1.9
Iteración 3
Clúster con centroide [5.63888356.02296994] contiene:
1.6, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9
El clúster con centroide [ 3.19452848 4.28541384]
contiene: 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.7, 1.8, 1.9
Iteración 4
El clúster con centroide [ 5.93613865 5.96069975]
contiene: 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8,
2.9
El clúster con centroide [ 3.14170883 4.52143963]
contiene: 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8,
1.9
Iteración 5
El clúster con centroide [ 5.93613865 5.96069975]
contiene: 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8,
2.9
El clúster con centroide [ 3.14170883 4.52143963]
contiene: 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8,
1.9
Resultado final
El clúster con centroide [ 5.93613865 5.96069975]
contiene: 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8,
2.9
El clúster con centroide [ 3.14170883 4.52143963]
contiene: 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8,
1.9
Resultado final
El clúster con centroide [ 6.07470389 5.67876712]
contiene: 1.8, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7,
2.8, 2.9
El clúster con centroide [ 3.00314359 4.80337227]
contiene: 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.9
La tabla de la derecha
muestra el contenido de un #Nombre
archivo que enumera #incisivos
algunas especies de superiores
#caninos
mamíferos, sus fó rmulas
superiores
dentales (los primeros 8 #premolares
nú meros), su peso adulto superiores
promedio en libras,135 y #molares
superiores
un có digo que indica su #incisivos
dieta preferida. Los inferiores
comentarios en la parte #caninos
inferiores
superior describen los
#premolares
elementos asociados con inferiores
cada mamífero, por #molares
ejemplo, el primer inferiores
#peso
elemento que sigue al
#Etiqueta: 0=herbívoro, 1=carnívoro,
nombre es el nú mero de 2=omnívoro Tejón,3,1,3,1,3,1,3,2,10,1
incisivos superiores. Oso,3,1,4,2,3,1,4,3,278,2
Castor,1,0,2,3,1,0,1,3,20,0
La figura 19.12 contiene Murciélago marrón,2,1,1,3,3,1,2,3,0.5,1
una funció n, Gato,3,1,3,1,3,1,2,1,4,1
Puma,3,1,3,1,3,1,2,1,63,1
readMammalData, para leer
Vaca,0,0,3,3,3,1,2,1,400,0
un archivo formateado de Ciervo,0,0,3,3,4,0,3,3,200,0
esta manera y procesar el Perro,3,1,4,2,3,1,4,3,20,1
contenido del archivo para Zorro,3,1,4,2,3,1,4,3,5,1
Lobo marino,3,1,4,1,2,1,4,1,200,1
producir un conjunto de Foca gris,3,1,3,2,2,1,3,2,268,1
ejemplos que representen Conejillo de indias,1,0,1,3,1,0,1,3,1,0
la informació n del archivo. Alce,0,1,3,3,3,1,3,3,500,0
Primero procesa la Humano,2,1,2,3,2,1,2,3,150,2
Jaguar,3,1,3,1,3,1,2,1,81,1
informació n del encabezado Canguro,3,1,2,4,1,0,2,4,55,0
al comienzo del archivo León,3,1,3,1,3,1,2,1,175,1
para obtener un recuento Visón,3,1,3,1,3,1,3,2,1,1
Mol,3,1,4,3,3,1,4,3,0,75,1
de la cantidad de
Alce,0,0,3,3,4,0,3,3,900,0
características que se Ratón,1,0,0,3,1,0,0,3,0.3,2
asociará n con cada ejemplo. Puercoespín,1,0,1,3,1,0,1,3,3,0
Luego usa las líneas Cerdo,3,1,4,3,3,1,4,3,50,2
Conejo,2,0,3,3,1,0,2,3,1,0
correspondientes a cada
especie para construir tres
listas:
especiesNombreses
una lista de los
nombres de los
mamíferos.
135Incluimos la informació n sobre el peso porque al autor le han dicho en má s de una ocasió n
que existe una relació n entre su peso y sus há bitos alimenticios.
Capítulo 19. Una mirada rápida al 2
La ú ltima parte de readMammalData usa los valores en featureVals para crear una lista
de vectores de características, uno para cada mamífero. (El có digo podría simplificarse
al no construir valores de características y, en cambio, construir directamente los
vectores de características para cada mamífero. Elegimos no hacer eso en previsió n de
una mejora para readMammalData que haremos má s adelante en esta secció n).
def readMammalData(fName):
dataFile = open(fName, 'r')
numFeatures = 0
#Líneas de proceso en la parte superior del archivo
para línea en archivo de datos: #Buscar número de características
if line[0:6] == '#Label': #indica el final de la
interrupción de funciones
if linea[0:5] !=
'#Nombre':
numFeatures += 1
valores de característica = []
La funció n testTeeth en la figura 19.13 usa trykmeans para agrupar los ejemplos
construidos por la otra funció n, buildMammalExamples, en la figura 19.13. Luego
informa la cantidad de herbívoros, carnívoros y omnívoros en cada grupo.
2 Capítulo 19. Una mirada rápida al
Hasta aquí nuestra conjetura de que el agrupamiento estaría relacionado con los
há bitos alimenticios de las diversas especies. Una inspecció n superficial sugiere que
tenemos un agrupamiento totalmente dominado por los pesos de los animales. El
problema es que el rango de pesos es mucho mayor que el rango de cualquiera de las
otras características. Por lo tanto, cuando se calcula la distancia euclidiana entre
ejemplos, la ú nica característica que realmente importa es el peso.
El có digo genera dos distribuciones normales con diferentes medias (100 y 50) y
diferentes desviaciones está ndar (5 y 10). Luego escala cada uno e imprime las
medias y las desviaciones está ndar de los resultados. Cuando se ejecuta, imprime
v1 media = -0,0 v1 desviación estándar
1,0 v2 media = 0,0 v1 desviación estándar
1,0136
Es fá cil ver por qué la declaració n result = result - mean asegura que la media de la
matriz devuelta siempre estará cerca de 0137. Que la desviació n está ndar siempre sea
1 no es obvio. Se puede demostrar mediante una larga y tediosa cadena de
manipulaciones algebraicas, con las que no te aburriremos.
136Una distribució n normal con una media de 0 y una desviació n está ndar de 1 se llama
distribución normal estándar.
137Decimos “cerrar”, porque los nú meros de punto flotante son solo una aproximació n a los
reales y el resultado no siempre será exactamente 0.
2 Capítulo 19. Una mirada rápida al
se imprime
Clúster sin escalar
Tejón, Oso, Puma, Perro, Zorro, Lobo marino, Foca gris, Humano,
Jaguar, León, Visón, Topo, Cerdo, Mapache, Murciélago rojo, León
marino, Mofeta, Lobo
0 herbívoros, 13 carnívoros, 5 omnívoros
Capítulo 19. Una mirada rápida al 2
19.8 Terminando
En este capítulo, apenas hemos arañ ado la superficie del aprendizaje automá tico.
Hemos tratado de darle una idea del tipo de pensamiento involucrado en el uso del
aprendizaje automá tico, con la esperanza de que encuentre formas de abordar el
tema por su cuenta.
Lo mismo podría decirse de muchos de los otros temas presentados en este libro.
Hemos cubierto mucho má s terreno de lo que es típico en los cursos introductorios
de ciencias de la computació n. Probablemente encontraste algunos temas menos
interesantes que otros. Pero esperamos que haya encontrado al menos algunos
temas sobre los que desea aprender má s.
138La posició n de los ojos puede ser una característica ú til, ya que tanto los omnívoros como
los carnívoros suelen tener los ojos en la parte delantera de la cabeza, mientras que los ojos
de los herbívoros suelen estar má s hacia los lados. Entre los mamíferos, solo las madres de los
humanos tienen ojos en la parte posterior de la cabeza.
2 Capítulo 19. Una mirada rápida al
x == ydevolucionesVerdaderosiXyyson iguales.
x != ydevolucionesVerdaderosiXyyno son iguales
<, >, <=, >=tienen sus significados habituales.
a y BesVerdaderosi ambosaybsonVerdadero, yFALSOde lo contrario.
a o BesVerdaderosi al menos uno deaobesVerdadero, yFALSOde lo contrario.
No unesVerdaderosiaesFALSO, yFALSOsiaesVerdadero.
Operaciones comunes en tipos de secuencia
secuencia[i]devuelve la ielelemento en la
secuencia.len(siguiente)devuelve la longitud
de la secuencia.sec1 + sec2concatena las dos
secuencias.
n * seqdevuelve una secuencia que se repite seq n veces.
seq[inicio:fin]devuelve un segmento de la secuencia.
e en secuenciaprueba simiestá contenido en la secuencia.
e no en secuenciaprueba simino está contenido en la secuencia.
para e en seqitera sobre los elementos de la secuencia.
Métodos de cadena comunes
Tipo de
Tipo Tipo de elemento Ejemplos de literales Mudable
índice
problema de detenció n, 3
aldea, 77
simulació n de mano, 19
hash, 69, 137–40
colisió n, 137, 138
cubos de hachís, 138
funció n hash, 137
tablas hash, 137
probabilidad de
colisiones, 177
Índ 2
editar menú , 13
conocimiento, declarativo versus
menú archivo, 13
imperativo, 1 Knuth, Donald, 117
si declaració n, 15
Problema de los puentes de Kö nigsberg, 241
tipo inmutable, 58
declaració n de importació n, 52
etiqueta argumento de
en operador, 66 sangría
palabra clave, 146
de có digo, 15 eventos
abstracció n lambda, 36
independientes, 154
Lampson, mayordomo, 128
indexació n para tipos de
Regazocordó n,PAGierre-‐Simon,201
secuencia, 17 indirecció n, 127
ley de los grandes numeros,
inducció n, 132
156, 157 hoja, de arbol, 254
definició n inductiva, 45
ajuste de mínimos
estadística inferencial, 155
cuadrados, 210, 212 len
ocultació n de informació n, 105, 106
funció n incorporada, 17
entrada, 18
longitud, para tipos de
aporteperoilt-‐in
secuencia, 17 Leonardo de
funció n,18raw_input frente a, 18
Pisa, 46
instancia, de una clase, 93
alcance léxico, 38
entorno de desarrollo integrado
biblioteca, Python está ndar,
(IDE), 13
consulte también mó dulos de
interfaz, 91
biblioteca está ndar, 53
intérprete, 3, 7
regresió n lineal, 211, 262
Introducción a los Algoritmos,
Liskov, Bá rbara, lista
125 es instanciaperoilt-‐in
103peroilt-‐in
funció n,101 iteració n, 18
funció n,63 lista de
para bucle, 23
comprensió n, 63
sobre enteros, 23
tipo de lista, 58–62
sobre listas, 61 + (concatenación)
operador, 62 clonación,
Java, 91 63
Julieta, 12 comprensió n, 63
Julio César, 50 copiar, 63
indexació n, 126
Kennedy, José, 81 representació n interna, 127
clave, en una parcela. Ver trazado literales, 4, 288
en PyLab, funció n de leyenda ó ptimo local, 240
argumento de palabra clave, 36 variable local, 38
palabras clave, 12 funció n logarítmica,
k--medioClustre,274–86 220 logaritmo, base
problema de la mochila, 234–40 de, 118 eje
0/1, 238 logarítmico, 124
bruto--fuerzaentonceslució n,238 escala logarítmica, 159
solució n de programació n diná mica, bucle, 18
254– 61 bucle invariante, 131
fraccionario (o continuo), 240 operador lt, 133
Knight Capital Group, 78 variable al acecho, 225
có digo má quina,
7 aprendizaje
automático
supervisado, 263
2 Índ
sin supervisió n, 264
distancia manhattan, 267
Proyecto Manhattan, 193
Índ 2
palíndromo, 48
coeficiente, 32
má quina paralela de acceso
grado, 32
aleatorio, 114 nodo principal, 240
ajuste polinomial, 211
Pascual, Blaise, 194
método pop, 62
línea de pase, 195
hacer estallar una
declaració n de aprobació n, 101
pila, 39
caminos a través de la
formato de grá ficos de red portá tiles,
especificació n, 72 Peters, Tim,
142 power set, 122, 238
136
no determinismo predictivo, 152
pi (π), estimació n por simulació n, 200–
declaració n de impresió n, 18
204 Pingala, 47
probabilidades, 154
Pirandello, 43
programa, 8
trazado en PyLab, 141–46, 166–68, 190
lenguaje de programació n, 3, 7
anotar, 276
compilado, 7
grá fico de barras, 224
hde alto nivel, 7
cifra actual, 143
interpretado, 7
configuració n predeterminada, 146
nivel bajo, 7
funció n figura, 141
semá ntica, 5
cadena de formato, 144
semá ntica está tica, 4
histograma, 166
sintaxis, 4
argumentos de palabra clave,
aviso, concha, 10
145 argumento de palabra
experimento prospectivo, 221
clave de etiqueta, 146
estudio prospectivo, 232
etiquetas para diagramas,
PyLab, véase también
146
trazado, 141
funció n de leyenda, 146
una funció n de rango, 218
marcadores, 189
matriz, 148
funció n grá fica, 141
polifit, 211
configuració n rc, 145
guía del usuario, 141
funció n savefig, 143
teorema de Pitá goras, 180, 202
funció n semilogx, 159
Pitó n, 7, 35
funció n de semilogía, 159
Python 3, versus 2.7, 8, 9, 18, 24
mostrar funció n, 142
Declaració n de Python, 8
estilo, 187
mesas, 268
mecá nica cuá ntica, 152
funció n de título, 144
ventanas, 141
conejos, 46
funció n xlabel, 144
elevar declaració n, 87
garrapatas, 224
má quina de acceso aleatorio,
funció n ylabel, 144
114 mó dulo aleatorio, 153,
garrapatas, 224
172
extensió n de archivo png,
elecció n, 153
142 puntos de ejecució n,
gauss, 170
36 puntos, en tipografía,
al azar, 153
145 puntero, 127
muestra, 274
polifit, 210
semilla, 157
ajuste de una funció n
uniforme, 170
polimó rfica, 218
paseo aleatorio, 179–92
exponencial, 86
parcial, 186
polinomio, 32
Índ 2