Tarea4

Anuncio
TAREA 4
Ejercicios
Algunas de las cuestiones siguientes hacen referencia al “computador del enano ” y a
su conjunto de instrucciones.
La cuestión 1 tiene por objeto familiarizarte con las técnicas de búsqueda.
1. Programa uno de los dos métodos de ordenación para números enteros: ordenación
por inserción u ordenación mediante el método de la burbuja.
Cualquiera de los dos programas anteriores te proporcionarán la cadena siguiente
ordenada de manera ascendente:
3 4 6 6 2 6 5 1 3 −1 −4 −8 9 −5 −2 7 1 0 5 −5
Imagina que tienes además el número 4 y lo que quieres es determinar (la posición
que ocupa) el primero de los veinte números anteriores mayor que 4.
Para ello te puedes ayudar del siguiente algoritmo:
1
i=1
j=n
⌋
indice = ⌊ i+j
2
if a(indice) ≤ l then
i = indice + 1
else
if indice = 1 or a(indice − 1) ≤ l then
return (indice encontrado)
endif
j = indice − 1
endif
if i > j then
indice = n + 1(FIN. Todos los ai ≤ l, i = 1, ..., n)
else
goto 1
endif
Escribe un programa en C que determine la posición exacta del valor buscado.
¿Podrı́as determinar la complejidad computacional (número de operaciones) del algoritmo de búsqueda descrito? ¿Y del proceso completo de ordenación y búsqueda?
2. Se dice que un algoritmo de ordenación es conservador del orden si, para cualesquiera
i, j el orden de los elementos xi y xj en la disposición original no queda alterado a
menos que sea imprescindible hacerlo a causa de los respectivos valores del campo
de ordenación (la “clave”). Por ejemplo, imaginemos un fichero tal como:
···
LLODIO
LLODIO
BILBAO
LLODIO
···
···
PEREZ
ORTIZ
ORTEGA
OSTOLAZA
···
···
GONZALEZ
GONZALEZ
BERMUDEZ
PEREZ
···
1
···
TERESA
ALBERTO
FRANCISCO
AITOR
···
Supongamos que ordenamos el fichero por (exclusivamente) municipio. Si el algoritmo de ordenación es conservador del orden, la lı́nea correspondiente a ORTEGA
BERMUDEZ pasará por delante de las otras tres, pero éstas, que no se diferencian
por el valor de ordenación (LLODIO en las tres) quedarán en el mismo orden relativo
en el que están: primero PEREZ, luego ORTIZ y luego OSTOLAZA.
Examina los algoritmos de inserción y burbuja y decide si son conservadores de
orden. ¿Qué trascendencia tiene este hecho? (Ayuda: piensa en lo que ocurrirı́a si
quisieras hacer una ordenación por múltiples claves: por ejemplo, por municipio y,
dentro de cada municipio, por primer apellido).
3. Se supone que al entrar en el siguiente fragmento de programa, la posición de memoria 50 contiene el valor 10, la 51 el valor 1 y la 52 un valor cualquiera. ¿A qué sentencia
de control de flujo corresponderı́a entonces este fragmento?
20
21
22
23
24
25
26
27
552
250
827
552
151
352
621
···
LOAD 52
SUB 50
BP 27
LOAD 52
ADD 51
STORE 52
BP 21
4. Ahora has de hacer lo contrario. Imagina que eres el compilador, y has de traducir
una sentencia como:
for{i=1;i<10;i++){
j=j+1;
}
a instrucciones ejecutables en el “computador del enano”. Muestra el código resultante.
Ayudas, comentarios,...
1. Un algoritmo como el presentado en la Cuestión 1 es un algoritmo de busqueda, que
necesita que el conjunto sobre el que se busca esté ordenado. El presentado en dicha
cuestión se conoce como algoritmo de búsqueda binaria, y es el más eficiente.
2. Las cuestiones 3 y 4 son simples una vez que se entiende que nada esencialmente distingue una instrucción de un dato: ambos son números binariamente representados
en la memoria de la maquina. Las instrucciones, por tanto, pueden ser modificadas
por el programa del que forman parte -aunque es una práctica muy poco recomendable: más abajo encontrarás la explicación-.
Considera por ejemplo el siguiente programita de (de [8], p.10):
2
00
01
02
03
04
05
901
360
901
106
902
000
READ
STORE 06
READ
ADD 06
PRINT
STOP
Podrı́amos, por ejemplo, sumar 100 al contenido de la posición de memoria 03 y el
ADD se convertirı́a en un SUBTRACT.
3. El siguiente ejemplo te ayudará a responder a las cuestiones 3 y 4. Supón que deseas
programar el “computador del enano” de modo que hiciera la suma de diez números,
situados en posiciones de memoria consecutivas a partir de la 50. Tu programa
tendrı́a una forma ası́ (se omite el comienzo y el final para concentrar la atención en
la parte que nos interesa). Se supone que en la posición de memoria 83 y 84 hay al
comienzo ceros y en la 82 un 1.
11
12
13
14
15
16
17
18
19
···
583
114
314
550
184
384
283
182
383
···
LOAD 83
ADD 14
STORE 14
LOAD 50
ADD 84
STORE 84
LOAD 83
ADD 82
STORE 83
;En 83 hay un ı́ndice que crece en uno cada iteración
; Se añade el ı́ndice a 14, que contiene una instrucción
;... y se modifica dicha instrucción.
; En la primera pasada, carga lo que hay en 50
; Lo añade al total en 84...
;... y actualiza dicho total.
; Las instrucciones 17-19 actualizan el ı́ndice.
A este fragmento de programa habrı́a que añadir lógica que controlara la terminación
(cuando el ı́ndice 83 alcanza el valor 10), etc.
Los siguientes detalles son de interés:
a) El programa se automodifica: la instrucción 14 cambia de una iteración a otra.
Se dice que el código es impuro. Esto es muy poco conveniente: si lo hubiéramos
de ejecutar repetidamente, habrı́amos de recargar una copia “frescaçada vez.
Además, en un sistema operativo multiusuario, dos o más usuarios no podrı́an
compartir la misma copia del programa (¿ves por qué?), lo que es muy ineficiente: por cada usuario habrı́a que cargar una vez el programa, desperdiciando
memoria.
b) La mayorı́a de los sistemas operativos modernos impiden que un programa se
modifique a sı́ mismo del modo ilustrado en el ejemplo. Por ejemplo HP-UX (la
versión de Unix que utilizas) fuerza una rı́gida separación entre programas y
datos. Un programa no puede alterarse. Se dice entonces que el código es puro
( se habla también de código reentrante), y puede ser compartido por múltiples
usuarios. En sistema DOS, todavı́a en uso en muchos ordenadores personales,
no es reentrante.
c) La descripción del computador del enano es una simplificación extrema. Observa
que un programa como el que parcialmente se recoge en 4 requerirı́a una carga
3
absoluta: necesitarı́a ser cargado en una zona precisa de memoria y en ninguna
otra. En otro caso, al modificar la posición de memoria 14 no estarı́amos modificando el LOAD que nos interesa, sino otra instrucción diferente, con resultados
casi con seguridad catastróficos.
d) Observa que el programar directamente en lenguaje máquina es horrendo: ¡Si
por azar necesitamos modificar una instrucción de nuestro programa, puede
que necesitemos acabar modificando todo el programa! Por ejemplo supón que
tuvieramos que emplear la posición de memoria 83 para otra cosa distinta del
ı́ndice que guardamos en ella. Imagina que el ı́ndice pasa a la posición 87. Todas
las referencias a dicha posición de memoria han de ser sustituı́das por 87. En la
práctica, tenemos la ayuda de los llamados ensambladores. Un ensamblador es
un compilador de bajo nivel que traduce nombres de instrucciones relativamente
mnemónicos a sus respectivos códigos - por ejemplo, transforma STOP en 000 en
el caso del computador del enano. Además, permite escribir nombres simbólicos
de variables en lugar de posiciones de memoria, o sea:
STORE X
LOAD Y
en lugar de
STORE 33
LOAD 34
El ensamblador lee el programa fuente y asigna a cada variable simbólica una
ubicación, sin que hayamos de preocuparnos nosotros de hacerlo.
4. Es frecuente que los compiladores permitan traducir un programa fuente en un lenguaje de alto nivel (como C) a código máquina, o a lenguaje de ensamblado. Si deseas
ver un programa en ensamblador, puedes tomar cualquiera de los que has hecho en
C y compilarlo ası́:
cc -Aa -S prog.c
Obtendrás como resultado un prog.s que puedes editar y mirar. Cuando lo hagas,
verás que dispones de un compilador de C.
Ahora que ya lo sabes hacer...
...no es preciso que lo hagas nunca más.
1. No es preciso que programes procedimientos de ordenación en general. Es una labor
tan usual que todos los sistemas operativos y paquetes como hojas de cálculo, gestores
de bases de datos, etc. incluyen facilidades para la ordenación. Tanto DOS como
UNIX disponen de un mandato sort invocable desde el sistema operativo, que permite
ordenar ficheros.
2. Si hubieras de hacer una ordenación en el seno de un programa en C, puedes invocar
directamente funciones predefinidas; necesitas para ello al comienzo de tu programa
un
4
#include <stdlib.h>
Dos funciones son de especial utilidad: bsearch y qsort. La primera un procedimiento
de búsqueda binaria. La segunda implementando Quicksort, método de ordenación
de complejidad O(nlog n), el más rápido que se conoce. Puedes consultar [3] y [4]
para ver el modo de utilizar dichas funciones.
Referencias
[1] A. Aho, J. Hopcroft, and J. Ullman, The Design and Analysis of Computer
Algorithms, Addison Wesley, Reading, Ma., 1974.
[2] T. Doan, Rats 4.0, Estima, Inc., Evanston, I1., 1992.
[3] Hewlett-Packard, HP C/HP-UX Reference Maunal. HP 9000 Series 600/700/800
Computers, Hewlett- Packard Co., Cupertino, Ca., 1991.
[4] P. J. Plauger and J. Brodie, C estandar, Microsoft Press/Anaya Multimedia,
Madrid, 1990.
[5] B. Kernighan and D. Ritchie, The C Programming Language (ANSI C), Prentice
Hall, Englewood Cliffs, second edition, 1978.
[6] L. Kronsjo, Algorithms: Their complexity and efficiency, Wiley, Chichester, 1986.
[7] S. S. U. Ltd., S-Plus Users’s Manual, Statistical Sciences U.K. Ltd., Oxford, 1990.
[8] S. Wolfram, Mathematica: A System for doing Mathematics by Computer, AddisonWesley, 1991.
5
Descargar