Los objetivos del curso CI-0110 "Introducción a la computación" son que la persona estudiante pueda:

  1. Explicar los conceptos fundamentales de la disciplina [C].

  2. Resolver problemas mediante diseño matemático y algorítmico [A].

  3. Emplear herramientas informáticas básicas para su formación y ejercicio profesional [H].

  4. Desarrollar un criterio ético ante el impacto de la tecnología en la sociedad [E].

1. Filosofía de la computación

1.1. Computación como disciplina [C]

¿Cuál es el propósito de la disciplina? Es decir, ¿a qué se dedicará el resto de su vida la persona profesional de esta disciplina? ¿Cuál es el método para lograr ese propósito?

Actividad 1. Mi filosofía de la computación [computing_philosophy_mine]

Responda de acuerdo a lo que usted piensa:

  1. ¿Es la computación una matemática? ( ) Sí ( ) No

  2. ¿Es la computación una ciencia? ( ) Sí ( ) No

  3. ¿Es la computación una ingeniería? ( ) Sí ( ) No

De acuerdo a lo que usted piensa: Indique en la siguiente tabla el propósito de cada disciplina en una oración. Indique el método que cada disciplina sigue para lograr su propósito. No indague ni comente con otras personas, simplemente escriba las ideas que le llegan a la mente.

Disciplina Propósito/Objetivo Método

Matemática

Ciencia

Ingeniería

Computación

Actividad 2. Nuestra filosofía de la computación [computing_philosophy_ours]

En parejas discutan y refinen sus propias definiciones, sin indagar en otras fuentes. Una vez refinadas todas las celdas de la tabla, discuten en grupos de cuatro, y así sucesivamente hasta formar un unico grupo. Un delegado transcribe las decisiones en un documento compartido (ej. pizarra).

Actividad 3. Filosofía de la computación del docente [computing_philosophy_prof]

El docente expone su filosofía.

Actividad 4. Filosofía de la computación final [computing_philosophy_final]

Cada estudiante de forma individual, plasma los objetivos y métodos que más le convencieron.

1.2. Estudios universitarios [E]

Los estudios universitarios no son un fin. Nadie estudia para contemplar un título colgado en una pared, ahí terminar y dedicarse a otra cosa. Los estudios son un medio. Los fines para los cuales se estudia una carrera son más trascendentales. Los hay a corto y largo plazo. Ejemplos podrían ser: para cambiar a otra carrera en la que no logré ingresar, para ganar bien, crear una empresa, ser como alguien que admiro, …​

Actividad 5. ¿Por qué escogí computación? [why_study_computing]

Liste las razones o motivaciones por las que escogió estudiar la carrera de computación. Piense en el momento en que tuvo que llenar el formulario con las opciones de carrera. ¿Qué le llevó a escoger computación?

Actividad 6. Mi objetivo académico [my_academic_goal]
  1. ¿Cuál es mi propósito de estudiar computación? ¿Qué meta quiero conseguir?

  2. Imagine que ya en este momento tiene el anhelado título en sus manos ¿qué haría con él? ¿emprendería? ¿continuaría estudiando? ¿saldría del país?

Mercado laboral costarricense e internacional. Sector industria, gobierno, y académico. Empleamiento vs emprendedurismo.

1.2.1. Grados académicos

Actividad 7. Educación universitaria [university_education]

Llene la siguiente tabla con las ideas que le vienen a la mente. Escriba una palabra en cada celda de la segunda columna, que resuma lo que una persona formada en ese nivel universitario es. Naturalmente esa palabra no es "bachiller", "máster", ni "doctor(a)". En la tercera columna puede indicar varias palabras o frases.

Un/a: es la formación de un/a: y se distingue de quien no lo es por:

Bachillerato (Licenciatura)

Maestría

Doctorado

Ejercicio 1 [student_vs_professional]

¿Cuál es la diferencia entre un estudiante y un profesional? Liste rasgos que identifica cada cuál.

¿Cuál es mi responsabilidad y compromiso como estudiante universitario?

1.2.2. Énfasis del bachillerato

Énfasis del bachillerato. Ejemplo con un problema complejo

  1. Ingeniería electrónica (EE)

  2. Ingeniería en computación (CE)

  3. Ciencias de la computación (CS)

  4. Ingeniería de software (SE)

  5. Tecnología de la información (IT)

enfasis computacion

1.3. Lenguajes de marcado ligero [H]

1.3.1. Archivos y editores de texto

Actividad 8. Archivos de texto [text_files]

Escriba las respuestas a las siguientes preguntas en un archivo de texto text_files.txt.

  1. ¿Cuál es la diferencia entre editor de texto (ej.: Notepad, Geany) y procesador de palabras (ej.: Google Docs, Microsoft Word…​)?

  2. ¿Qué es un archivo?

  3. ¿Cuál es la diferencia (informal) entre archivos de texto y binarios?

  4. ¿Cuál es la importancia de los archivos de texto en la disciplina?

Por sencillez, se puede considerar un archivo de texto como una secuencia de caracteres alfanuméricos y de puntuación, sin formato. Hay una amplia oferta de editores de texto, por ejemplo, Notepad++, Sublime Text, Geany, Emacs, y Vi.

Actividad 9. Filosofía de la computación en texto plano [computing_philosophy]

Transcriba lo mejor que pueda una de las tablas de la filosofía de la computación (del grupo, del docente, …​) a un archivo de texto plano. Piense que va a enviar el archivo a otra persona ajena a la discusión y que esa persona pueda comprender que se trata de una tabla. Guarde el archivo con extensión .txt en una carpeta computing_philosophy/.

1.3.2. Ambiente de desarrollo integrado (IDE)

Un ambiente/entorno de desarrollo integrado (IDE, Integrated Development Environment) es un software que facilita la creación de código fuente y otros archivos asociados a la programación de computadoras. Se caracteriza por tener un editor de texto para editar el código fuente, herramientas de construcción para compilar, depurar, y probar dicho código.

Si no lo tiene ya, instale en su máquina personal el Microsoft Visual Studio Code. Se puede trabajar con un archivo directamente, pero lo usual es hacerlo con una carpeta que puede abrir o arrastrar. El IDE mostrará un explorador de archivos y un editor de texto. En el curso lo usaremos principalmente para marcado ligero (Markdown, AsciiDoc), diseño algorítmico (pseudocódigo) y programación (Python).

Actividad 10. Filosofía de la computación en texto plano [computing_philosophy_ide]

Abra la carpeta computing_philosophy/ en el ambiente de desarrollo. ¿Qué diferencias nota respecto al editor de texto? Trate de renombrar el archivo desde el IDE y deshaga el cambio.

1.3.3. Marcardo ligero: Markdown [H]

Un archivo con marcado ligero es un archivo de texto que aplica las reglas de un lenguaje para marcar trozos de texto y darles un significado diferente. Es ligero porque las marcas son poco obstructivas, y permite leer el contenido original con facilidad. Hay varios de estos lenguajes, entre los que se puede citar Markdown por sencillez/popularidad y AsciiDoc por funcionalidad.

Ejemplo de marcas en Markdown: párrafos, títulos, listas, tablas. Énfasis, código fuente, bloques de código fuente.

Actividad 11. Filosofía de la computación en Markdown [computing_philosophy]

Transcriba una de las tablas de la filosofía de la computación a un archivo de texto en formato Markdown. Agregue el propósito del documento, títulos, y resalte las palabras más importantes.

Ejercicio 2 [computing_philosophy]

Complete el documento de filosofía de la computación en Markdown. Convierta todas las tablas elaboradas durante la lección:

  1. Mi filosofía inicial.

  2. La filosofía de mis colegas.

  3. La filosofía del docente.

  4. Mi filosofía final.

Cada tabla debe estar en su propia sección, y por lo tanto, tener un encabezado en el archivo.

Plugin de Markdown para el navegador para visualizar documentos.

Plugin de Markdown para enriquecer texto. Enviar un correo electrónico con el archivo final.

Ejercicio 3 [curriculum_vitae]

Escriba en Markdown su hoja de vida (currículum vitae) que incluya:

  1. Una fotografía tamaño pasaporte de máximo 100kB. Este video explica cómo reducir el tamaño de una imagen con editor libre Gimp.

  2. Datos personales: nombre y apellidos, fecha y lugar de nacimiento.

  3. Datos de contacto: teléfono, correo electrónico personal e institucional, lugar de residencia.

  4. Redes sociales: tu usuario de Telegram, Facebook, …​, tu blog o sitio web personal.

  5. Educación formal: dónde y cuándo obtuviste tu bachillerato en educación primaria y secundaria o cualquier otro título.

  6. Experiencia laboral: puesto, dónde y rango de fechas, si has trabajado por alguna remuneración.

  7. Idiomas que conoces y su nivel (ej.: inglés: escrito avanzado, conversacional intermedio).

  8. Conocimientos técnicos: software especializado, lenguajes de programación que dominas.

  9. Intereses: áreas de la computación u otra disciplina en los que te gustaría trabajar.

  10. Habilidades y pasatiempos: habilidades artísticas, deportes, aficiones, gustos, que reflejen tu dimensión humana.

Nota: Trate de que su currículum sobresalga. Resalte méritos, premios, u otras características que hablen de su talento. Recuerde que su currículo será simplemente un documento más entre muchos otros candidatos buscando un puesto u otro fin de su interés. Sobresalir incrementa la probabilidad de ser considerado(a), pero siempre siendo una persona honesta, humilde, y sobre todo, profesional. Tome en cuenta el contenido y la forma en que se expresa en sus intervenciones en redes sociales podría comprometer el profesionalismo que expresa en su currículo.

1.3.4. Expresiones regulares [H]

Convertir las tablas de Google Docs a Markdown

2. Resolución de problemas

2.1. Ejercicios vs problemas [A]

Un problema es una situación vivencial desventajosa y las acciones para cambiarla a una situación favorable (modelo solución) no son inmediatamente obvias. Hay que averiguar o construir este modelo, lo cual genera un bloqueo mental. Se diferencia de un ejercicio que es una situación para la cual se tiene un modelo solución, el cual simplemente se aplica o “ejercita”.

Actividad 12. Misioneros y caníbales [missionary_cannibals]

Tres misioneros y tres caníbales deben cruzar un río. Hay un bote pequeño que no puede contener más de dos personas. Si llegara a ocurrir que a un lado del río hayan más caníbales que misioneros, incluyendo los que están en el bote, los caníbales devorarán a los misioneros. Su propósito es encontrar el itinerario más sencillo que permita a todos los misioneros y caníbales cruzar el río de forma segura usando el bote. Suponga que todos los pasajeros desembarcan siempre al llegar a la otra orilla y que debe haber al menos una persona en el bote para cruzarlo de un lado al otro del río.

Actividad 13. Números enemistados [enemy_numbers]

Los números del 1 al 9 reservaron un hotel con nueve habitaciones dispuestas como muestra la figura de abajo. Sin embargo, antes de llegar al hotel ocurrió un altercado entre los números, de tal forma que tanto los números consecutivos (ejemplo 6 y 7), como los divisores y múltiplos excepto 1 (ejemplo 3 y 6) quedaron enemistados. Ellos no están dispuestos a dormir en una habitación que tenga por vecino (en cruz o diagonal) a uno de sus enemigos. ¿Puede ubicar los nueve dígitos del 1 al 9 en el hotel de tal forma que puedan dormir tranquilos?

enemy numbers
Ejercicio 4 [hanoi3]

Torres de Hanói es un juego matemático inventado en Francia en 1883. Su objetivo es trasladar la torre ubicada en la primera columna (marcada A en la ilustración) a la tercera columna (marcada C). La torre consta de discos de diferente tamaño. Sólo se puede mover un disco a la vez de una columna a otra. Nunca se puede colocar un disco más grande encima de otro más pequeño.

hanoi3 initial

Elabore un modelo solución que contenga todos los estados y movimientos válidos para trasladar una torre de tres discos. Provea una simbología que ayude a otras personas a entender su modelo solución.

Ejercicio 5 [jealous_husbands]

Tres esposos celosos y sus esposas deben cruzar un río para llegar a su hotel. Hay un bote pequeño que no puede contener más de dos personas. Encuentre el itinerario más sencillo que permita a las seis personas cruzar el río. Dados los incontrolables celos de los esposos, ninguna mujer puede estar en compañía de otros hombres, a menos de que su celoso esposo esté presente. Suponga que todos los pasajeros desembarcan siempre al llegar a la otra orilla y que debe haber al menos una persona en el bote para cruzarlo de un lado al otro del río.

Elabore un modelo solución que contenga todos los estados y movimientos válidos para trasladar a las parejas al lado del hotel. Provea una simbología que ayude a otras personas a entender su modelo solución.

Ejercicio 6 [mosters_globes]

Tres extraterrestres de tamaño pequeño, mediano, y grande, cayeron aparatosamente en la Tierra. Cada uno controla un globo cuántico de su mismo tamaño. Sin embargo, por la turbulenta caída, los globos se desacomodaron y el monstruo pequeño tiene el globo grande, el monstruo mediano tiene el globo pequeño, y el monstruo grande tiene el globo mediano.

Ilustraciones de monstruos por Nook Fulloption, Kantor Tegalsari, y tk66. The Noun Project (CC BY 3.0) con fines académicos

Aunque el problema es muy fácil para humanos, las reglas extraterrestres son muy estrictas:

  1. Sólo un globo puede ser transferido a la vez.

  2. Un extraterrestre podría sostener cero, uno, o máximo dos globos.

  3. Si un extraterrestre tiene dos globos, sólo puede transferir el más grande.

  4. No se puede transferir un globo a un monstruo que tiene un globo más grande.

¿Que secuencia de transferencias lograría que cada extraterrestre tenga el globo acorde a su tamaño? Elabore un modelo solución que contenga todos los estados y transferencias válidas. Provea una simbología que ayude a otras personas a entender su modelo solución.

2.2. Proceso de resolución de problemas [A]

Proceso de resolución de problemas
Figura 1. Proceso de resolución de problemas

2.3. Análisis [AC]

Comprender el problema. Complejo cuando uno no está familiarizado(a) con el contexto.

Producto: documentos de requerimientos.

Prueba: verificación con los usuarios/clientes.

Generar casos de prueba

2.4. Diseño [AC]

Arquitecto que construye un edificio sin planos. Cambian los requerimientos.

Resolver el problema con artefactos baratos. Pueden ser artefactos de invención propia, o si son definidos por un colectivo, depende del paradigma.

Producto: modelos o diseños.

Prueba: Rastrear el modelo con casos de prueba y confirmar que realiza o produce el resultado deseado.

2.5. Implementación [AC]

Traducir los modelos

2.6. Implantación [AC]

2.7. Métodos de resolución de problemas [A]

2.7.1. Prueba y error

2.7.2. Descomposición (divide-y-vencerás)

2.7.3. Analogía

2.8. Control de versiones [H]

Concepto de control de versiones y sus beneficios.

Metáfora del dragón y el castillo de Git
Figura 2. Metáfora del dragón y el castillo de Git

Crear un repositorio para el curso, y cada curso de la carrera

Agregar todos los archivos de Markdown hechos hasta el momento (filosofía de la computación).

3. Paradigmas de computación

3.1. Adopción o programación [A]

Buscar soluciones existentes primero: adopción de herramientas de software

Definición de programación

Reutilizar recursos siempre dando el crédito cuando corresponde.

3.2. Diseño humano [A]

No se puede instruir una máquina si uno no puede resolver un problema con los recursos que uno dispone. Sin las limitaciones de la máquina

Recursos: papel, plastilina, legos, matemática…​

Resolver algunos problemas funcionales/matemática discreta.

3.3. Recursos de la máquina computacional [C]

Procesamiento

Almacenamiento

Comunicación

3.4. Paradigmas de computación/programación [C]

Definición de paradigma de computación/programación.

Paradigmas, conceptos, y lenguajes de programación. Relación entre ellos.

Relaciones entre paradigmas

Computación/programación imperativa vs declarativa

Computación/programación funcional

Actividad 14. Apretones de mano [handshake_count]

En una convención se encuentran 20 personas. Si cada asistente saluda a todos las demás con un apretón de manos: (1) ¿Cuántos apretones de mano hubo en total?

(2) Si en la convención se encuentran \(n\) personas, diseñe un modelo que encuentre la cantidad total de apretones de mano que se efectuarán.

(3) Si por el contrario, se sabe que en la convención se efectuaron \(h\) apretones de mano, diseñe un modelo que indique la cantidad de personas que participaron en el evento.

Computación/programación procedimental

Actividad 15. El ejército cruza el río [soldiers_boys_river]

En su camino, una tropa de infantería se encuentra con que el puente sobre un profundo río fue destruido, y cerca de él, un par de niños juegan en su bote de remos. El bote es tan pequeño que sólo soporta el peso de un soldado o de dos niños. Pese a esto, la tropa logró cruzar el río. ¿Cómo lo lograron? Si la tropa consta de \(n\) soldados, diseñe un modelo que resuelva el problema.

Computación/programación genérica

Computación/programación orientada a eventos

Computación/programación orientada a objetos

Computación/programación concurrente

Computación/programación distribuida

Computación/programación lógica

Computación/programación metaprogramación

Problemas complejos: combinación de los paradigmas

Metáfora de la caja de herramientas. Ejemplo del albañil que sólo usa martillo.

Ejercicio 7 [t_area_pattern]

La ilustración de abajo muestra las primeras cuatro figuras de una sucesión infinita. Cada cuadro de la rejilla es una unidad cuadrada de área. Diseñe un modelo que permita determinar: (a) el área de la i-ésima figura, (b) la orientación de i-ésima figura. Sugerencia: cree una tabla que le ayude a encontrar un patrón.

t area pattern

Use su modelo para determinar el área y orientación de la figura 100.

Ejercicio 8 [ecci_directions]

Suponga que usted se compromete a ayudar a una persona ciega que recién ha ingresado a la carrera de computación en la Universidad de Costa Rica. Cuando esta persona no encuentra ayuda en los edificios de la ECCI, le escribe un mensaje de texto que indica dónde se encuentra ella y hacia adónde se dirige. Para cada uno de los siguientes casos, escriba el mensaje de respuesta que usted le brindaría a esta persona para que alcance su destino. Haga su mensaje tan detallado como pueda. Si lo necesita haga el recorrido físico.

Caso Origen Destino

1

Auditorio de la ECCI

Recepción del CITIC

2

Recepción del CITIC

Oficina del profesor Edgar Casasola

3

Laboratorio 305 del edificio anexo

AECCI

4

CASE

Taller de equipo

3.5. AsciiDoc [H]

Hacerle un readme al repositorio

Tabla de contenidos

4. Diseño procedimental: algoritmos

Un algoritmo es un conjunto ordenado de instrucciones efectivamente computables no ambiguas que se ejecutan en un tiempo finito y producen un resultado. Un algoritmo correcto es el que produce el resultado esperado para resolver un problema. El resultado debe ser observable, podría ser un número, un texto, imagen, o un cambio en el medio. Un algoritmo eficiente es el que produce el resultado usando una cantidad razonable de recursos de la máquina: tiempo (procesamiento), espacio (memoria), y comunicación (entrada/salida).

Una instrucción es no ambigua si puede ser comprendida y ejecutada directamente por el agente computacional sin necesidad de más simplificaciones o explicaciones [Schn19]. Los agentes disponen, de nacimiento o de fábrica, de un conjunto de instrucciones primitivas que son por naturaleza no ambiguas. Para una máquina, la cantidad de instrucciones primitivas son reducidas, no así para un humano. Las instrucciones primitivas varían también de máquina en máquina de la misma forma que de una persona a otra. De esta forma, un algoritmo para un agente podría no ser apto para otro.

Sobre las instrucciones primitivas se escriben algoritmos que pasan a formar parte del banco de operaciones que el agente puede realizar. Nuevos algoritmos pueden escribirse en función de las instrucciones primitivas, de los algoritmos ya conocidos por el agente, o ambas. Esta poderosa capacidad no sólo permite reutilizar código, sino resolver problemas más complejos.

Una instrucción es efectivamente computable si el agente, además de comprenderla, es capaz de realizarla. Por ejemplo, si a usted se le pide saltar con sus pies del suelo al techo de la Torre de Pisa, leer La Biblia completa en una hora, o ganarle diez partidas seguidas de ajedrez al motor de Stockfish, son instrucciones comprensibles, no ambiguas, pero incapaces de ser ejecutadas por un humano.

Las instrucciones se escriben en la conjugación verbal imperativa.

Origen del concepto: Abu Ja’far Muhammad ibn Musa Al-Khwarizmi (780-850?) profesor de matemáticas en Baghdad cuando el imperio Persia se convirtió en punto de encuentro de pensadores y científicos. En el año 820 escribió Kitab al jabr w’al muqabala (El libro conciso de cálculos por reducción) que se convirtió en el libro de texto de matemáticas más usado en Europa. El término "reducción" en árabe se escribe al jabr que se occidentalizó como "álgebra". En el año 825 Al-Khwarizmi escribió otro libro sobre el sistema número posicional en base 10 recién desarrollado en la India. Al-Khwarizmi fue muy cuidadoso de incluir procedimientos para realizar las operaciones en este nuevo sistema numérico. Al traducir el libro al latin, su apellido quedó Algoritmi y en su honor, los procedimientos contenidos en el libro se comenzaron a llamar "algoritmos". Por lo tanto, el término "algoritmo" es un epónimo.

4.1. Algoritmos para agentes humanos [A]

Actividad 16. Algoritmo para bañarse [shower_algorithm]

Diseñe un algoritmo para que una persona que lo ejecute pueda efectivamente bañarse.

Ejercicio 9 [rice_algorithm]

Diseñe un algoritmo para que una persona que lo ejecute pueda efectivamente cocinar arroz blanco usando una olla arrocera. Su receta debe ser original, es decir, su forma propia de conocinar arroz. Si no ha cocinado arroz antes, averigüe la receta en su hogar. No indague en internet u otras fuentes.

Escriba el procedimiento de forma reproducible, pensando en que lo enviará a alguien que lo necesita o usted mismo(a) en el futuro. Provea precisión en medidas, tiempos, y otros detalles. Suponga que el ejecutante sabe realizar operaciones comunes de cocina. Por ejemplo, basta indicar "pique el chile dulce en tres rodajas de 0.5cm de grosor" sin educar al ejecutante en cómo se realiza la operación de "picar".

Actividad 17. Le adivino el número [guess_number_trick]

Haga pareja con otra persona. Una persona tomará el rol de mago y la otra el rol de modelo. La persona en rol de mago realiza los siguientes pasos (de preferencia memorizárselos y agregar dramatismo y sonidos de suspenso). La persona modelo no debe conocer el truco y hará lo que el mago dice. Ambos pueden usar una calculadora si lo prefieren.

  1. [Mago dice:] Piense un número positivo de tres dígitos cualquiera.

  2. Duplique los dígitos, por ejemplo, 123 se convertiría en 123123.

  3. Divida el número de seis dígitos por 7, un número de buena suerte.

  4. Divida el número que obtuvo por 13, un número de mala suerte.

  5. ¿Pero por qué hace esa cara? ¿Qué cosa rara de número le dejó la mala suerte?

  6. [Mago divide el número que le dieron entre 11, el resultado será <X>]

  7. ¡Chan-chan-chan-chan Mis poderes dicen que usted pensó en el número <X>!

El agente que ejecuta un algoritmo (persona o máquina), llamado agente computacional, no necesita comprender el problema, ni las decisiones de diseño que llevaron a la formulación del algoritmo.

No todo problema se puede resolver algorítmicamente, incluso aunque parezcan factibles. Un ejemplo clásico el el problema de la parada, que consiste en construir un algoritmo que indique si otro algoritmo se detendrá en algún momento. Teóricamente es imposible construir este algoritmo. Este tipo de problemas se les conoce como problemas no resolubles {unsolvable problems}.

Existe otro conjunto de problemas para los cuales teóricamente se puede construir una solución algorítmica, pero su ejecución consume tantos recursos que incluso la super computadora más potente que disponga la humanidad tardaría millones de años en poderlos ejecutar. A estos problemas se les llaman problemas intratables.

Un ejemplo de un problema intratable es una solución que construya todo el espacio de movimientos (jugadas) del Ajedrez, lo cual sería el algoritmo que gane todos los juegos en que compita. Con una oportunidad promedio de 40 jugadas posibles por turno y 30 turnos para que el juego concluya, la cantidad aproximada de movimientos es un árbol de \(40^30 ~ 10^48\) jugadas. Una supercomputadora que pueda analizar mil billones de jugadas en un segundo (\(10^15\)) tardaría \(\frac{10^48}{10^15}=10^33s=3.17 \cdot 10^25 \text{años}\), es decir, aproximadamente 32 cuatrillones de años en hacer su primer movimiento.

Existe un último grupo de problemas que se conjetura pueden resolverse algorítmicamente, pero nadie ha intentado o logrado elaborar uno. Algunas actividades que son naturales para los seres humanos, pero realmente difíciles para las computadoras entran en esta categoría, como la habilidad de generar humor.

4.2. Algoritmos para máquinas computacionales [A]

La máquina no tiene tanta libertad como los humanos. Sus capacidades son limitadas a los tres tipos de recursos. De ellos se definen ocho tipo de instrucciones:

Tabla 1. Los ocho tipos de instrucciones procedimentales
Recurso # Instrucción Ejemplos en pseudocódigo

Comunicación

1

Leer valor(es)
input, scan: texto con formato
read: puede preferirse para binario

input nombre, masa, altura
input const increment, mutable salary

2

Imprimir valor(es)
print, output: texto con formato
write: puede preferirse para binario

print nombre, "su imc es", masa / altura^2
print `{nombre} su imc es {masa / altura^2:.2}`

Almacenamiento

3

Crear una variable con un valor
declare var := value
declare constant = value
mutable var := value
const constant = value

const G = 6.67e-11
declare imc := masa / altura^2
declare sum as real := 0
declare ptr := @sum
declare arreglo := [-1, 0, 1]
declare matrix := 0.0[3][4]
mutable registro := {nombre: "Ana", edad: 34}
const christmas = Date{25, DEC, 0000}

4

Cambiarle el valor a una variable
var := expr

altura := altura / 100
arreglo[3] := 3 * arreglo[2] + 1

Procesamiento

5

Condicionar instrucción(es)
if-then-else, case-of

if cantidad > 0 then
  promedio := suma / cantidad
else
  print "no hay datos"
end if

case answer of
  'y': save_changes()
  'n': discard_changes()
end case

6

Repetir instrucción(es)
for (ciclo por contador)
cuando se conoce la cantidad
de repeticiones.
while (ciclo por condición)
cuando no se conoce la
cantidad de iteraciones.
foreach (ciclo por colección)
recorre todos los elementos
de una colección.

read count
declare values := 0.0[count]
for index := 0 to count do
  read values[index]
end for

read number
while number != 0 do
  print mod(number, 10)
  number := div(number, 10)
end while

foreach value in values do
  print 1.13 * value
end foreach

Descomposición

7

Realizar una subtarea
subr(args)

const h = hypot(cathetus1, cathetus2)
print_angles(h, cathetus1, cathetus2)

8

Definir una subtarea:
function es una pregunta
(siempre retorna un valor)
sin efectos secundarios
procedure es una orden
(puede o no retornar un valor)
con efectos secundarios en el
estado del programa.

function celcius(fahrenheit)
  return 5 / 9 * (fahrenheit - 32)
end function

procedure main()
  while there are data do
    read fahrenheit
    print celcius(fahrenheit)
  end while
end procedure
Tabla 2. Sintaxis especiales
Sintaxis Ejemplos en pseudocódigo

Definir tipos de datos
No genera instrucciones ejecutables

enumeration Month of
  JAN = 1, FEB, MAR, APR, ..., OCT, NOV, DEC
end enumeration

record Date of
  day: unsigned
  month: Month
  year: unsigned
end record

Expresiones
Combinación de operadores y operandos
que se evalúa en un valor. Importa
aridad, precedencia, y asociatividad
de los operadores

+   add, concatenation
-   negative, substraction
*   multiplication, pointer desreferencing
/   float-division
^   exponentiation, also **, pow(b,e)
@   address of, pointer-declaration
:=  assignment

=   equals
!=  different than, also ≠
<   less-than
<=  less-than or equals, also ≤
>   greater-than
>=  greater-than or equals, also ≥

~   bitwise not (one's complement)
&   bitwise conjunction
|   bitwise conjunction
xor bitwise exclusive-or
<<  left-shift
>>  right-shift

not logical negation
and logical conjunction
or  logical disjunction

div(a,b) fixed-division
mod(a,b) modulus, remainder, also %
log(x,b) logarithm of x in base b (default b=10)
sqrt(x)  square-root

Constantes literales
Valores escritos en el código

350     Decimal fixed-point
0xAF    Hexadecimal fixed-point
0777    Octal fixed-point
0.0     Decimal fixed floating-point
2e-3    Decimal exponential floating-point

'a'     A single character
"yes"   Text, string of characters
`2{i}`  Formatted string with interpolation

[]      Empty array
[2, 7]  Literal array
{}      Empty record
{a: 2}  Record, associative-array, map, dictionary

Manejo de archivos

  1. open: abre un archivo para sólo-lectura (reading), escritura (writing), o agregar al final (append).

  2. close: cierra un archivo.

  3. seek: reubica el cursor a un byte específico (posición).

  4. input, output: lee y escribe valores de/hacia el archivo.

  5. scan, print: lee y escribe texto.

  6. read, write: lee y escribe en binario.

open input_file from "path/file.csv" for reading
open output_file from "path/file.tsv" for writing
while there is data in input_file do
  input line from input_file
  declare csv_values[] = split(line, ",")
  declare tsv_line = join(values, "\t")
  output tsv_line to output_file
end while
seek input_file to 0
input header from input_file
close input_file
close output_file

Un valor es un elemento de un conjunto de datos. Por ejemplo, -5 es un valor del conjunto de números enteros. Una expresión es una combinación de operadores y operandos que se evalúa en un valor. Por lo tanto, las instrucciones que trabajan con un valor, puede este cambiarse por una expresión.

Las tareas/subtareas (rutinas/subrutinas) es el mecanismo funcional y procedimental de descomponer, modularizar, y abstraer grandes problemas en otros unidades más pequeñas y reutilizables desde diferentes contextos.

Las primitivas de comunicación (primer grupo de la tabla) son los archivos (una secuencia de bytes que se accede con cursores de entrada o salida) y los eventos. Los archivos ofrecen operaciones de entrada (input o read), salida (output o write), posicionamiento (seeking), apertura (open), cierre (close), y manejo de errores (eof). Los eventos pertenecen al paradigma de computación dirigida por eventos (event-driven programming). A nivel de algoritmos se podrían incorporar la primitiva de registro de eventos (when).

La memoria (almacenamiento, segundo grupo de la tabla) se organiza en cuatro primitivas: variables, indirección (direcciones de memoria o punteros), arreglos (array), y registros de memoria (record).

Ejercicio 10 [alg_pseudo]
  1. Lea dos números imprima su suma.

  2. Lea dos números imprima el menor.

  3. Dadas las respuestas de dos jugadores de piedra-papel-tijera, indique cuál de los dos ganó.

  4. Lea los enteros a y b. Imprima todos los enteros entre {a, …, b}

  5. Lea n, luego n números, imprima la suma.

  6. Imprima el mayor de n números.

  7. Imprima el promedio de n números.

  8. Lea todos los números que haya en la entrada, imprima el promedio.

  9. Lea b y h. Imprima un rectángulo relleno de b x h asteriscos.

  10. Igual a 8, pero imprima el rectángulo no relleno (sólo el borde).

  11. Lea naturales y para cada uno de ellos imprima si es primo o no.

  12. Imprima la mediana de n números.

Actividad 18. Índice de masa corporal [bmi]

La obesidad es una de las enfermedades más alarmantes del presente y futuro de la humanidad. Actualmente mueren casi 3 millones de adultos por año a causa de la obesidad y el sobrepeso. Para dar un punto de comparación, si esta enfermedad atacara a todos los ticos, en año y medio acabaría con toda la población del país.

La forma convencional de medir la obesidad es a través del índice de masa corporal (IMC) (en inglés BMI de body mass index), que relaciona la masa m en kilogramos de la persona con su altura h en metros, según la ecuación:

\(bmi = \frac{m}{h^2}\)

La Organización Mundial de la Salud definió cuatro estados nutricionales de acuerdo a la siguiente tabla:

Resultado

Estado nutricional

BMI category

bmi < 18.5

infrapeso

underweight

18.5 ≤ bmi < 25

normal

normal

25 ≤ bmi < 30

sobrepeso

overweight

30 ≤ bmi

obesidad

obese

En un centro de observación de la obesidad, se tienen registros de varios indicadores de salud, entre ellos el peso (en kilogramos) y altura (en centímetros), de millones de personas. Se quiere un programa que calcule y reporte el índice de masa corporal y el estado nutricional para cada registro.

Ejemplo de entrada:

57.5 149
58 169
15.8 113
70.6 192
-68.9 0
120 175

Ejemplo de salida:

 57.50 149.00 25.90 overweight
 58.00 169.00 20.31 normal
 15.80 113.00 12.37 underweight
 70.60 192.00 19.15 normal
-68.90   0.00 invalid data
120.00 175.00 39.18 obese

Debido a problemas de calidad de datos, hay algunas masas y alturas registradas como cero o negativas, o valores exagerados, presumiblemente por errores de digitación. Estas dos medidas siempre deben ser positivas, la altura no puede superar los 300cm y la masa los 1000kg. En caso contrario, se debe indicar invalid data para ayudar al observatorio a detectar esos casos.

Ejercicio 11 [hour_order]

Un sistema debe trabajar con registros en el orden en que ocurrieron. Sin embargo, las horas en que ocurrieron se guardaron con notación a.m y p.m.. El sistema necesita saber para un par de horas en este formato que se saben ocurrieron el mismo día, ¿cuál de ellas ocurrió primero?

Ejemplo de entrada
11:35AM 02:58PM
03:25PM 02:01PM
12:00AM 00:00AM
Ejemplo de salida
11:35AM < 02:58PM
03:25PM > 02:01PM
12:00AM = 12:00AM
12:00AM > 12:00PM

El sistema recibe las horas, dos por línea, en formato HH:MM seguido por AM o PM siempre en mayúsculas. El sistema debe indicar en la salida las mismas dos horas en el mismo orden en que fueron ingresadas, pero separadas por = si las dos horas son la misma, < si la primera hora ocurrió antes que la segunda hora, y > si la primera hora ocurrió después que la segunda hora.

Diseñe una solución procedimental tanto en pseudocódigo (un archivo .pseudo) como en un diagrama de flujo (una imagen vectorial .svg).

4.3. Pseudocódigo [AC]

Entrada y salida [AC]

Variables y expresiones (cómputo) [AC]

Control de flujo/secuencia [AC]

Tareas/subrutinas/modularización [AC]

4.4. Diagramas de flujo [AC]

4.5. PSeint [H]

Ejercicio 12 [fraction_simplify]

Cerca de 300 años antes de Cristo, se escribió uno de los algoritmos más antiguos que sigue en uso actual. Es es el Algoritmo de Euclides para calcular el máximo común divisor de dos enteros positivos. Aproveche este conocimiento para crear un programa que lea una fracción y la imprima de forma simplicada:

  1. Cree en PSeInt un programa que lea una fracción compuesta de dos números enteros: un numerador y un denominador y la imprima en notación numerador/denominador, por ejemplo 18/12.

  2. Como es costumbre en matemáticas, las fracciones se tratan en forma simplificada. Para simplificar una fracción basta con dividir tanto su numerador como su denominador por el máximo común divisor (MCD).Suponga que tiene una función que calcula el MCD y úsela para imprimir la fracción simplificada.

  3. Implemente la función que calcula el máximo común divisor. Use una versión iterativa del algoritmo de Euclides (es decir, tiene un ciclo while entre las provistas por la Wikipedia).

  4. Implemente en otra función la variante recursiva del algoritmo de Euclides, usando la definición matemática de abajo (también provista por la Wikipedia). Use la nueva función en lugar de la iterativa y compruebe que logra simplificar la fracción correctamente.

    \[\text{gcd}(a,b)=\begin{cases}a & \text{ if } b=0 \\\\ \text{gcd}(b, a \bmod b) & \text{ if } b \neq 0 \end{cases}\]
  5. Las fracciones pueden tener numerador, denominador, o ambos negativos, por ejemplo -18/12. ¿Logra su programa simplificar una fracción con negativos? En caso contrario, estudie o rastree el comportamiento del algoritmo de Euclides con negativos y haga las correcciones a su programa para que pueda simplificar con negativos.

  6. En matemática no se suele colocar negativos en el denominador. Mejore su programa para que normalice los negativos, de tal forma que expresiones como: 18/-12 se simplifique a -3/2, y -18/-12 a 3/2.

  7. Por definición una fracción no puede tener denominador nulo. Su programa no debe aceptarla. Imprima un mensaje de error al usuario si intenta ingresar una fracción con denominador nulo en lugar de mostrarle una fracción simplificada incorrecta.

4.6. Rastreo de algoritmmos

4.7. Análisis de complejidad

Actividad 19. Comparación de los grados de magnitud [order_comparison]

Use una hoja de cálculo para comparar el comportamiento de los órdenes de magnitud \(O(lg n)\), \(O(n)\), \(O(n^2)\), \(O(n^3)\), \(O(2^n)\), y \(O(n!)\). Trate de representar el comportamiento con algunos valores de \(n\), al menos entre 0 y 100.

Elabore un gráfico donde el eje-x corresponde a \(n\) y el eje-y a la cantidad de recursos que debe consumir la máquina para ese \(n\). Cada función de orden corresponde a una serie en el gráfico.

Suponga que una computadora puede ejecutar una cantidad de instrucciones por segundo, indicada en una celda de la hoja de cálculo. Para cada valor de las funciones, calcule cuánto tiempo le tomaría a la máquina ejecutarlas. Haga un segundo gráfico que refleje el tiempo.

5. Calculadoras

Las calculadoras son las predecesoras a las computadoras. Se pueden considerar la pre-historia de la computación.

Ejercicio: hacer una línea de tiempo.

5.1. Calculadoras mecánicas

El ábaco: representa el estado del programa, la persona es la CPU. La persona ejecuta procedimientos o algoritmos para realizar las operaciones.

El incremento de la investigación científica en el renacimiento, que requería el cómputo de cálculos aritméticos más complejos y precisos, pudo ser la motivación de automatizarlos mediante máquinas [Schn19]. Hubo un considerable número de máquinas calculadoras mecánicas que se desarrollarían hasta la aparición de la calculadora electrónica hacia 1970. En lo siguiente sólo se listan algunas.

La Pascalina (Francia) Video de la máquina. Podía hacer sumas y restas. Concepto de acumulador, desborde, aritmética de precisión de punto fijo vs matemática entera.

Gottfried Leibnitz (Alemania) se interó en el trabajo de Pascal y otros. Construyó dos calculadoras capaces de multiplicar. Su innovación se debe a un dispositivo al que llamó La rueda de Leibnitz (Leibnitz’s Wheel) creado en 1673 y que se usaría en las calculadoras mecánicas hasta su extición hacia la década de los 1970.

Se diferencian de las computadoras en que las calculadoras no son programables.

5.2. Concepto de acumulador

En la Pascalina, cada engranaje representa un dígito en base 10. Naturalmente, la Pascalina no puede representar números de una cantidad infinita de dígitos, si no de tantos dígitos como engranajes tenga. Si una pascalina tiene w engranajes, se dice que es una Pascalina de w digitos. El concepto de Pascalina en este contexto sigue siendo válido en las computadoras modernas, pero recibe el nombre de acumulador (en inglés, accumulator) o registro de CPU (en inglés, register). Naturalmente las computadoras modernas no utilizan engranajes sino transistores, pero el concepto sigue siendo el mismo.

Actividad 20. Algoritmo de suma de dos números en precisión arbitraria
  1. Sea \$a\$ el primer número a sumar, compuesto de \$w\$ dígitos: \$a_{w-1}, a_{w-2}, ..., a_1, a_0\$ tal que el dígito \$i\$ tenga un valor posicional de \$a_i \cdot 10^i\$.

  2. Sea \$b\$ el segundo número a sumar, compuesto también de \$w\$ dígitos \$b_{w-1}, b_{w-2}, ..., b_1, b_0\$ con las mismas propiedades que \$a\$.

  3. Sea \$r\$ la variable que almacenará el resultado de la suma \$r = a + b\$, compuesto de máximo \$w + 1\$ dígitos \$r_w, r_{w-1}, r_{w-2}, ..., r_1, r_0\$ con las mismas propiedades de los dos anteriores.

  4. Sea \$c := 0\$ la variable "acarreo" de la suma de dos dígitos.

  5. Para \$i := 0\$ hasta \$w\$ haga:

    • Sea \$s = a_i + b_i + c\$ la suma parcial con sus correspondientes dígitos \$s_1, s_0\$.

    • \$r_i := s_0\$

    • \$c := s_1\$

  6. Si \$c > 0\$ entonces:

    • \$r_w := c\$*

  7. El resultado de la suma estará en \$r\$.

Suma de dos números en precisión de punto fijo. En un aritmética de precisión de punto fijo, la instrucción 6.a del algoritmo anterior, cambiaría por "encienda bombillo de desbordamiento (overflow)".

Actividad 21. Sumador de n dígitos [n_digits_adder10]

Diseñe un algoritmo para máquinas que permita sumar dos números de hasta w dígitos. El programa lee la cantidad de dígitos y luego dos números de máximo esa cantidad de digitos. Calcula la suma y la imprime. Ejemplo de entrada:

2
37
28

Ejemplo de salida

65

Si el número resultado ocupa

Ejercicio 13 [bin_to_dec]

Escriba un algoritmo en PSeInt que convierta números en binario a su representación en base 10. El programa debe leer un texto que representa los dígitos (bits) del número binario. Recorra todos los dígitos del texto y ensamble un número en base 10 usando multiplicaciones (o potencias) y sumas. Imprima el resultado en pantalla. Por ejemplo:

Número en binario: 10
10 = 2
Número en binario: 11010011010
11010011010 = 1690
Número en binario: 0

El programa se mantiene solicitando números al usuario hasta que se ingresa 0.

Ejercicio 14 [dec_to_bin]

Escriba un algoritmo en PSeInt que convierta números enteros a su representación en binario usando divisiones repetidas y tomando los residuos. Recuerde que los residuos se obtienen en el orden inverso al que deben imprimirse. Suponga que los números son siempre positivos y no superan los 15 dígitos en base 10. Use un arreglo de 64 números para almacenar los bits del número convertido a binario que luego usará para imprimir. Ejemplo de ejecución:

Número en decimal: 10
10 = 1010
Número en decimal: 2022
2022 = 11111100110
Número en decimal: 0

El programa se mantiene solicitando números al usuario hasta que se ingresa 0.

Ejercicio 15 [bin_table]

Escriba un programa que reciba dos números que representan el inicio y fin de un rango entero, e imprima una tabla de conversión decimal a binario de todos los números en ese rango. Ejemplo de ejecución:

Primer valor del rango: 10
Último valor del rango: 20

10 = 1010
11 = 1011
12 = 1100
13 = 1101
14 = 1110
15 = 1111
16 = 10000
17 = 10001
18 = 10010
19 = 10011
20 = 10100

Suponga que los números que ingresa el usuario son siempre positivos y nunca superan los 15 dígitos decimales. No es necesario que el programa se mantenga repetitivamente imprimiendo tablas.

5.3. El telar de Jacquard [E]

El telar de Joseph Jacquard primera máquina programable para automatizar la creación de tejidos que era una labor muy lenta y propensa a fallos. 1801. El dispositivo innovador fue el uso de tarjetas perforadas que indicaban si un hilo iba por encima o debajo del tejido. Forman un patrón. Cada tarjeta programa una fila del patrón. Varias tarjetas se conectan entre ellas y el telar las procesaba en secuencia. Primer dispositivo en usar código binario?

Impacto en la sociedad. Personas por temor a perder su puesto de trabajo, formaron agrupaciones que violentamente se opusieron a los cambios tecnológicos. Un ejemplo fueron los Luddites, liderados por Ned Ludd Nottingham en Inglaterra, quienes quemaron fábricas donde se usaban telares de Jacquard. Es el inicio de la tecnofobia [Schn19].

5.4. Las máquinas diferencial y analítica

Charles Babbage era hijo de un bancario en Inglaterra. Estudió matemáticas y se convirtió en profesor de esta disciplina en la Universidad de Cambridge. En las dos anteriores el operador interpreta el resultado que es la posición final de los engranajes. Babbage visionó que la calculadora imprimiera el resultado para evitar errores de interpretación.

Actividad 22. Sumador de cuatro dígitos [three_digits_adder]

Diseñe un algoritmo para sumar dos números en base 10 de máximo tre dígitos. Piense que el resultado puede ser de cuatro o menos dígitos.

Actividad 23. Sumador de cuatro dígitos con desborde [three_digits_adder_overflow]

Adapte su algoritmo del sumador de tres dígitos para que el resultado siempre se produzca en un número también de tres dígitos. Descarte siempre el acarreo en la cifra más significativa. Rastree su algoritmo e indique qué resultado produce con la expresión 708 + 524?

Actividad 24. Sumador generalizado [arbitrary_precision_adder]

Adapte su algoritmo para que pueda sumar dos números en base 10 de cualquier cantidad de dígitos. A este tipo de matemática se le conoce como aritmética de precisión arbitraria.

¿Por qué escribir procedimientos que sabemos hacer informalmente de esa manera tan complicada? Porque si podemos especificar formalmente un algoritmo para resolver un problema, entonces podemos automatizar su solución. Es decir, codificarlo en un lenguaje de programación, convertirlo en un programa de computadora, de tal forma que pueda ser ejecutado con las ventajas de velocidad, almacenamiento, y comunicación que esta dispone.

6. Aritmética de precisión de punto fijo

La aritmética de precisión de punto fijo son aproximaciones finitas de los números enteros de los seres humanos.

Tabla 3. Posibles representaciones de todos los valores en un acumulador de w=4 bits

Accumulator

Hexadecimal

Unsigned

Sign-magnitude

Two-complement

0000

0

0

+0

0

0001

1

1

+1

+1

0010

2

2

+2

+2

0011

3

3

+3

+3

0100

4

4

+4

+4

0101

5

5

+5

+5

0110

6

6

+6

+6

0111

7

7

+7

+7

1000

8

8

-0

-8

1001

9

9

-1

-7

1010

A

10

-2

-6

1011

B

11

-3

-5

1100

C

12

-4

-4

1101

D

13

-5

-3

1110

E

14

-6

-2

1111

F

15

-7

-1

6.1. Aritmética sin signo (~enteros no negativos)

6.2. Aritmética con signo (~enteros)

Hay varias convenciones para representar enteros negativos en un acumulador: signo-magnitud, notación de exceso, y complemento a dos.

6.3. Signo-magnitud

El primer bit siempre indica el signo, donde 0 es positivo o neutro, y 1 es negativo. El bit de signo sólo tiene ese significado. Los restantes bits codifican la magnitud utilizando notación sin signo.

6.4. Complemento a la base

Se utiliza el método de los complementos para representar positivos y negativos en acumuladores de ancho \$w\$ dígitos en base \$b\$. En este método se define:

Definición Nombre largo Nombre corto

\$-x = b^w - x\$

El complemento a la base

El negativo

\$\bar{x} = b^w - 1 - x\$

El complemento a la base - 1

El complemento

Una forma más rápida de calcular el negativo es obtener el complemento a la base -1 de cada dígito y luego sumarle uno, expresado \$\bar{x} = b^w - 1 - x + 1\$, de lo que se infiere que el negativo de un número es su complemento más 1:

\$-x = \bar{x} + 1\$

6.5. Complemento a dos

Se utiliza el método de los complementos para representar positivos y negativos.

6.6. Conversiones de base

Sea \$\vec{n}\$ un número en una base cualquiera \$b \ge 2\$, cuyos dígitos representados un acumulador de ancho \$w\$ corrresponden al vector \$(n_{w-1}, n_{w-2}, ..., n_0)\$. Al sumar el valor posicional de esos dígitos, se obtiene su equivalente valor en base \$10\$:

\$\text{b2u}(\vec{n}, b, w) = \sum_{i=0}^{w-1}n_ib^i\$

En complemento a 2 el bit más significativo además de su valor posicional, representa el signo. Al convertir el vector \$\vec{n}\$ de complemento a 2 a base 10, el primer dígito pesa su valor posicional acorde a su signo, es decir:

\$\text{b2s}(\vec{n}, b, w) = -n_{w-1}b^{w-1} + \sum_{i=0}^{w-2}n_ib^i\$

Para convertir un número \$n\$ en base \$10\$ a cualquier otra base sin signo \$b \ge 2\$, el resultado serán los residuos de múltiples divisiones por la base hasta que el cociente se anule. Este algoritmo puede expresarse construyendo el vector resultado \$\vec{r}\$ compuesto de los dígitos:

\$r_i = mod(\lfloor n \cdot 2^{-i} \rfloor, 2)\$
\$\vec{r} = (r_{w-1}, r_{w-2}, ..., r_0)\$

Para convertir un número \$n\$ en base \$10\$ con signo, es decir a complemento a \$b\$, donde la base destino \$b \ge 2\$, se hacen dos pasos. Primero, se convierte el valor absoluto \$|n|\$ a la base destino cuyo resultado intermedio sería el vector \$\vec{s}\$, usando la conversión sin signo de la Ecuación (4). Luego, si \$n\$ es negativo, el resultado final sería el negativo de \$\vec{s}\$, expresado como el complemento a la base según la Ecuación (1), es decir \$-\vec{s}=\bar{s} + 1\$:

\$s_i = mod(\lfloor |n| \cdot 2^{-i} \rfloor, 2)\$
s2b

6.7. Representaciones comunes

6.7.1. Representación octal

Tomar grupos de tres bits.

Actividad: permisos de Unix.

6.7.2. Representación hexadecimal

Tomar grupos de cuatro bits.

Hacer tabla decimal, binario, octal, y hexadecimal de los primeros 16 números.

Actividad: Visualizar un archivo binario (eg. xxd) ojala con flotantes de doble precisión (usar txt2bin).

Ejercicio: Hacer tabla de sumas hexadecimales de los primerso 16 números

7. Aritmética de precisión de punto flotante (IEEE-754)

La aritmética de precisión de punto flotante son aproximaciones finitas de los números reales de los seres humanos. El estándar IEEE-754 define varios formatos de punto flotante, expresados en la siguiente tabla. El hardware moderno normalmente implementa sólo punto flotante de precisión simple, doble, y cuádruple.

Precisión Tamaño Signo Exponente Mantisa Exceso

Medio (half)

16 bits

1 bit

5 bits

10 bits

31

Simple (single)

32 bits

1 bit

8 bits

23 bits

127

Doble (double)

64 bits

1 bit

11 bits

52 bits

1023

Cuádruple (quadruple)

128 bits

1 bit

15 bits

112 bits

16383

Óctuple (octuple)

256 bits

1 bit

19 bits

236 bits

262143

El primer bit siempre es el signo y no cumple ninguna otra función, por lo que punto flotante es signo-magnitud para la parte decimal. El exponente también puede ser negativo, pero no usa signo-magnitud ni complemento a dos, sino que se representa en notación de exceso (offset-binary o exponent bias). El exceso se suma al exponente real, lo que genera el exponente ajustado que se almacena en el valor flotante.

 s exponent mantissa
[1|10001101|10011001000000000000000]

7.1. Conversión real a flotante

Algoritmo para convertir de decimal (reales de los humanos) a IEEE 754:

  1. Asignar el bit de signo.

  2. Convertir a base 2 el número sin signo.

  3. Normalizar y obtener el exponente.

  4. Sumar el exceso (127 en 32 bits, 1023 en 64 bits, o 16383 en 128 bits) al exponente, sea en decimal o binario.

  5. Copiar la mantisa normalizada sin el bit oculto.

Ejemplo de conversión del real n = -84.3125 a punto flotante de precisión simple (32 bits).

  1. Dado que \$n\$ es negativo, asignamos el bit de signo en 1

     s exponent mantissa
    [1|--------|-----------------------]
  2. Convertirmos a base 2 el número sin signo usando la Ecuación (2). Los dígitos a la derecha del punto decimal tienen valores posicionales con exponentes negativos. Con mucha probabilidad tengan una expansión decimal infinita o mayor a la que se pueda representar en el número de punto flotante.

    84.3125 = 1010100.0101
  3. Expresamos en notación científica multiplicado por una potencia de 2, esto nos permite correr o "flotar" el punto decimal. Normalizamos el punto decimal, corriéndolo hasta que quede inmediatamente después del primer bit 1 (de izquierda a derecha). Si el punto decimal se corre una posición a la izquierda, incrementamos el exponente en uno, si lo corremos a la derecha, decrementamos el exponente en 1:

    84.3125 x 10^0 = 1010100.0101 x 2^0
                   = 101010.00101 x 2^1
                   = 10101.000101 x 2^2
                   = 1010.1000101 x 2^3
                   = 101.01000101 x 2^4
                   = 10.101000101 x 2^5
                   = 1.0101000101 x 2^6

    El exponente en este caso sería 6 y la mantisa (parte decimal de un número) sería 0101000101.

  4. El exponente podría ser negativo, pero en lugar de representarlo en complemento a 2, IEEE-754 utiliza notación en exceso. El exceso es un número que representa el 0 y se suma siempre al valor a representar. En punto flotante de precisión simple (32-bits), el exceso es 127, es decir 127 representa 0, 128 representa 1, 129 representa 2, 126 representa -1, y así sucesivamente. El exceso 127 (o su equivalente en binario 01111111) se suma al exponente lo que genera el exponente ajustado.

    En nuestro ejemplo el exponente es 6, y el exponente ajustado sería 127 + 6 = 133 que al convertilo a base 2 sin signo usando la Ecuación (4) sería 10000101 (es fácil de calcular porque el primer bit vale por 128, y nos faltaría 5 para llegar a 133). Colocamos el exponente ajustado en el flotante:

     s exponent mantissa
    [1|10000101|-----------------------]
  5. Finalmente copiamos la mantisa normalizada al flotante. Por definición la mantisa es la parte decimal del número, que en nuestro ejemplo es 0101000101. El entero a la izquierda del punto decimal siempre será un 1 y no es necesario copiarlo al flotante, por lo que se le llama el bit oculto (hidden bit).

     s exponent mantissa
    [1|10000101|0101000101-------------]

    Los dígitos a la derecha de la mantisa son ceros a la derecha del punto decimal, por lo que el flotante final sería

     s exponent mantissa
    [1|10000101|01010001010000000000000]

7.2. Conversión flotante a real

Algoritmo para convertir de punto flotante a decimal:

  1. Agregar el bit oculto y el punto decimal a la mantisa

  2. Restar el exceso al exponente

  3. Desnormalizar: correr la coma decimal hasta que el exponente se haga 0

  4. Convertir a base 10 el número resultante

  5. Aplicar el bit de signo

7.3. Valores flotantes especiales

8. Arquitectura de computadoras

Objetivo: diseñar un procesador (de ocho bits). Herramienta: LogiSim.

8.1. Circuitos digitales

Tabla de mapeo entre operadores lógicos, operadores aritméticos, y compuertas lógicas. Sus tablas de verdad.

Actividad: probar las compuertas lógicas en LogiSim.

8.2. Comparador

Un comparador (CMP) es una función que recibe dos números a y b de igual cantidad de bits y produce tres salidas:

  1. LT (less-than), es una salida de 1 bit que se enciende sólo si a < b, de lo contrario produce 0.

  2. EQ (equals), es una salida de 1 bit que se enciende sólo si a = b, es decir, a y b tienen todos sus bits iguales, de lo contrario produce 0.

  3. GT (greater-than), es una salida de 1 bit que se enciende sólo si a > b, de lo contrario produce 0.

8.2.1. Comparador sin signo

Ejercicio 16 [comparator_u]

Diseñe un circuito comparador de 8 bits en una carpeta comparator_u/ de su repositorio de control de versiones, mediante los siguientes pasos.

  1. Diseñe un comparador sin signo de 1 bit (CMPu1) que recibe por entrada dos números cada uno de 1 bit. Cree un documento comparator_u/readme.adoc y escriba la tabla de verdad esperada del circuito.

  2. Infiera las tres funciones (ecuaciones) de los bits de salida LT, EQ, y GT.

  3. Opcional: Si es necesario, optimice las funciones para ahorrar compuertas lógicas.

  4. Represente las ecuaciones en un circuito usando la herramienta LogiSim Evolution. Guarde el diseño en la carpeta con el nombre comparator_u.circ. Nombre el circuito CMPu1. Recuerde nombrar todas las entradas y salidas. Pruebe que el circuito produce el resultado correcto. Puede comparar la tabla de verdad con la opción de menú Project > Analyze circuit.

    Exporte su circuito a una imagen vectorial (menú File > Export image), escoja formato SVG y desactive la opción Printer view. Guarde la imagen en la carpeta con nombre CMP1u.svg. En su archivo readme.adoc cree una sección "Comparador sin signo de 1 bit", incruste la imagen. Sugerencia: hacer que la imagen sea un enlace hacia el archivo CMPu.circ.

  5. Agregue a su archivo de LogiSim, un circuito comparador sin signo de 2 bits (CMPu2) que recibe por entrada dos números a y b cada uno de 2 bits (cambie el ancho de cada entrada). Utilice dos comparadores sin signo de 1 bit (2 x CMPu1). Distribuya las entradas entre los comparadores de 1 bit, utilice un splitters que se encuentra dentro de los componentes de cableado (wiring). Idee cómo unir las salidas de los comparadores de 1 bit con compuertas lógicas para generar las tres salidas LT, EQ, y GT. Recuerde probar que su circuito realiza la función de comparción correctamente. Genere una imagen vectorial de su circuito y agréguela a otra sección de su readme.adoc.

  6. Repita el paso anterior para agregar a su archivo de LogiSim, un circuito un comparador sin signo de 4 bits (CMPu4) que recibe dos números a y b cada uno de 4 bits. Nombre su componente como CMPu4. Reutilice dos comparadores de 2 bits. Aplique su lógica para generar las tres salidas LT, EQ, y GT. Recuerde probar que su circuito realiza la función de comparción correctamente.Genere una imagen vectorial de su circuito y agréguela a otra sección de su readme.adoc.

  7. Repita el paso anterior para agregar a su archivo de LogiSim un comparador sin signo de 8 bits (CMPu8) que recibe dos números a y b cada uno de 8 bits. Nombre su componente como CMPuu. Reutilice dos comparadores de 4 bits. Aplique su lógica para generar las tres salidas LT, EQ, y GT. Recuerde probar que su circuito realiza la función de comparción correctamente. Genere una imagen vectorial de su circuito y agréguela a otra sección de su readme.adoc.

Ejercicio 17 [comparator_u_full]

Diseñe en papel un comparador sin signo teórico completo de n bits. Recibe por entrada dos números a y b cada uno n bits y genera las tres salidas LT, EQ, y GT. Nombre su componente como CMPu. Suponga que existen y reutilice dos comparadores sin signo de n/2 bits.

8.2.2. Comparador con signo

Un comparador con signo tiene la misma interfaz que su contraparte sin signo, con la diferencia de que los números a y b se deben interpretar en complemento a dos. Un número que inicie con el bit más significativo en 1, es negativo.

Ejercicio 18 [comparator_s]

Siga el procedimiento del Ejercicio 16 [comparator_u] para crear un comparador con signo de 8 bits. Guarde su archivo de circuitos en la carpeta comparator_s/comparator_s.circ.

Ejercicio 19 [comparator_s_full]

Diseñe en papel un comparador con signo teórico completo de n bits. Recibe por entrada dos números a y b cada uno n bits y genera las tres salidas LT, EQ, y GT. Nombre su componente como CMPs. Suponga que existen y reutilice dos comparadores sin signo de n/2 bits.

8.3. Sumador

Circuito que recibe dos números de igual cantidad de bits y produce otro número resultado de igual cantidad de bits. Produce además un bit de acarreo final que sirve para saber si hubo desborde (overflow) o no. Para permitir encadenar varios sumadores, recibe como entrada un acarreo que proviene de otro sumador.

Ejercicio 20 [adder]

Diseñe circuito sumador completo de 1 bit. Recibe por entrada dos números de 1 bit (A y B) y un acarreo previo (Ci = carry_in). Genera por salida un número de 1 bit con el resultado de la suma (S = sum) y un bit de acarreo (Co = carry_out).

Cree su tabla de verdad, infiera ecuaciones, optimícelas (opcional), y diagrame el circuito correspondiente en un componente de LogiSim con nombre ADD4 o Adder4.

Reutilice circuitos previos para crear un sumador de 2 bits, 4 bits, y 8 bits. Recuerde probar que cada componente realiza la función de suma correctamente.

Ejercicio 21 [add_n]

Diseñe en papel un sumador teórico completo de n bits. Suponga que dispone de componentes sumadores de n/2 bits.

Ejercicio 22 [negative]

Diseñe componentes que calculen el negativo de un número en complemento a dos:

  1. Negativo de 2 bits (NEG2), recibe un número x de dos bits y genera por salida el valor de -x también de dos bits.

  2. Negativo de 4 bits (NEG4), recibe un número x de cuatro bits y genera por salida el valor de -x también de cuatro bits.

  3. Negativo de 8 bits (NEG8), recibe un número x de ocho bits y genera por salida el valor de -x también de ocho bits.

Recuerde que en complemento-a-dos, el negativo de un número es su complemento-a-uno más uno, \$-x = \bar{x} + 1\$. Reutilice sus componentes sumadores de 2, 4 y 8 bits.

8.4. Multiplexor (selector) (MUX)

Un multiplexor o selector (MUX) es un circuito lógico (función) que recibe \$2^n\$ entradas y escoge/selecciona una de ellas que será copiada en la salida. Las entradas están numeradas desde 0 hasta \$2^n - 1\$. Para saber cuál de las entradas es la que será copiada en la salida, recibe la identificación de éste como otro número de \$n\$ bits. Por lo tanto, la cantidad total de entradas del multiplexor corresponde a \$2^n + n\$.

8.4.1. Multiplexor 2 a 1 (MUX 2:1)

8.4.2. Multiplexor 4 a 1 (MUX 4:1)

Ejercicio 23 [multiplexer8to1]

Diseñe en LogiSim un circuito multiplexor de 3 bits. Recibe \$2^3 = 8\$ bits de entrada. Importante: reutilice multiplexores 4 a 1.

Su circuito debe ser un componente con nombre MUX8_1 o Multiplexer8to1. Si usa LogiSim estándar (no Evolution) edite la interfaz del componente para etiquetar las entradas y salidas. Su componente debe ser parte del archivo circuits/processor.circ en su repositorio de control de versiones.

Recuerde probar que su circuito realiza la función de comparción correctamente.

8.4.3. Multiplexor de n bits (MUX 2^n:1)

Multiplexor teórico completo de n bits.

8.5. Demultiplexor y decodificador

Un demultiplexor (DEMUX) es un circuito lógico (función) que invierte la función del multiplexor. Recibe un número por entrada que será copiado a una de las \$2^n\$ salidas del circuito, las demás producirán ceros. Las salidas están numeradas desde 0 hasta \$2^n - 1\$. Para saber cuál de las salidas será la que copie la entrada, recibe la identificación de esta como otro número de \$n\$ bits. Por lo tanto, la cantidad total de entradas del demultiplexor corresponde a \$n + 1\$ mientras que las salidas a \$2^n\$.

Un decodificador (decoder, DEC) es el mismo circuito que el demultiplexor con la diferencia de que no tiene un número por entrada que será copiado en una de las salidas. Sino que las salidas son siempre de un único bit y una de ellas estará encendida (1) y las demás apagadas (0). En adelante se usará más este circuito que el demultiplexor.

8.5.1. Decodificador 1 a 2 (DEC 1:2)

8.5.2. Decodificador de 1 a 4 (DEC 1:4)

Ejercicio 24 [decoder1to8]

Diseñe en LogiSim un circuito decodificador de 3 bits. Recibe un número de tres bits como entrada y produce \$2^3 = 8\$ bits de salida, todos con el bit 0 excepto la salida seleccionada por el número de tres bits, el cual tendrá el valor 1. Importante: reutilice Decodificadores 1 a 4.

Su circuito debe ser un componente con nombre DEC8_1 o Decoder8_1. Si usa LogiSim estándar (no Evolution) edite la interfaz del componente para etiquetar las entradas y salidas. Su componente debe ser parte del archivo circuits/processor.circ en su repositorio de control de versiones.

Recuerde probar que su circuito realiza la función de decodificación correctamente.

8.5.3. Decodificador de n bits (DEC 1:2^n)

Decodificador teórico completo de n bits.

8.6. Memoria principal

Memoria es un arreglo de celdas (bytes) que almacenan datos o instrucciones. Toda celda está identificada por un entero sin signo, su dirección de memoria.

Ancho de la memoria (actualmente 8 bits). Se requieren varias celdas continuas para almacenar números más grandes de 16, 32, 64, y 128 bits.

Memoria principal vs memoria secundaria. Random Access Memory (RAM) vs Read-Only Memory (ROM).

Dirección vs valor. Prefijos en base 10 y base 2.

Registros de una memoria:

  1. MAR Memory address register.

  2. MDR Memory data register.

Operaciones de una memoria (algoritmos):

  1. value = fetch(address)

    MAR <- address
    Decode address in MAR
    Copy memory contents in that address to MDR
  2. store(address, value)

    Load address into MAR
    Load value into MDR
    Decode address in MAR
    Store contents of MDR into memory location

8.7. Máquinas de programa almacenado

  1. Programa en entrada del dispositivo

  2. Programa almacenado

Arquitectura de von Neumann
Figura 3. Arquitectura de von Neumann

Arquitectura de von Neumann

  1. Instrucción: código de operación (operation code, op code) + parámetros (entre 0 y 3)

  2. Conjunto de instrucciones (instruction set)

  3. RISC (Reduced Instruction Set Computers)

  4. CISC (Complex Instruction Set Computers)

  5. Lenguaje máquina (machine language)

Tipos de instrucciones

  1. Data transfer: copian bits de un lado para otro destruyendo el destino. Ej: LOAD, STORE.

  2. Arithmetic/logic: suma, resta, multiplicación, división, tanto fija como flotante. Operadores lógicos: AND, OR, NOT, XOR, etc. Pueden tener de uno a tres parámetros, como:

    1. Three-address instruction: ADD X,Y,Z que podría significar Z := X + Y

    2. Two-address instruction: ADD X,Y que podría significar X := X + Y o Y := Y + X a criterio de las personas diseñadoras.

    3. One-address instruction: ADD X que debe usar un registro, como R := R + X

  3. Compare: comparan dos números y encienden banderas (bits) llamadas condition codes de acuerdo al resultado. Ejemplo COMPARE X,Y podría GT=1 y LT=0 si X=5 y Y=3.

  4. Branch: Alteran el flujo normal de ejecución (la secuencia). Por defecto, después de ejecutar la instrucción en la dirección PC de la memoria, la arquitectura de von Neumann ejecuta la instrucción que le sigue, que estaría en la dirección PC + 1k, donde k es la cantidad de bits (ancho) de cada instrucción. Normalmente se salta en función de los códigos de condición (banderas) como LT y EQ, por lo que usualmente están precedidas por una instrucción de comparación. Ej.: JUMP X salta incondicionalmente a la dirección X mientras que JUMPNE X salta a la instrucción X si el código de condición NE (not-equal) es 1, de lo contrario, ejecuta la instrucción que le sigue como de costumbre. La instrucción JUMPLE salta si alguna de las banderas LE o EQ están encendidas.

Actividades:

  1. Ejecutar el código ¿que produce en la salida? ¿qué hace el programa?

  2. Desensamblar (disassemble) el código (Ingeniería inversa)

  3. Decompilar a pseudocódigo

  4. Optimizar el código

  5. Generar código que lea repetidamente un número e imprima su antecesor hasta que haya un desborde

Ejercicios:

  1. Programar la suma de dos números

  2. Programar la suma de tres números

  3. Encontrar el mayor de tres números (ej.: hipotenusa)

  4. Leer repetidamente números de la entrada estándar hasta que se ingrese cero, e imprimir la suma

Ejercicio 25 [bn_trace_01]

Clone el modelo de arquitectura de von Neumann visto en clase (en hexadecimal). Cargue el siguiente programa en la memoria principal a partir de la dirección 00200. Cargue esta misma dirección en el contador de programa (PC) e inicie su ejecución hasta que se detenga con la instrucción HALT. Rastree la ejecución de cada instrucción y deje rastro en ensamblador en la columna Program trace. Actualice el valor de los registros (columna Registers) al ejecutar cada instrucción. Una vez que finalice, debe quedar rastro del último valor almacenado en cada registro de la CPU.

9. Máquinas modernas

9.1. Computación paralela

  1. Von Neumann bottleneck

  2. Niveles de caché

  3. Pipelining

  4. Fin de la ley de Moore

  5. Núcleos de procesador

  6. Hilos de ejecución

Actividad 25. Ensamblar una PC [assemble_pc]

Cada estudiante emamble una PC a partir de sus componentes: caja, tarjeta madre, procesador, memoria, disco duro, monitor, teclado, y ratón. Algunos de ellos podrían estar defectuosos.

Instalarle dos sistemas operativos con arranque dual: Windows y Linux.

9.2. Computación distribuida

Actividad 26. Servidor virtual privado [virtual_private_server]

Crear una máquina virtual en la nube, ej.: Oracle Academic Cloud, con algún Linux.

Darle una IP pública. Instalarle un servidor web.

Crear un sitio web personal con su currículo y un portafolios para proyectos académicos (hechos durante la carrera) y personales.

10. Máquinas teóricas

La máquina de Turing es un modelo (versión simplificada de la realidad que abstrae/elimina detalles).

(state, symbol, next symbol, next state, direction)

La máquina siempre arranca en el primer bit no blanco en estado 1. Ejemplos:

  1. Inversor de bits

  2. Bit de paridad (odd parity bit): escribir 1 al final si la cantidad de bits 1 en el número es par para que quede siempre impar.

  3. Incrementar números en notación unaria.

Tesis Church-Turing: Todo algoritmo ejecutable por una máquina convencional puede ser ejecutado por una máquina de Turing y viceversa.

Problemas no resolubles:

  1. Saber si un programa se detiene o no independientemente de la entrada que se le dé (problema de la parada, halting problem).

  2. Saber si un programa siempre va a producir un resultado específico para una entrada específica.

  3. Saber si dos programas son equivalentes, es decir, producen la misma salida para todas las posibles entradas.

11. Ética

La ética y la moral ambas se refieren a distinguir las acciones humanas "buenas" de las "malas". La moral es dependiente de la cultura (ej.: tomar café, o comer carne de res). La ética es una rama de la filosofía y trata de transcender las idiosincracias de las culturas.

La tecnología ha modificado fuertemente la sociedad, tanto que se habla vivimos un revolución de la información posterior a la revolución industrial. La ética no tiene aún una respuesta para muchos de estos cambios que ocurren día a día y queda a responsabilidad de las personas profesionales en la disciplina obrar con corrección. Algunas preguntas a realizarse para ayudarse ante situaciones nuevas [Schn19]:

  1. ¿Quiénes son todos los involucrados en esta situación?

  2. ¿En qué se beneficia o afecta cada involucrado?

  3. ¿Cuáles son los deberes y responsabilidades de cada involucrado?

  4. ¿Hay situaciones similares que no involucren tecnología para las que se ha encontrado una solución? (analogía)

Ejemplos de situaciones que involucran tecnología:

  1. Compartir archivos con derechos de copia. Ej: libros, música (vinilos, casetes, radio, CDDA, Napster, iTunes Store, Spotify) y películas (descarga directa, P2P).

  2. Intervenir llamadas (teléfono, VoIP, WhatsApp) para facilitar la investigación policial, pero también facilita a los hackers su mal uso.

  3. Ceder células para un estudio genómico (privacidad, anonimazación, lucro, medicamentos)

Referencias

  • [Schn19] Schneider, Michael G.; Gersting, Judith L (2019). Invitation to Computer Science. 8th edition. Cengage Learning, Inc.