[MÚSICA DE FONDO]
PONENTE: OK.
Entonces, ¿qué es la programación dinámica?
Por lo general, me gustaría pedirles que aportaran ideas
para que pudiéramos discutirlas,
pero en este caso en particular, el punto que quiero resaltar
es que, ya sea que sepan la respuesta, o que estén equivocados,
la programación dinámica se diseñó para ser compatible con las palabras de moda.
Es algo que inventó Richard Bellman
y se le ocurrió este nombre.
Y es en realidad, de acuerdo con su autobiografía,
el problema era que Rand era un contratista de defensa,
entonces él trabajaba en Rand, trabajaba con algoritmos de computadora,
y Rand trabajó para la Fuerza Aérea, aún todavía trabaja para ella,
era parte del gran complejo industrial militar
y eso significaba que su presupuesto dependía del Secretario de Defensa.
Y si al Secretario de Defensa no le gustaba lo que estaba haciendo,
o al Congreso no le gustaba lo que estaba haciendo, y se enteraban,
sería el final todo.
Aparentemente, según Bellman, el Secretario de Defensa de ese tiempo
sentía una aversión patológica por la investigación.
La forma en que lo describió es que se enfurecía y se ponía rojo
si mencionaban la palabra investigación en su presencia.
Y realmente no le gustaba si decían algo como matemáticas.
Entonces Bellman quería asegurarse de que si lo que estaba haciendo aparecía
en un informe del Congreso o si aterrizaba un informe en el Departamento
de Defensa, o en el escritorio del Secretario de Defensa,
no habría algo objetable.
Se le ocurrió el término programación, que en el mundo de las matemáticas,
significa optimización, encontrar la mejor respuesta a un problema.
Y el término dinámico, y la idea de dinámico era este
adjetivo que de alguna manera expresa que las cosas están cambiando,
pero sobre todo, que no tiene connotaciones negativas.
La dinámica simplemente suena bien.
Así que de ahí viene el nombre.
Entonces, la programación dinámica básicamente es un nombre
que nadie podría objetar, y por lo tanto
nadie cancelaría el proyecto.
Hay un término que es mucho más descriptivo de lo que está pasando,
que es una tabla de búsqueda.
Entonces, la programación dinámica es, a fin de cuentas,
una forma de ver ciertos tipos de problemas
con estructuras de datos vinculados, listas vinculadas,
tries, tablas hash, todo eso que cae en la categoría de una estructura de datos
y que utilizamos cuando tenemos datos y queremos
mapear las conexiones entre ellos.
Y entonces hay toda una categoría de estructuras de datos diferentes
que implican el uso de punteros para mapear las conexiones entre las cosas.
La programación dinámica es una categoría de algoritmos
donde decimos, en mi problema, cuando quiero resolverlo,
termino haciendo la misma pregunta una y otra vez como parte de la solución.
La misma pregunta con los mismos parámetros, si quieren verlo así.
Y entonces, en lugar de hacer todo el mismo trabajo una y otra vez,
me permite recordar la respuesta después de resolverlo una vez,
y la siguiente vez que haga la misma pregunta,
Voy a ir a buscar cuál fue la respuesta.
Esa es la idea de la programación dinámica.
y la de una tabla de búsqueda.
Ya hemos usado antes tablas de búsqueda.
Por ejemplo, si volvemos al comienzo de la clase,
Aquí hay un bucle for.
for(int i = 0, i < strlen(str); ...
i++).
este
Es un bucle, el bucle que hay a través de cada carácter
de esta cuerda, str, uno por uno.
Pero el problema es que, cada vez que pasamos por el bucle,
se recalcula la longitud de esta cadena.
Y calcular la longitud de la cadena en C
toma algo de tiempo porque se tiene que caminar por toda la cadena hasta que
se alcanza ese carácter nulo al final.
Ahora, volviendo al comienzo de la clase,
recuerden que hablamos de si sería mejor desde una perspectiva de rendimiento
no seguir recalculando la longitud de la cadena.
En cambio, ¿por qué no decir algo como int length = strlen (str),
guardar ese valor en una variable, y solo buscar ese valor cada vez que
pasa por el bucle y comprueba si hemos alcanzado el último valor de i?
Allí pueden pensar en la longitud como una tabla de búsqueda que almacena un valor.
Hay un ejemplo que ya hemos visto.
Vamos a ir a las tablas de búsqueda que almacenan
la respuesta a más de una cosa.
Y hoy veremos algunos ejemplos diferentes de problemas
que pueden resolver usando la programación dinámica.
Así que estos son problemas que pueden resolver de manera
que se haga la misma pregunta una y otra vez
y simplemente busque la respuesta en una tabla o en un arreglo.
Y el primer problema de este tipo es relativo al corte de una barra.
La idea es que tengo una barra hecha de algo valioso,
así que tal vez sea una Tootsie Roll o plutonio,
hay una gran variedad de cosas que vienen en forma de cilindro largo
y quiero cortarla en trozos más pequeños para venderlos.
Y lo que saben es que son piezas o barras de diferentes longitudes,
que se venden en distintos precios.
Y la pregunta es, ¿cómo debería cortar esa cosa larga
en partes más pequeñas para maximizar el dinero que obtengo al venderlas?
Entonces, si sé, que una barra de una pulgada de largo se vende en $1
y que una barra de dos pulgadas de largo resulta ser mucho más valiosa,
que puedo venderla en $5.
Y que cuando tiene tres pulgadas de largo, puedo venderla en $8.
Ahora hay mucha más demanda de cosas que tienen tres pulgadas de largo
que cosas que tienen cuatro pulgadas de largo.
Entonces si tiene cuatro pulgadas de largo, puedo venderlo
en $9, que es un poco más, pero no mucho más.
Y luego viene la pregunta.
Tengo una barra de longitud 10,
¿cómo debería cortarla en 1, 2, 3 y 4 para maximizar el beneficio?
Y hay diferentes maneras en las que puedo hacer esto.
Podría cortarla en 10 barras pequeñas de una pulgada, y vender cada una en $1
y ganaría $10.
Podría cortarla en cinco barras de dos pulgadas y obtendría 5 por 5, igual a $25.
Eso es mucho más dinero.
Siendo el capitalista sinvergüenza que soy, es probablemente que elija eso.
Y hay algunas opciones intermedias que podrían hacerme ganar dinero.
La pregunta es, cómo descubrir rápidamente
en cuantos pedazos debo cortar la barra para maximizar mi ganancia
y asegurarme de que sea el máximo precio en que pueda venderlo
porque no había otra forma de cortarla que fuera mejor.
Y una forma de pensar en el problema es,
si tengo una barra de 10 pulgadas y puedo cortarla en trozos de una pulgada,
hay 1, 2, 3, 4, 5, 6, 7, 8, 9 posibles lugares donde podría cortar la barra.
Y en cualquiera de esos lugares podría decidir hacer un corte
o no hacer un corte.
Y eso afectaría el resultado final.
Entonces son 9 bits
ya sea que corte o no.
Son un 1 o un 0.
Así que en términos de las posibles combinaciones
de cómo podría cortarla con cada número de 9 posibles bits
en binario, que tienen 2 a la 9 valores posibles, es igual a 512.
Entonces podría probar 512 posibles maneras diferentes de cortar esta barra.
Y puedo descubrir cuál es la mejor.
Y luego podría intentar con una barra que tenga 20 pulgadas de largo
y descubrir que hay 19 posibles lugares para decidir dónde cortarla o no,
y hay 2 a la 19 posibilidades,
que aproximadamente son 500,000.
2 a la 10 son cerca de 1000, 2 a la 20 son casi un millón, 2 a la 19
es casi de un millón dividido por 2, lo cual es igual a 500,000.
Entonces, en general, si dijéramos que esta barra mide n pulgadas de largo
y que hay n lugares, o n-1 lugares en los que pueda tomar una decisión
para cortarla, hay aproximadamente 2 a la n posibles combinaciones
de hacer combinaciones de cortes.
Y eso es un montón de cosas por probar.
Al mismo tiempo, podemos ver cómo se divide en barras
de 1, 1, 5, 5 y 9 pulgadas de largo
no es la única combinación de cortes que me daría eso.
Hay otras combinaciones que también me lo darían, dos de 1, dos de 5 y
perdón, una de 4 pulgadas de largo.
Dos de 1, dos de 2 y una de 4.
Luego podría probar una de 4 y luego dos de 2 y dos de 1,
y obtendría el mismo valor.
Así que hay muchas maneras posibles en las que podríamos cortarla
y nos daría el mismo resultado en términos del valor.
Entonces tal vez quiera reorganizar el problema
de modo que no tenga que probar todo, porque me doy cuenta que muchas veces
estoy recalculando lo mismo.
Y una forma de hacerlo,
y aquí es donde entra el concepto de programación dinámica o tabla
de búsqueda,
es decir, déjame decidir primero dónde haré el corte que sea más a la izquierda.
Entonces, en lugar de decidir si voy a cortarlo 1 pulgada o no,
voy a cortarlo 2 pulgadas o no, voy a cortarlo 3 pulgadas o no,
Voy a decidir ¿cuál es el primer lugar que voy a cortar?
Eso podría ser a 1 pulgada, a 2 pulgadas, a 3 pulgadas, a 4 pulgadas
o a 5, 6, 7, 8 o a 9 pulgadas,
o podría decidir que no voy a cortar en absoluto,
Voy a dejar la barra completa.
En la barra de longitud 10, hay 10 lugares posibles para hacer el primer corte.
Y lo que voy a decidir es si divido la barra en dos,
la parte de la izquierda que no voy a cortar de nuevo.
No voy a cortar esta parte 2
o la parte 3.
Estoy tomando esa decisión.
Esta es la primera pieza que voy a vender.
Entonces, el valor total que puedo obtener será
aquello en lo que pueda vender la pieza de la izquierda por $1, $5, $8 o $9,
más lo que pueda conseguir por la parte correcta si la corto en trozos más pequeños
la mejor manera posible
Entonces, no volveré a cortar la parte de la izquierda.
Puedo buscar en la tabla cuál es el valor de una barra de esta longitud.
Pero también necesito descubrir, para la parte de la derecha,
¿cuál es la mejor manera de cortarla.
E incluso, si calculamos todas las posibilidades,
terminaremos con las mismas 2 a la n,
excepto que podemos empezar a ver algunas cosas en común.
Por ejemplo, si deciden cortar una barra de longitud 2,
tienen ocho sobrantes, y quieren encontrar la mejor manera de cortarla.
Ahora, si solo cortaron una pulgada primero, ese fue su primer corte,
y luego hicieron otro corte donde cortaron otra pulgada,
de repente tienen la mejor manera de cortar una barra de longitud ocho.
Entonces, entre las posibles formas para cortar esta cosa de longitud 9,
una posibilidad es, una vez más, quitar una pulgada para venderla,
y nos quedan ocho pulgadas.
Si desde el principio tomamos dos pulgadas de distancia,
también nos quedarían ocho pulgadas.
Y nos hacemos la misma pregunta en ambas ocasiones.
Dijimos, tengo algunas cosas que corté y que voy a vender,
y ahora me queda una barra de ocho pulgadas.
¿Cómo debo cortarla?
No tiene sentido descifrar eso dos veces.
Podríamos tener una pequeña matriz donde almacenáramos,
¿cuál es la mejor manera de cortar una barra de longitud 8?
¿cuál es la mejor manera de cortar una barra de longitud 9, 7, 6, 5 o 4?
Y para cada una de ellas, simplemente vemos la matriz y decimos:
¿Ya pusimos un valor allí?
Si es así, simplemente lo usamos.
No lo recalculamos
Y el resultado es que, si comienzan a observarlo,
tenemos 10 opciones para nuestro primer corte, y luego, en promedio,
tenemos como nueve opciones para nuestro segundo corte,
y hasta ocho opciones para nuestro tercer corte.
Y así para cada una de las 10 opciones posibles la primera vez,
tenemos nueve opciones, para nuestro segundo corte tenemos hasta 90.
Pero esto nos vuelve exponenciales.
El problema es que muchos de estos cortes terminan siendo los mismos.
Y entonces acabamos diciendo, solo tengo que lidiar
con 10 opciones como primer corte para la barra de longitud 10
y 9 opciones como primer corte para la barra de longitud 9,
y 8 opciones como primer corte para la barra de longitud 8,
porque después de eso tendré que hacer algo más corto.
Con el tiempo lo averiguaré.
Y terminan con algo como n al cuadrado.
Entonces, para barras de longitud 10, tendría cerca de 10 veces 10 opciones
por lo que realmente tengo que trabajar.
Y así disminuimos a n al cuadrado, en comparación con n al cubo.
Otra forma de hacerlo es decir, comenzaré con una barra de longitud 1.
No tengo opciones para cortarla, así que sé la respuesta
por cómo voy a vender eso.
Para una barra de longitud 2, puedo sacar el costo para una barra de longitud 1,
y hacer un corte, y luego tener una barra de longitud 1 sobrante.
Puedo buscar cuánto cuesta.
Y almacenaré cuál de esos fue el mejor valor para la barra de longitud 2.
Diré, para la barra de longitud 2, puedo obtener $5, y la forma de hacerlo
es no cortarla.
Y para una barra de longitud tres, podría decidir cortarla después de 1 pulgada,
y tendría 2 pulgadas sobrantes.
Entonces diremos, bueno, lo mejor que podemos hacer con una barra de 2 pulgadas
es obtener $5 por eso, está muy bien.
Si es de 3 pulgadas, podemos obtener $6 haciendo un corte después de una pulgada.
Si el corte es después de 2 pulgadas, son $5 por vender una barra de longitud 2,
y luego vemos hacia arriba, ¿qué hacemos con una barra de longitud 1?
y la respuesta es que lo vendemos por $1, eso nos da $6.
Luego vemos, ¿y qué pasa si solo vendemos una barra de longitud 3?
Ganamos $8.
Así que registraré... genial, para barras de longitud 3, no lo cortaremos,
solo la venderemos.
Para una barra de longitud 4, la cortamos después de 1 pulgada.
Veamos, ah, para una barra de longitud 1, la vendemos en $1,
pero, ¿qué hacemos con una barra de longitud 3?
Solo la vendemos.
Está hecho.
Descubrí que eso nos da $9.
¿Qué pasa si la cortamos a 2 pulgadas?
Bueno, tenemos una barra de dos pulgadas que venderemos en $5
y luego veamos cómo recuperar la mitad derecha que mide 2 pulgadas de largo.
No nos molestemos.
Mi tabla dice simplemente, quédatela.
Y después de haber probado todo eso, sabemos que cortar 4 en 2 y 2
es la mejor opción que tenemos.
Entonces, lo que sucederá es que, a medida que construimos esto,
a la larga llegaremos a donde queríamos con la barra de longitud 10.
Por cada corte que hagamos, podemos simplemente
ver hacia la mitad izquierda y saben que diremos
solo una cosa, pueden buscar en una tabla la mitad derecha
y decir, ¿cuál es el valor máximo que puedo obtener por esto?
Y la tabla dirá: puedes obtener tantos dólares,
y aquí es donde hacemos el primer corte.
Luego nos sobra una pieza más corta
y podemos buscar dónde hacer el siguiente corte.
¿Preguntas?
Entonces pasemos a otros ejemplos.
El próximo lo veremos muy rápidamente,
al igual que el que le sigue, entraremos en más detalles
incluso en el que cortamos las barras.
Y dibujaremos esa tabla, completando algunos de los valores.
Si recuerdan la red que vimos la semana pasada,
teníamos todo tipo de computadoras que estaban conectadas juntas, por ejemplo,
mi computadora, al menos cuando estoy en mi oficina,
probablemente esté conectada a todo el departamento, a través de un servidor,
o tal vez solo a través del servidor de la Universidad de Yale.
Natalie también tiene una computadora conectada
y dado que nuestras oficinas están cerca, es posible que tengan una conexión
directa entre ellas.
Ahora, David y Doug, en la oficina de CS50 en Harvard, o sus computadoras,
también pueden estar directamente conectados entre sí,
para que puedan enviar mensajes de ida y vuelta directamente,
y también están conectados al servidor de Harvard.
Pero si quiero enviar un mensaje a David, necesitaré,
y podría ser una solicitud HTTP, por ejemplo,
si está ejecutando un servidor web en su computadora
necesitaría encontrar una manera de saltar de computadora a computadora.
No puedo hablar directamente con la computadora de David,
porque no hay cables entre ellas.
Necesito encontrar una computadora para decir, tengo un mensaje para David,
¿sabes cómo entregarlo?
Y dirá, sí, puedo hacer eso por ti.
Y luego lo transmitirá a otra computadora,
diciendo que eso es para David, y lo seguirán
pasando de servidor en servidor, hasta que lleguen a uno que esté
conectado a la computadora de David.
Y el problema de cómo encontrar a quién pasar ese mensaje se llama enrutamiento.
Y es uno de los problemas en las redes.
Es particularmente difícil porque en las redes,
tenemos que lidiar con otras personas
Cuando escriben alguno de sus programas en la laptop,
si algo sale mal, probablemente fue culpa de ustedes,
y probablemente puedas arreglarlo.
Si estamos hablando de una red,
es posible que alguien haya apagado las luces en otro lugar,
o desenchufó su computadora sin previo aviso.
Eso está completamente fuera de nuestro control.
Pero aun así tenemos que lidiar con eso.
Entonces, ¿cómo llegamos desde mi computadora a la computadora de David?
es algo de lo que tenemos que estar pendientes
y puede seguir cambiando, y para llegar allí, tenemos que encontrar...
no solo cómo podríamos llegar a la computadora de David,
sino, esencialmente, a quién pasar ese mensaje para llegar allí más rápido.
Puede haber más de una forma de hacerlo.
Y una forma será bastante rápida,
y el otro camino pasará por China, Sierra Leona
y por un cable submarino congestionado en África, que tiene enlaces de internet
muy pobres, y tomará mucho tiempo para que el mensaje llegue a David.
A menos que, por supuesto, alguien del MIT haya encontrado
el cable que conecta a Harvard con Yale y lo haya cortado solo por diversión,
en cuyo caso podría quedarme estancado en la ruta más larga por un tiempo,
porque algo que está fuera de mi control se rompió.
Y la pregunta de la que quiero hablar hoy es,
¿cómo sabes a quién pasarle ese mensaje?
Y este es otro sitio donde la programación dinámica
es realmente útil.
Entonces, la idea es que no solo necesito
saber cómo llegar a la computadora de David,
Necesito saber cómo llegar a todas partes en Internet.
Y todas partes en Internet también necesita
saber cómo llegar a todas partes en internet.
Todo el mundo está tratando de resolver este problema, cómo encontrar la forma
más rápida para llegar a cualquier otra computadora.
Y para empezar, solo estoy enchufado, ni siquiera sé con quién estoy conectado,
y mucho menos sé quién más está por ahí.
Entonces, el proceso es, en lugar de que yo trate de fisgonear y explorar
todo el internet,
y que para cuando termine, alguien haya desenchufado su computadora
y por lo tanto, el proceso necesite cambiar
Quiero intentar y asegurarme de que todos
trabajemos juntos y compartamos la mayor cantidad de información posible.
Entonces, lo que haré es transmitir un mensaje a todos
mis vecinos, diciendo ja, ja, ya llegué, soy Benedict,
y ahora Natalie lo sabrá, y el servidor de Yale lo sabrá,
ajá, la computadora de Benedict está conectada directamente a nosotros.
si necesitamos enviar un mensaje a Benedict, podemos simplemente dárselo.
Ahora, al mismo tiempo, todas las demás harán eso.
Así que recibiré un mensaje de Natalie, diciendo ja, ja,
aquí estoy, y conseguiré uno de Yale diciendo ja, ja, aquí estoy.
Y ahora sé que estoy conectado directamente con Natalie y Yale.
Esto es lo que llamamos estar a un salto de distancia.
Entonces, si necesito enviar un mensaje, necesito hacer un salto,
al igual que un avión hace un salto de ciudad
a ciudad, para llegar a la siguiente computadora.
Y hasta donde yo sé, eso es todo lo que hay en el mundo.
Pero todo el mundo hace esto, así que ahora todos conocen todas las computadoras
que están adyacentes.
Lo siguiente es que todos podemos enviar esa información y decir,
aquí estoy, y oh, por cierto, aquí está todo el mundo con el que estoy conectado.
Entonces, si me dan un mensaje para cualquiera de esas personas,
estoy diciendo a cualquiera que me pase un mensaje para Natalie,
puedo llevárselo de un salto.
Entonces pueden calcular cuántos saltos les toma un salto más que eso
si me pasan el mensaje
Después que compartimos eso, podemos actualizar nuestras listas.
Lo que sucederá es que el servidor de Yale aprendió de mí, ja, ja, ja,
Puedo darle el mensaje a Natalie por ti, solo dámelo y sin escala
se lo daré a Natalie.
Y el servidor de Yale dice, sí, pero yo solo puedo dárselo a Natalie,
¿por qué molestarme en hacerlo?
El servidor de Yale seguirá en su lista. Sé cómo llegar a Natalie en dos saltos.
no me importa.
Puedo llegar allí de un salto para dárselo directamente a Natalie.
Aprendí que Yale puede alcanzar al servidor de Qwest de un salto.
Si necesito pasarle un mensaje a Qwest, le daría el mensaje a Yale
y le pediría que se lo dé a Qwest, esa es
la manera más rápida que conozco para llegar ahí.
En cada paso, todo el mundo envía un mensaje bastante corto
a todos sus vecinos.
Toma una cantidad de tiempo fija.
Acumulamos esta información y siempre mantenemos
el seguimiento de, ¿cuál fue el más corto?
¿cuál tomó el menor número de saltos para llegar a un servidor?
¿y a quién le daremos el mensaje al llegar ahí?
Siempre que le dé el mensaje a Yale,
Yale se preocupará de cómo dárselo a Qwest, me prometió que solo
tomaría un salto.
Si hago esto de nuevo, podría llegar a Google en tres saltos.
Puedo llegar a Harvard en tres saltos.
Lo único importante es que llegaría en tres saltos y la manera en que le
daría el mensaje al servidor de Yale.
Incluso el servidor de Yale no sabe cómo hablar directamente con el de Harvard
o con el servidor de Google.
Solo sabe pasar el mensaje al servidor de Qwest.
Por cierto, Qwest, posee muchas de las grandes fibras que
conectan grandes centros de datos y otras cosas.
Al trazar lo que sucede, si intento conectarme
desde mi oficina al servidor de Google, atraviesa el servidor del
departamento CS, luego un servidor de Yale, luego va a Qwest,
y luego va a Google.
Hay algunos otros, como Internet2,
que conecta a muchas universidades y America Online
que ​​tenía mucho de esto, no sé si alguna vez lo vendieron.
Existen algunas compañías que poseen la mayoría de esos cables.
Finalmente, después de cuatro rondas, descubro que incluso llegaría con David
y Doug, en cuatro saltos para darle el mensaje a Yale.
Yale sabe que solo necesita darle
ese mensaje a Qwest, que le da ese mensaje a Harvard,
y luego Harvard sabe cómo pasar el mensaje.
Así es, cómo los principales enrutadores de Internet
comparten esta información.
Nuestras computadoras, o mi computadora, lo que generalmente saben es que
cualquier mensaje que necesito enviar lo envío del servidor al que estoy conectado,
al punto de acceso inalámbrico.
El punto de acceso inalámbrico se lo envía al servidor de Yale.
Tal vez hay una muy pequeña, que sabe que las siguientes laptops se
conectan directamente a mí, y que todo lo demás lo envié al servidor de Yale.
Pero cuando llega al nivel de toda la Universidad de Yale
puede iniciar el proceso de compartir esta información
para saber si podría hablar con Qwest, si podría hablar con Internet2,
si podría hablar con Comcast, hay muchas personas con las que podría hablar.
¿A cuál cable debería enviar este mensaje
para asegurarme que llegará a donde va?
Y el aspecto de programación dinámica de esto es, que en cada paso
estoy reutilizando toda la información que se calculó antes
para simplemente agregar otra capa en la parte superior.
¿Cómo llego a lugares en tres saltos?
No estoy recalculando la ruta para ir de Yale a David,
por así decirlo, cuando descubro que puedo llegar allí en cuatro saltos.
Solo utilizo la información que tengo de que el servidor de Yale puede hacerlo.
Y eso reduce la cantidad de trabajo que debo hacer.
¿Alguna pregunta?
Ahora, la tercera categoría de algoritmo de la que quiero hablar,
y voy a hablar sobre esto con un poco más de detalle,
hablaré de algunas aplicaciones diferentes que tienen exactamente
el mismo algoritmo, entonces, no solo es programación dinámica
sino un algoritmo específico que puede resolver diferentes problemas.
Y comenzaré con la alineación de la secuencia de ADN,
también se utiliza para alinear secuencias de proteínas.
Si se tiene una secuencia de ADN, esta se compone
de una cadena de algo que se llaman bases, los cuales son productos químicos.
Son moléculas.
Y hay cuatro de ellas que se usan en el ADN,
adenina, timina, guanina y citosina.
Y por lo general abreviaremos cada una con una letra.
Entonces, si tuvieran que desenrollar una secuencia de ADN,
y simplemente escriben la lista de estas bases de estas moléculas
que están conectadas en una larga cadena, esencialmente
obtendrían una cadena compuesta de cuatro letras.
Entonces es una cadena con alguna combinación de los caracteres A, C, G y T.
Ahora bien, es posible que sepa de memoria todo el genoma de un ratón
y sepa lo que hace cada gen.
O que al menos sepa lo que algunos de ellos hacen.
Con el tiempo divergimos de los ratones y varias mutaciones pasaron a nuestro ADN,
porque cada vez que se copia, a veces se cometen errores,
y de alguna manera acabamos siendo, en vez de un ratón proto,
un ser un humano moderno.
Y ese mismo ratón proto terminó siendo un ratón moderno.
a través de diferentes mutaciones.
Entonces, si empiezo a secuenciar mi ADN, podría encontrar
un fragmento que corresponde a un gen particular
y no sé lo que hace, pero me gustaría averiguarlo.
Una forma de hacerlo sería tratar de hacer coincidir y encontrar
el mejor lugar posible para que coincida con el gen del ADN del ratón,
¿coincide con el mejor?
Teniendo en cuenta que podría haber gaps, un gen podría haber desaparecido,
otro haber reaparecido o ser agregado, y algunas veces una de estas bases
podrían haber sido reemplazadas por otras diferentes.
Entonces podríamos tener una disparidad
y podrían suceder diferentes cosas
durante el proceso, en el ratón no será un gen idéntico perfecto
al del ser humano, será más o menos lo mismo.
Y podría usar eso para decir cuál es la mejor combinación
y luego usar aquello para predecir lo que codifica este gen,
o cuál es la función de la proteína que codifica.
Podemos hacer lo mismo con las proteínas, porque están hechas de aminoácidos.
Podemos observar esa cadena y buscar cosas similares entre una proteína
y una base de datos de proteínas donde sabemos lo que hacen
para ver si podemos predecir la función.
Pero el problema es que hay muchas maneras diferentes de hacerlo.
A continuación escribiré en la pizarra estos que tenemos, AAC, AGT, TACC.
Ahora, intentaré encontrar la peor coincidencia posible.
TAAGGTCA.
Entonces, hay diferentes formas posibles para alinear estas cadenas.
No tienen la misma longitud, así que necesitaré algunos gaps.
Hay diferentes lugares donde podría intentar ponerlas y ver
cómo se alinean los caracteres.
Y la regla va a ser...
y se basa, de hecho, en cuán común
es para que ocurra una mutación donde una base
cambia a otra, en lugar de que una base se borre o se agregue
en mutaciones genéticas.
Resulta que es casi dos veces más probable que una base simplemente
cambie a otra base, a que una base desaparezca o aparezca completamente
de la nada
durante la mutación.
Entonces diremos que cuando alineamos las cadenas,
cada vez que tengamos una disparidad entre dos caracteres,
de modo que dos bases no sean lo mismo,
vamos a tener una penalización de 1.
Y tenemos un costo de uno.
Eso es lo que pagamos por forzar a las cadenas para que coincidan así.
Y cada vez que tenemos un gap donde, digamos, la C de la primer cadena o
secuencia de ADN no coincide en nada con la segunda,
de modo que eso corresponde a una base que se inserta o elimina,
le asignaremos una penalización o un costo de 2.
Así que preferimos tener dos mutaciones,
dos cosas cuya base se convirtió en otra cosa,
eso parece tan probable como tener algo que simplemente se agregó o se eliminó.
Y luego puedo sumar todos esos costos
y obtener el costo de esa forma particular de igualar las cadenas.
Y busco la manera de lograrlo con el menor costo de coincidencia posible.
A esto se le llama también edición de distancia, porque también
mide el número de ediciones que tendría que aplicar a un texto,
cuántos cambios tendría que hacer en un texto para cambiarlo de una cosa
a otra.
No solo tiene que ser estas cuatro letras.
Esto podría ser cualquier cosa.
Pero lo que he escrito en la pizarra y que quiero que vean
es la peor manera en la que podría hacer coincidir esto, es decir,
¿Qué pasa si acabo de eliminar todo el gen del ratón,
y luego hice un montón de inserciones para producir el gen completo del humano?
Entonces, cada uno de estos caracteres, decimos, coincide con un gap.
Esa es una posibilidad.
No es muy buena.
Eso sería 2 + 2 + 2 + 2 + 2 + 2, bla, bla, bla,
bla, bla, bla.
Eso costaría mucho.
Esa es una manera muy poco probable de que hubiésemos llegado de esto a esto,
y estamos buscando la forma más probable.
Pero lo que dice es que realmente no tiene sentido insertar más
gaps en el medio, porque esas cosas se alinean perfectamente,
no cambiarán el puntaje de coincidencia.
Eso es un poco tonto.
Una vez que esto se haya ido por completo, también podría
comenzar a agregar el nuevo.
Así que la secuencia más larga posible donde tratamos de alinear los dos
es la longitud de uno más la longitud del otro, que aquí es aproximadamente 18.
Y ahora, si observamos cada una de estas columnas,
hicimos una especie de elección.
De la misma manera con la barra pudimos elegir, ¿cortamos o no cortamos?
Aquí tenemos una opción para hacer una eliminación
así que tuvimos una letra en la primer cadena
y haremos coincidir con un gap la segunda cadena, como si esta letra se borrara.
O podríamos hacer una inserción, en cuyo caso es algo así como esta situación
donde no teníamos nada y agregamos una letra.
Podríamos hacer eso al comienzo de la cadena
o podríamos tratar de hacer coincidir dos letras.
Entonces aquí, en cada punto, podemos elegir entre tres opciones,
una inserción, una eliminación o una coincidencia.
Y si pudiéramos hacer esa elección de forma independiente,
entre cada una de las letras, tendríamos 3 a la 17 combinaciones posibles
de cosas para probar.
Eso es demasiado.
¿Cuánto es del 3 a la 17?
¿Alguien sabe la respuesta aproximada?
OK, el término técnico es, demasiado.
Es un número n.
Se define como 3 a la 17.
Entonces, de nuevo, no queremos seguir...
no queremos probar todo.
Lo que realmente queremos es esto.
Los biólogos computacionales quieren descubrir, observar
a través de una base de datos de genes o de proteínas
y trata de hacer coincidir algo para descubrir qué podría hacer.
Qué partes del genoma que acabamos de secuenciar
o de la cadena de ADN que acabamos de secuenciar vale la pena poner a cero,
y hacer más estudios al respecto, porque podrían codificar algo importante.
Resulta que también nos gusta hacer esto también en ocasiones,
o al menos lo hacemos, cuando queremos revisar las tareas de dos personas
y ver qué tan similares son.
Entonces, esencialmente, los detectores de plagio hacen eso.
Tratan de calcular rápidamente cuántos cambios tendría que
aplicar al programa de un estudiante para convertirlo en el programa de otro.
Y una vez que lo hayamos calculado, queremos buscar y encontrar a los que,
para la mayoría, tal vez les tomó mil ediciones,
pero aquí tenemos a un par que hicieron solo dos.
Y eso es algo sospechoso.
Voy a replantear el problema.
Y será similar al del corte de barras, donde voy a decir,
Imaginemos que ya sé cómo hacer coincidir la parte derecha de dos
de las cadenas.
Entonces, si ya conozco la mejor manera de unir GTTACC a GTCA,
y luego para descubrir la mejor manera de hacer coincidir todo
solo tengo que descubrir cómo hacer la primera parte.
Y esto es como decir, déjame determinar
dónde voy a poner la primera letra de la primera palabra,
dónde quiero poner el primer gap, digamos, o la primera inserción,
y luego descubre el resto.
Pero lo expreso yendo desde el final hasta el principio.
Y lo fraseo yendo al final del inicio
tal como lo que he mostrado aquí, porque cuando
calcule la tabla, terminará poniendo
la respuesta en un lugar más lógico.
Así que podemos decir, en la última posición de esta coincidencia
vamos a tener una inserción, vamos a tener una eliminación
o vamos a tratar de hacer coincidir dos caracteres
Eso nos da tres posibilidades
y cada uno tiene un costo.
Así que cualquiera que sea la mejor manera de alinear estas dos cadenas,
lo último que sucederá será una de estas tres cosas
esas son las únicas tres formas posibles para que ocurra la última parte
de la coincidencia,
tiene que ser una inserción, una eliminación o una coincidencia,
porque son las únicas opciones.
Si conocemos los costos de ello, entonces
podemos decir que el costo de hacer coincidir toda la cadena es el costo de
cualquier elección más el mejor costo de edición de distancia, o mejor alineación,
de lo que quede.
Lo cual es ligeramente diferente en cada uno de estos tres casos.
Excepto que si, por ejemplo, hacemos una eliminación y luego para esta elección
donde tenemos TAAGGTCA en la parte inferior, la próxima vez
tratamos de hacer una inserción, y luego a lo mejor de lo que quede,
tendríamos una coincidencia de AACAGTTACC con TAAGGTC.
Luego tendríamos que insertar A y después eliminar C.
Perdón, terminaríamos con estas dos cadenas.
Pero en lugar de que tratemos de hacer coincidir C con A,
habríamos modelado una inserción y una eliminación.
Nos quedaría lo mismo aquí.
Así que, a medida que construimos estas cosas,
evitamos hallar la mejor coincidencia dos veces para el mismo par de cadenas .
Y, de manera realista, lo que va a pasar es que podemos comenzar con, bueno,
¿y si ambas cadenas estuvieran vacías?
Entonces coincidirían perfectamente.
No hay problema.
¿Qué pasa si al final quisiera
que la cadena C o el carácter C fuera eliminado?
Me gustaría tener algún tipo de coincidencia aquí
donde lo último que sucede es que C se elimina,
y el resto de ese TAA, bla, bla, bla, bla, está en algún lugar de aquí.
Pero me preocuparé más tarde de dónde.
Entonces, en ese caso, tenemos un costo de 2 y cuando terminamos,
hubiéramos usado este carácter C, porque lo borramos.
Y lo que nos queda después de eso es lo que está en la celda correspondiente,
a la derecha en la tabla, que es el costo de igualar nada a nada.
Del mismo modo, si dijéramos que lo último que queremos tener en la tabla
es una coincidencia de CC a nada, el costo de eso
es 2 para la coincidencia, la eliminación de C, más el costo de igualar nada,
C a nada, lo cual era esta celda en la tabla.
Así que tenemos 2 más el costo de 2, que aquí es 4.
Entonces, en general, lo que sucederá, si observo la celda
donde ahora está la manita...
que justo desapareció, aquí, este es el costo,
bueno, básicamente registraremos el costo de igualar GTTACC a GGTCA.
Y dirá... para obtener la mejor combinación entre estos,
es decir, la menor edición de distancia, si quieren verlo así,
aquí está lo que será el costo total y les dirá que deberían
tratar de hacer coincidir esas dos G entre sí, o que deberían hacerlo como...
que deberían tener una eliminación o una inserción
Eso es lo que se almacenará en esta celda de la tabla.
Y, finalmente, eso significa en la entrada 0, 0 de la matriz
al inicio de la tabla, les dirá el costo
de hacer coincidir AACAGTTACC a TAAGGTCA.
Les dirá cuál es el costo total y
les dirá si deben comenzar con la inserción,
la eliminación o una coincidencia.
Y dependiendo de cuál, eso agotará los caracteres de una
o de ambas cadenas, las cuales los moverán a una nueva celda en la tabla
que les dirá qué hacer a continuación.
Así que una vez que está preparado puedo decir al final, bueno,
si observo el final de cada cadena, tengo que hacer coincidir la letra C con la letra A.
¿Y cuál es la mejor manera de hacerlo?
Bueno, tengo tres opciones.
Una opción es que podría eliminar la C. Oh, hay un error aquí,
debería leerse i + 1.
Entonces podría decir, déjame ir sobre el costo de i + 1.
Así que he usado C. Pero me quedaré en...
perdón, i + 1 es esta fila, pero me quedaré en la columna j...
así que me voy a pasar para acá, la cual dice que he agotado la A,
así que esta A de aquí coincidió con un gap, el cual es como una inserción.
Dice, en algún lugar al final de la tabla,
voy a terminar con esa A de la segunda cadena.
Y, oh, aún tengo la C flotando alrededor.
Dejen vuelvo a utilizar la tiza blanca.
Pero el sobrante, aún tenemos una C aquí,
porque estábamos tratando de hacer coincidir A a C, y hasta ahora
no he tratado con esa C. Así que tendríamos
el costo de emparejar A con un gap, el cual es 2,
más cualquiera que sea el costo de igualar C a nada, el cual es 2.
O podríamos decir qué pasaría si, en lugar de eso, hiciéramos C
y borráramos C en lugar de insertar A,
pero significa que todavía tenemos una A flotando por aquí, que aún no hemos usado.
Esa fue una forma alternativa de comparar C y A. Entonces, tenemos un costo de 2,
porque estamos haciendo una eliminación.
Además, nos queda el costo de la celda que está
a la derecha de la tabla, de hacer coincidir a la cadena A con nada,
y eso sería un costo de 4.
O podríamos tratar de hacer coincidir C y A. Y cuando hacen coincidir C y A,
el costo de esto es cero si las letras son las mismas y uno
si son diferentes.
Entonces, en este caso es uno, porque C y A no son la misma letra.
Y nos quedan dos cadenas vacías, porque hemos agotado la C y la A.
Así que terminamos con 1, más el costo de lo que esté sobre 1
y abajo 1 en la tabla, el cual es 0.
Entonces ese costo total es de 1.
Y de esas tres posibilidades, simplemente tomamos la más pequeña.
Decimos, ah, el más pequeño sería si fuera diagonal, si hiciera coincidir A y C.
Entonces, eso es lo que registraré en la tabla.
El costo total de igualar A a C es uno, y lo mejor que se puede hacer
es tratar de hacer coincidir estos dos caracteres.
Sería una disparidad.
Y luego buscar en la tabla qué hacer con lo que quede.
Y ahora podría decir, bueno, ¿qué pasa si hago coincidir la cadena CC a A?
Puedo hacer la misma pregunta.
Podría decir que comenzaría por unir las letras C y A,
y ahora tengo un C sobrante.
Podría eliminar la primera C y ahora tengo una A y una C sobrantes.
Y sería de la siguiente manera.
Lo siento, si hago coincidir ambos.
Podría tener una C sobrante.
Si borro... si solo comienzo con una C y hago una eliminación,
termino con una C y una A sobrante.
Si lo primero que hago es insertar la A, tengo una CC sobrante.
Entonces, para cada uno de ellas tengo una penalización de
2 por ir a la derecha, 2 por bajar, 1 por ir en diagonal, que agrego a lo que sea
en esa tabla
Descubrí que si trato de igualar A a CI tendré una penalización de 1,
y tengo una C sobrante que debo eliminar,
entonces hay un costo total de 3.
Y esa era una buena opción como yo, así que
eso es lo que puse en la tabla.
Y luego puedo tratar de averiguar para la cadena ACC a A,
y solo funciona hacia atrás, a través de esta tabla y sube las filas
hasta que todo esté completo
Y lo bueno de esto es que, cada vez que veo la tabla,
necesito tres piezas de información.
Necesito la información que está en la columna, justo a mi derecha,
necesito la información que está en la columna de la fila,
justo debajo de mí,
en la celda que está justo debajo de mí
y necesito la información que está uno abajo y uno a la derecha.
Y dado que estoy trabajando hacia atrás en la tabla,
es agradable y fácil porque los datos ya están allí.
Empecé a llenar la última fila en la última columna,
los cuales son una especie de casos especiales, y ahora solo
tengo algunos bucles que se ejecutan hacia atrás a través de esto.
Y si una cadena tiene n letras de longitud, es decir, que n un número
y la otra cadena es m, una m como en mnemotécnico,
entonces el total es m veces n.
Y veo que Doug se ríe del mnemotécnico.
Tengo que atribuir ese chiste particular al profesor Mitzenmacher, de Harvard,
según me lo contó mi compañero de cuarto, porque yo no estuve
en esa clase, cuando hizo la broma.
Pero me gustó.
Entonces pueden llenar la tabla.
Y ahora, en lugar de tener 3 a la n, esencialmente, cosas que
tuvieron que probar, solo tienen n al cuadrado.
Entonces son 18 letras
18 al cuadrado es un poco menos que 20 al cuadrado.
Y 20 al cuadrado es 400.
Así que tenemos, como máximo, cerca de 400, pero en este caso
ni siquiera es n + m al cuadrado, es n al cuadrado.
Es como 10 al cuadrado.
Es como 100.
Entonces, tenemos cerca de 100 pasos diferentes para encontrar la respuesta.
Si probamos todo, estaríamos haciendo el mismo trabajo
una y otra y otra vez, y sería como 3 a la 20,
y eso, una vez más, es un número realmente grande.
2 a la 20 es un millón.
Y del 2 al 3 es como--
3 es como 2 a la 1.2... digamos, aproximadamente un millón
a la 1.2.
Es cerca de 130 millones, y allí tienen.
Sabía que había un sabio idiota en alguna parte de la sala.
Y de nuevo, cada una de estas celdas en la tabla
nos dice esencialmente qué hacer.
¿Haz coincidir este primer par de letras?
¿Haz una eliminación?
¿O haz una inserción?
Y a partir de eso, pueden averiguar cuánto queda de cada una de las cadenas.
Y luego miran a la siguiente celda que corresponde de la tabla
donde se hará el paso siguiente.
Y para descubrir exactamente cómo coinciden estas cosas
y cuál es el costo de hacerlas coincidir
de la mejor manera posible, se trabaja la ruta a través de la tabla,
en vez de tratar de almacenar toda esa información en cada celda,
y almacenar la misma información una y otra vez,
se almacena solo una parte de la respuesta en cada celda.
¿Preguntas?
Sí.
PÚBLICO: En el tiempo de ejecución dice que hay OMN, la M es una constante
o es otra...
¿Qué es [INAUDIBLE]?
PONENTE: Bien, en este tiempo de ejecución, donde dice OMN,
¿qué significa esto?
Lo que estamos diciendo es que tenemos dos cadenas diferentes.
Una de ellas tiene n caracteres de longitud, la otra tiene m.
Entonces, en términos de la función de la entrada,
solo estamos siendo un poco más precisos y decimos:
tenemos dos palabras que tienen n letras de longitud.
Y luego es una especie de n al cuadrado.
Lo cual implica que tal vez son longitudes muy diferentes
y que podemos ser un poco más precisos en cuanto a la cantidad de pasos que siguen.
Pero la clave es que se ve una cosa por otra,
es una especie de algo al cuadrado.
A medida que se piensa, a medida que estas cadenas se hacen cada vez más largas,
¿Cuánto trabajo implica?
Entonces no es tan malo.
Y en realidad es muy viable aplicar esto en las secuencias de ADN
que podrían ser unos cientos de pares base de longitud.
Es viable hacer esto para un archivo fuente de la tarea
y hacer trampa al revisar, quizás son unos cuantos miles de caracteres.
No es gran cosa, a pesar de que tenemos 1000 estudiantes,
y tenemos todas las entregas pasadas, entonces tenemos 10,000 presentaciones,
así que tenemos aproximadamente un millón de pares de archivos,
o algo así, y eso no es gran cosa para una computadora.
Y cada uno de estos toma alrededor de un millón de pasos,
porque tenemos 1000 caracteres por 1000 caracteres.
Podemos manejarlo.
¿Alguna otra pregunta?
Sí.
PÚBLICO: ¿Se aplica la fuerza bruta para completar la tabla inicialmente?
PONENTE: ¿Se aplica la fuerza bruta para completar la tabla inicialmente?
Eso depende de cómo definas la fuerza bruta.
Tienes que llenar todas las celdas de la tabla.
Porque para encontrar la respuesta que está arriba a la izquierda,
a pesar de que, a fin de cuentas, solo usará el valor de uno
de estos tres, debes mirar los tres
valores, el de la derecha y los dos de abajo.
Entonces, para encontrar la respuesta final,
tengo que llenar toda la tabla.
Y hay m por n entradas en la tabla, que es donde se llega a este m por n
n o n tipo cuadrado de cosas.
Pero comparado con lo que podríamos pensar como fuerza bruta
al tratar cada alineamiento posible de las dos cadenas,
o cual implica muchas subalineaciones, muchas
partes que se alinean de la misma manera, en muchos lugares diferentes,
no estamos rehaciéndolos una y otra vez.
Así que no estamos aplicando una fuerza bruta tan brutal que
diríamos algo así como de 3 a 2000 pasos
para comparar un solo par de archivos, y que luego debamos hacer un millón.
Eso sería un problema.
¿Alguna otra pregunta?
Así que de alguna manera incorporé dos aplicaciones en una
justo ahí, con la edición de distancia.
Una es de biología computacional, donde se trata una secuencia de ADN o proteína
como una cadena.
Y el otro es una especie de comparación de archivos sofisticados
que pueden usar para buscar y reemplazar.
Pueden usarla para revisar la ortografía o para descubrir
la mejor palabra en el diccionario.
No solo es la palabra allí, sino de las palabras
que están en el diccionario, cuál es más probable que quiera
escribir según algún modelo de frecuencia de los diferentes tipos
de errores.
Y podría calcular la edición de distancia para cada palabra en el diccionario,
a fin de proponer una lista de sugerencias.
Pero hay otra aplicación de la que quiero hablar,
que es el cosido de imágenes.
Esto sucede cuando sacan su teléfono celular
y construyen una imagen panorámica
Entonces, barren alrededor con la cámara y toman un video
o tal vez tomen una serie de imágenes cada uno o dos segundos,
y luego los unen en una gran imagen.
Y hay algunas partes en ese proceso.
Una parte es que cada una de la imágenes que tomaron descubre
cómo se movió su cámara entre cada una.
Entonces puede transformar las imágenes como si
se superpusieran en los lugares correctos.
Pero incluso entonces, no se alinearán perfectamente, porque tal vez alguien
caminó en ese instante en la escena, o resulta
que si estaban caminando con su cámara así,
en lugar de simplemente girar alrededor, se obtendría un efecto de paralaje
donde las cosas que están más cerca de ustedes parecen moverse más rápido
que las cosas que están más lejos.
Entonces, ahora veo parte de la silla del medio.
El lado derecho de la misma, para mí, está escondido detrás del trípode.
Pero si camino hasta aquí, se vuelve visible y el lado izquierdo del asiento
Queda oculto detrás del trípode.
Así, con este panorama, tal vez no haya un modo perfecto de iluminar las imágenes,
porque hay información visible diferente incluso en las áreas superpuestas.
Y para resolver ese problema, la clave es una vez que nos demos cuenta
cómo se superponen las dos imágenes, queremos descubrir y formar parte
de esa área superpuesta de una imagen y parte de ella
de la otra imagen.
Entonces hay una costura entre ambas.
Y una costura es solo una línea de píxeles conectada.
Todo en un lado proviene de la imagen A, todo en el otro lado
proviene de la imagen B. Y la pregunta es, ¿a dónde debería ir esa costura?
Ahora, esa costura debería ir, idealmente, en algún lugar donde no la notemos.
Cambiemos de una imagen a la otra en algún lugar
donde sean realmente similares, y en algún lugar
donde veamos que el paralaje del trípode se mueve,
o donde alguien caminó en la escena.
Intento hacer esta costura donde conecto las dos imágenes que rodean eso.
Así que no se trata de una especie de salto donde las dos imágenes eran
diferentes.
Bueno, la forma más sencilla de hacerlo es ver
a un par en este rectángulo superpuesto, y digamos, dibujemos esta costura
donde los píxeles sean realmente similares.
Y podría hacerlo, por ejemplo, viendo la diferencia
en el color del píxel entre ambos.
En este caso, es agradable y fácil porque las imágenes son en blanco y negro
así que solo tomo la diferencia en intensidad,
que es un número entre 0 y 255, y voy a
obtener un número diferente que esté entre 255 y 255 negativos,
Así podría restar blanco de negro.
Tomo el valor absoluto de eso.
Si esto es 0, significa que el píxel era del mismo color en ambas imágenes.
Si es realmente grande, significa que los colores fueron diferentes.
Y ahora, podría decir que el costo de una costura
a través de un píxel particular es esa diferencia en valores entre los píxeles
de la imagen,
la diferencia en intensidad.
Y para la costura total, quiero averiguar a partir de ahí si,
¿debería ir a la derecha?
¿debería ir hacia abajo?
¿o debería ir hacia abajo y a la derecha?
Lo que puedo hacer buscando en la tabla
el costo de una costura que pasa por el píxel de la derecha o el píxel de abajo
y a la derecha o el píxel de la izquierda.
Aquí está la tabla, solo es eso ahora, una celda en la tabla,
en lugar de decir el costo de hacer coincidir ACCC, por ejemplo, con TCA,
completando una celda en la tabla.
Con eso, voy a interpretar esa celda como el costo de
que la costura de la imagen se ejecute de aquí hasta la esquina inferior derecha.
Y en ese sentido, si ven las costuras de la imagen,
pueden ver esa matriz.
Es este rectángulo de píxeles superpuestos entre las dos imágenes.
Y el algoritmo para encontrar la costura óptima es idéntico a la edición de distancia.
Absolutamente idéntico.
La única diferencia... aunque dije que era absolutamente idéntica,
por supuesto, hay una diferencia, de lo contrario no sería divertido,
pero la diferencia y la razón por la que digo que el algoritmo es idéntico
es esta función de costo que decide cuál es el costo total de esta celda basada
en el costo de lo que está a la derecha, abajo y a la derecha
y abajo, es diferente.
Esa función de costo para editar la distancia fue un número
que depende de si coinciden o son dispares
o era una inserción o eliminación más el costo de lo que está a la derecha,
o abajo o diagonal.
Entonces, era una penalización diferente que
se pagaba por moverse a la derecha en esta tabla, o hacia abajo, o en diagonal.
Con esta costura de la imagen en este momento, en la forma en que lo definí,
el costo es el mismo.
La penalización que se paga por tener una costura atraviesa un píxel en particular
es lo mismo incluso si continúa hacia la derecha, en diagonal o hacia abajo.
es solo la diferencia en intensidad.
Entonces tomaríamos el mínimo de...
tomaríamos la diferencia en intensidad más el mínimo
de lo que está a la derecha, abajo, o en diagonal a la derecha.
Es una función de costo ligeramente diferente, pero es el mismo algoritmo.
Y hay formas de implementar esto donde esencialmente podemos
decir al algoritmo, OK, rellena esta tabla,
usa esta función de costo para que pueda usar exactamente el mismo código,
implementa esto una vez, edita la distancia
y también puedes hacer cosidos de imagen.
Y uno de los aspectos en donde los algoritmos
y la abstracción de la informática se vuelven algo
es muy interesante cuando comenzamos a ver dos problemas que parecen
no tener relación y resultan ser lo mismo.
Y es aún más divertido cuando podemos escribir
un programa que hace dos cosas completamente diferentes para nosotros
y ni siquiera tenemos que volver a implementarlo.
¿Alguna pregunta?
Voy a finalizar entonces con una demostración de un algoritmo más
que utiliza la programación dinámica
y pueden encontrar varias demos en la web, este es solo uno de ellos.
llamado seam carving.
Esto es muy similar al cosido de imágenes, donde encontramos
esa costura yendo de la esquina superior izquierda a la esquina inferior derecha.
La idea aquí es que queremos cambiar el tamaño de la imagen de la derecha.
Y ya lo hicieron en la serie de problemas 3.
Cambiaron el tamaño de las imágenes, o en la serie de problemas 4, perdón.
Ahí cambiaron el tamaño de las imágenes.
Y si las hicieron más estrechas, quedaron algo aplastadas,
si las hicieron más anchas, se estiraron un poco,
y todo se veía mal
¿No sería genial si pudiéramos cambiar el tamaño de la imagen,
pero que aun así se viera normal?
Entonces, la idea del algoritmo seam carving
es, esencialmente, cambiar el tamaño de la imagen.
En lugar de escalar cada píxel para que sea un poco más estrecho que antes,
solo tomamos una fila de píxeles y los eliminamos.
Entonces podríamos tomar esta fila de aquí, o esta columna de píxeles,
y eliminarlos, y tal vez nadie lo note.
El problema es que solo podemos hacer esto en una imagen cierto número de veces
antes de que alguien lo note.
Porque se obtiene algún tipo de salto, en donde se borra información útil.
Y una de las razones es que, por ejemplo, en esta columna de aquí,
en el agua, podrían salirse con la suya borrando un píxel.
en el cielo, podrían salirse con la suya borrando un píxel.
Pero en el globo, es mejor que no eliminen un píxel
porque sería bastante obvio.
El globo comenzaría a verse muy chistoso, muy rápidamente.
Entonces, en lugar de encontrar una línea recta de píxeles para eliminar,
vamos a encontrar una línea ondulada.
Aún seguimos eliminando un píxel de cada fila,
así que cada fila terminará siendo un píxel más pequeña de lo que era antes,
eso significa que la imagen seguirá siendo rectangular, pero tenemos una opción.
Si eliminamos un píxel en particular arriba en la imagen, en la siguiente fila
hacia abajo podríamos eliminar el píxel que está justo debajo de él, o el que está
a la izquierda o el de la derecha.
Así que esto también es bastante... tenemos estas tres opciones,
tal como lo hicimos antes,
con una función de costo ligeramente diferente.
Y luego tenemos... puedes comenzar eliminando cualquiera de los píxeles
de la parte superior.
Elegiremos el que tendrá el menor costo.
Y ese costo podría ser cuán diferentes son sus píxeles a los de su vecino
a la izquierda y a la derecha.
Entonces, si son más o menos del mismo color los de la izquierda
que los de la derecha, nadie se dará cuenta si lo eliminamos.
Y con tan solo un poco de trabajo extra,
pueden actualizar posteriormente esa información, esa tabla,
para reflejar el hecho de que eliminaron ese píxel de cada fila.
Pueden arreglar todas las costuras que
hayan afectado para averiguar, si tienen que aplastar
otro píxel, cuál sería este.
Y pueden comenzar a cambiar el tamaño de la imagen.
Y pueden ver que está borrando mucho cielo,
al igual que está borrando mucha agua.
Está siendo muy cuidadoso con la forma en que reduce estos arbustos.
Y los adelgaza mucho menos que lo que reduce otras cosas,
trata de hacerlo de manera sensata.
Lo acostumbramos ver se queda normal en su mayoría y el espacio vacío
está siendo aplastado
Pero ese espacio vacío está en diferentes lugares
dependiendo de dónde se encuentre en la imagen.
Y aquí abajo, por ejemplo, no tenemos mucho espacio vacío.
Es difícil eliminar el pasto sin que uno se dé cuenta,
porque se obtendrá media brizna de hierba y se verá raro.
Y hay mucha agua que se puede eliminar antes de que se vuelva obvio.
Entonces, en algún sitio, comenzamos a desinflar el reflejo del globo.
Así que se ve gracioso, pero mucho menos que si todo hubiera tenido
que aplastarse en la misma proporción.
Siempre y cuando guarden la información de lo que borraron,
podrán volver a expandirlo.
Y así, como pueden ver, incluso si lo aplastamos mucho,
este globo aún se verá entero, porque es
muy fácil eliminar píxeles en el cielo, y aun así parecerá
que no hay necesidad de encoger el globo.
El reflejo del globo... eso fue más difícil porque debemos equilibrarlo
cuando eliminemos cosas en el césped.
Por lo tanto, cuesta un poco de trabajo más allá de ese cosido de imagen
y del problema de edición de distancia para hacer este seam carving.
Y cuesta un poco más de trabajo
ser capaz de cambiar el tamaño tanto horizontal como vertical, y eso es genial.
Y esto es algo que se publicó hace diez años en la Conferencia ACM Siggraph, que
es una megaconferencia en computación gráfica que se celebra cada agosto,
quisiera añadir.
Puedo decirles la fecha exacta buscando la referencia,
2007.
Entonces sí, fue hace 10 años.
Y de hecho es un documento bastante corto
y muy fácil de entender, porque es un algoritmo bastante simple
y es una idea realmente genial.
Y entonces es uno de esos documentos raros que
pueden sentarse a leer y entender,
pueden ir a la conferencia y entender.
y después pueden ir a casa.
Y si tienen algo de experiencia en informática,
y particularmente en computación gráfica, pueden entenderlo.
Y pueden sentarse e implementarlo en una hora.
Y entonces obtienen estas demostraciones web que aparecieron.
Photoshop puede hacerlo.
Comenzó a aparecer rápidamente en todas partes,
porque fue una idea excelente.
Funcionó sorprendentemente bien.
Y lo hace a través de esta programación dinámica.
No recalculemos la información sobre cómo llegar desde un punto particular,
cómo eliminar una escena desde allí hasta la parte inferior.
Eso no se calcula para cada posibilidad.
De alguna manera se da cuenta, bueno, desde aquí lo mejor que puedes hacer
sería bajar o lo mejor sería bajar y salir.
Y ese píxel se preocupa por cómo continuar desde ese punto.
¿Alguna pregunta?
OK, bueno, gracias a todos por venir.
Recuerden que mañana por la mañana habrá otra conferencia
transmitida desde Harvard.
Y si están en Harvard, pueden divertirse asistiendo.
Hablaremos de una introducción a Python,
que es otro lenguaje de programación como C.
Tiene una sintaxis ligeramente diferente, pero el mismo trato básico.
Tiende a acostumbrarse a cierto tipo de programas
en los cuales su sintaxis facilita un poco escribirlos.
Entonces obtendrán, en vez de una tarea de programación,
un tipo de ejercicio escrito mucho más divertido
para trabajar durante el fin de semana.
Recuerden su examen.
Se aplicará después de la conferencia de mañana
y tendrán tres días para ello.
La próxima semana en Yale no habrá secciones, debido a las vacaciones de otoño.
Entonces, no habrá secciones el martes o el miércoles.
Tendrán un descanso.
Eso les dará tiempo para recuperarse
y tiempo para dormir.
Y el próximo viernes, que es durante nuestras vacaciones de otoño,
habrá otra conferencia transmitida desde Harvard
donde se hablará más sobre Python.
Y se publicará la serie de problemas 6.
Es probable que vincule muchas cosas de las que hablamos esta semana
y de las que hablaremos la próxima, en una tarea de programación.
Y, por supuesto, tendrán 10 días para trabajar en ello.
Para que pueda verlo cuando terminen, pero no debe
arruinar el resto de sus vacaciones de otoño.
Lo prometo.
Y con eso nos despedimos, gracias de nuevo por venir.
Esto fue CS50.