-
Notifications
You must be signed in to change notification settings - Fork 0
/
resumen.tex
2093 lines (1953 loc) · 129 KB
/
resumen.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
\documentclass[10pt,a4paper]{article}
\usepackage{blindtext}
\usepackage{subcaption}
\usepackage{graphicx}
\usepackage{tikz}
\usepackage{amssymb}
\usepackage{caption}
\usepackage{amsmath}
\usepackage{circuitikz}
\usepackage{hyperref}
\usepackage{amssymb}
\usepackage{amsmath}
\usepackage{listings}
\lstset{
inputencoding=utf8,
extendedchars=true,
literate={á}{{\'a}}1 {é}{{\'e}}1 {í}{{\'i}}1 {ó}{{\'o}}1 {ú}{{\'u}}1 {ñ}{{\~n}}1 {Á}{{\'A}}1 {É}{{\'E}}1 {Í}{{\'I}}1 {Ó}{{\'O}}1 {Ú}{{\'U}}1 {Ñ}{{\~N}}1
}
\input{AEDmacros}
\newcommand{\notimplies}{\;\not\!\!\!\implies}
\title{Algoritmos y Estructuras de Datos II}
\author{Tomás Agustín Hernández}
\date{}
\begin{document}
\maketitle
\begin{figure}[b]
\centering
\begin{tikzpicture}[remember picture,overlay]
\node[anchor=south east, inner sep=0pt, xshift=-1cm, yshift=2cm] at (current page.south east) {
\begin{minipage}[b]{0.5\textwidth}
\includegraphics[width=\linewidth]{logo_uba.jpg}
\label{fig:bottom}
\end{minipage}
};
\end{tikzpicture}
\end{figure}
\newpage
\section{Especificación}
\subsection*{Consideraciones importantes / Reminders}
\begin{itemize}
\item Utilizar operadores luego: Si estoy en LPO (Lógica de Primer Orden) utilizar los operadores luego si vemos que hay una posible indefinición como una división, o ingresar a una lista a un índice. Recordar que el para todo y un existe, aunque esté acotado por un rango, los cuantificadores predican IGUAL para todos los valores. Entonces, aunque diga que x es positivo, también probará dividir inclusive por 0 y estallará.
\item Recordar las condiciones bidireccionales
\begin{itemize}
\item Si por algún motivo tengo que armar una “lista”, como, por ejemplo, los divisores de un número x tengo que indicar que, si el número divide a x, entonces ese número está en res, pero además todos los valores que están en res DIVIDEN a x. Es una condición bidireccional.
\item Otro ejemplo puede ser que tenga que considerar el máximo de una lista, si todos los valores y que están en la lista son menores que res entonces significa que res también pertenece a esa lista original.
\end{itemize}
\item Recordar el significado de los cuantificadores con dos variables al mismo tiempo: En la lógica se ejecutan todos de uno a la vez. Es decir, si tengo que poner un para todo adentro de un para todo entonces hago un para todo solo con dos variables y listo.
\item Recordar que cuando en un procedimiento llamo a un predicado y ese predicado devuelve algo de un para todo, existe (básicamente un valor de verdad) tengo que castear ese valor en el procedimiento porque son dos mundos distintos.
Ej: asegura: { res = True \(\iff\) predicado}
\item Los predicados y funciones auxiliares no describen problemas. Son herramientas sintácticas para descomponer predicados.
\begin{itemize}
\item Los procedimientos pueden llamar a funciones auxiliares o predicados. Un procedimiento no puede llamar a otro procedimiento.
\item Los predicados pueden llamar a predicados o auxiliares.
\item Las auxiliares solo pueden llamar auxiliares.
\end{itemize}
\item No usamos nunca \(==\) en especificación, usamos siempre \(=\) y estamos comparando, no asignando.
\item No existe el guardar o asignar en el mundo de la lógica. No puedo guardar en una lista en un índice específico porque si un valor. Para esto solemos usar que x valor pertenecerá a esta lista, por ejemplo.
\item Si tengo un algoritmo que cumple una funcionalidad específica con un require más débil, puedo poner el require más restrictivo y va a funcionar igual pero NO al revés.
\end{itemize}
\subsection*{Fórmulas compuestas}
Decimos que una fórmula es compuesta a una fórmula que tiene más de una operación y esa operación necesita realizarse antes de conocer su valor.
\begin{itemize}
\item \((p \land q) \lor m\)
\item \(((p \land q) \lor m) \implies n\)
\end{itemize}
\subsection*{Fórmula atómica}
Decimos que una fórmula es atómica si se puede inferir su valor con una, o ninguna operación. Es irreducible.
\begin{itemize}
\item p
\item \(p \land q\)
\end{itemize}
\subsection*{Fórmulas bien definidas}
Decimos que una fórmula está bien definida cuando el orden que hay que hacer las operaciones es clara. Es decir, cuando cada operación toma dos variables proposicionales, y al realizar la operación termina siendo una fórmula atómica.
\begin{itemize}
\item \(p \land q \lor r\) está mal formada. No se especifica si primero se realiza el \(\land\) o el \(\lor\).
\item \((p \land q) \lor r\) está bien formada.
\item \(p \land q \land r \land m\) está bien formada porque son todas conjunciones.
\item \(p \lor q \lor r \lor m\) está bien formada porque son todas disyunciones.
\end{itemize}
\subsection*{Cuantificadores}
\begin{itemize}
\item Para todo: \(\forall\)
\begin{itemize}
\item \(Garantiza \ la \ \text{conjunción}: p(1) \land p(2) \land p(3) \dots \land p(m) \). Todos los casos deben ser \True \ para que el cuantificador sea \True.
\item Se acompaña por un \(\implica\) a la hora de predicar sobre los elementos.
\item \((\forall i: \ent)(0 \le i < \longitud{s} \implicaLuego \ s[i] \ mod \ 2 = 0)\). Todos los elementos de la lista son divisibles por 2.
\item Estructura: \(\forall\) + rango + \(\implicaLuego\)
\end{itemize}
\item Existe: \(\exists\)
\begin{itemize}
\item \(Garantiza \ la \ \text{disyunción}: p(1) \lor p(2) \lor p(3) \dots \lor p(m) \). Con un caso \True \ el cuantificador es \True.
\item Se acompaña por un \(\land\) a la hora de predicar sobre los elementos.
\item \((\exists i: \ent)(0 \le i < \longitud{s} \yLuego s[i] \ge 0)\). Existe algún elemento en la lista que es mayor o igual a 0.
\item \(\exists\) + rango + \(\yLuego\)
\end{itemize}
\end{itemize}
\subsection*{Equivalencias entre fórmulas}
Decimos que dos fórmulas son equivalentes \(\iff\) los valores de la tabla de verdad al aplicar la operación arroja el mismo resultado.
\subsection*{Valuaciones}
Las valuaciones surgen en base a la tabla de verdad. Las valuaciones serian darle valor a las variables proposicionales y ver el resultado de la operación. Solo hacen referencias a fórmulas atómicas.
\subsection*{Tautologias, contradicciones y contingencias}
\begin{itemize}
\item Una fórmula es tautología \(\iff\) el resultado de la operación en cada fila arroja siempre V.
\item Una fórmula es contradicción \(\iff\) el resultado de la operación en cada fila arroja siempre F.
\item Una fórmula es contradicción \(\iff\) el resultado de la operación en cada fila arroja siempre V y F.
\end{itemize}
\subsection*{Relaciones de fuerza entre fórmulas}
Decimos que una fórmula es más fuerte que la otra \(\iff\) una fórmula es más restrictiva que la otra, o está incluida en la otra. \\
En el mundo de la lógica, decimos que A es más fuerte que B \(\iff A \implies B\)
\begin{itemize}
\item Si (\(A \implies B\)) y (\(B \implies A\)) son tautologías, entonces A y B son equivalentes.
\item Si (\(A \implies B\)) es tautología y (\(B \notimplies A\)) no es tautología, entonces decimos que A es más fuerte que B.
\item Si (\(A \notimplies B\)) y (\(B \notimplies A\)) son contigencias, entonces no existe relación de fuerza entre A y B.
\end{itemize}
Algunos ejemplos:
\begin{itemize}
\item \(\longitud{s} = 0 \implies \longitud{s} \ge 0\). En este caso vemos que \(\longitud{s} = 0\) es más fuerte que \(\longitud{s} \ge 0\) pues \(\longitud{s} = 0\) está incluido en \(\longitud{s} \ge 0\). Por lo tanto, \(A \implies B\)
\item \(\longitud{s} = 0 \implies \longitud{s} \ge 3\). En este caso vemos que \(\longitud{s} = 0\) no es más fuerte que \(\longitud{s} \ge 3\) pues \(\longitud{s} = 0\) no está incluido en \(\longitud{s} \ge 3\). Por lo tanto, \(A \notimplies B\)
\item \(2 \le i < \longitud{s} \implies 1 \le i < \longitud{s}\). En este caso A \(\implies\) B, pues i = 2 está incluido en el rango de B. Por lo tanto, \(A \implies B\)
\item \(0 \le i < \longitud{s} \implies 1 \le i < \longitud{s}\). En este caso A \(\notimplies\) B, pues el 0 de A no es parte de B. Por lo tanto, \(A \notimplies B\)
\end{itemize}
\subsection*{Tipos de parámetros en especificacion}
\begin{itemize}
\item in: Solo nos interesa el valor de entrada de una variable. No la vamos a modificar. Ya están inicializados
\item out: Donde se retornará el resultado. No nos importa el valor inicial ni tampoco determina nada en nuestra función.
\item inout: Necesitamos el valor original por referencia el cual podrá ser modificado durante el procedimiento. Que sea inout no significa que siempre será modificado, sino que NO se garantiza que el valor al salir del procedimiento sea el mismo que cuando ingresó. Ej: Añadir un valor a un conjunto es inout pues hay casos donde no se modifica (pues el elemento ya está) y en otros sí (se agrega).
\end{itemize}
\subsection*{Lógica trivaluada}
También llamada lógica secuencial porque se procesa de izquierda a derecha; Nos introduce los conceptos de \(\yLuego \ \oLuego \ \implicaLuego\) y el valor de indefinido \(\bot\).
Se termina de evaluar una expresión cuando se puede deducir el valor de verdad. \\
Considere \(x = \True \land y = \bot \land z = \False \)
\begin{itemize}
\item \(x \oLuego y\): Como el \(\oLuego\) necesita uno solo para ser verdadero, entonces como x ya es \(\True\) entonces toda la fórmula es verdadera.
\item \(x \yLuego y\): Como el \(\yLuego\) necesita que ambas variables sean verdaderas, evalúa indefinido y el programa estalla.
\item \(\neg x \implicaLuego y\): Como el \(\implicaLuego\) solo es falso si el antecedente es \(\True\) y el consecuente \(\False\), como en este caso el antecedente ya es falso, toda la implicación es verdadera.
\item \((x \land z) \yLuego y\): Como el \(\yLuego\) necesita que ambas fórmulas sean \(\True\), en este caso, como \((x \land z)\) es falso, entonces ya toda la fórmula es falsa. Nótese que el \(\land\) de la condición interna no contiene el luego porque jamás se indefinirá.
\item \((\forall i: \ent)(0 \le i < \longitud{s} \implicaLuego s[i] \ge 0)\) Nótese que aquí usamos un \(\implicaLuego\) porque podría ser que la lista esté indefinida o no exista el valor en s[i]
\end{itemize}
\subsection*{Predicados}
\begin{itemize}
\item Viven en el mundo de la lógica.
\item Nos sirven para poder modularizar nuestras especificaciones.
\item Solamente devuelven valores de verdad True y False y es necesario castearlos en caso de querer devolver bool como tipo de dato.
\item Los predicados pueden llamar a otros predicados o funciones auxiliares.
\item Pueden utilizar cuantificadores.
\item No tienen requiere ni asegura.
\item No admite parámetros in, out, inout.
\end{itemize}
\leavevmode
\\
Ejemplo cuando tenemos que transformar el valor de verdad a tipo de dato:
\leavevmode
\\
\pred{divisiblePorDos}{n: \ent}{
n \ mod \ 2 = 0
}
\begin{proc}{esMultiploDeDos}{\In n: \ent}{\bool}
\requiere{\True}
\asegura{res = true \iff divisiblePorDos(n)}
\end{proc}
\leavevmode
\\
Ejemplo usando un predicado sin necesidad de transformar el valor de verdad a tipo de dato:
\leavevmode
\pred{todosSonPares}{l: \TLista{\ent}}{
\paraTodo[unalinea]{i}{\ent}{0 \le i < \longitud{l} \implicaLuego l[i] mod 2 = 0}
}
\begin{proc}{todosPares}{\In l: \TLista{\ent}}{\bool}
\requiere{todosSonPares(l)}
\end{proc}
\newpage
\subsection*{Funciones Auxiliares}
\begin{itemize}
\item Son reemplazos sintácticos.
\item Nos ayudan a modularizar las especificaciones.
\item No pueden ser recursivas.
\item Solo hacen cuentas.
\item No pueden utilizar cuantificadores.
\item Pueden llamar a predicados.
\item Devuelven un tipo de dato.
\item No tienen requiere ni asegura.
\item No admite parámetros in, out, inout.
\end{itemize}
\leavevmode
\aux{sumar}{n: \ent, m: \ent}{\ent}{
n + m
}
\aux{sumarTodos}{s: \TLista{\ent}}{\ent}{
\sum_{i=0}^{\longitud{s}-1}{s[i]}
}
\subsection*{Aridad}
Decimos que una función es de aridad \(n\) cuando la función recibe \(n\) cantidad de parámetros.
\subsection*{Variables Ligadas y Libres}
Las variables son ligadas \(\iff\) están dentro de un cuantificador mientras que son libres cuando no lo están.
\begin{itemize}
\item \((\forall i: \ent)(0 \le i < \longitud{s} \implicaLuego n \ge s[i])\) i es una variable ligada mientras que n y s son variables libres.
\item \((\exists j: \ent)(0 \le j < \longitud{s} \yLuego n \ge s[i])\) j es una variable ligada mientras que n y s son variables libres.
\item \((\forall i: \ent)(0 \le i < \longitud{s} \implicaLuego n \ge s[i]) \land P(i)\) Ojo acá. i es una variable ligada, pero la i que está fuera del cuantificador \(P(i)\) no está ligada. Esta última debería ser renombrada para no tener problemas y confusiones.
\end{itemize}
Cuando tenemos variables ligadas \textbf{no} podemos hacer nada sobre ellas, entre esas cosas, no podemos reemplazarlas porque no dependen de nosotros sino de los cuantificadores.
\subsection*{Cuantificadores anidados}
Anidamos cuantificadores cuando el rango de las variables es exactamente el mismo.
\begin{itemize}
\item \((\forall i, j: \ent)(0 \le i, j < \longitud{s} \implicaLuego n \ge s[i][j]) \equiv (\forall i: \ent)((0 \le i < \longitud{s}\implicaLuego (\forall j: \ent)(0 \le j < \longitud{s} \implicaLuego n \ge s[i][j]))) \)
\end{itemize}
\subsection*{Estado}
Llamamos estado a los valores de las variables en un punto de ejecución específico. El estado de un programa es importante porque muta al asignar valores a las variables.
Cuando necesitamos hablar del estado de una variable en un instante específico, hablamos de \textbf{metavariables} \\
\subsection*{Metavariables}
Llamamos metavariable a una variable en un instante dado. Es útil cuando tenemos que predicar como cambio el valor de una variable con respecto al inicial. \\
Cuando tenemos que utilizar metavariables, sea S una variable cualquiera podemos referirnos al instante de tiempo de S como \(S_{t}\) donde t indica el momento. \\ \\
Notación \(S = S_{0}\)
\begin{proc}{multiplicarPorDosAImpares}{\Inout l: \TLista{\ent}}{}
\requiere{l = l_{0}}
\asegura{\longitud{l} = \longitud{l_{0}}}
\asegura{(\forall i: \ent)(0 \le i < \longitud{s} \implicaLuego if(s_{0} [i] \ mod \ 2 \neq 0) \ then \ (s[i] = s_{0}[i] \ast 2) \ else \ (s[i] = s_{0}[i]) \ fi)}
\end{proc}
\leavevmode
\\
Nota: Cuando utilizamos metavariables tenemos que indicar que al modificar algo directamente, si no modificamos todo el conjunto de valores tenemos que indicar que los demás permanecen inalterados. En este caso, como estamos editando los valores, no tendría sentido que la lista salga con mayor longitud, es por eso que garantizamos que no cambia.
Otra manera de resolver el ejemplo anterior es utilizando old(s)
\begin{proc}{multiplicarPorDosAImpares}{\Inout l: \TLista{\ent}}{}
\asegura{\longitud{l} = \longitud{old(l)}}
\asegura{(\forall i: \ent)(0 \le i < \longitud{s} \implicaLuego if(old(s)[i] \ mod \ 2 \neq 0) \ then \ (s[i] = old(s)[i] \ast 2) \ else \ (s[i] = old(s)[i]) \ fi)}
\end{proc}
\leavevmode
\\
\section*{Correctitud de un Programa}
Decimos que un programa S es correcto respecto a una especificación si se cumple la precondición P, el programa termina su ejecución y se cumple la postcondición Q.
\subsection*{Tripla de Hoare}
Notación para indicar que S es correcto respecto a la especificación (P, Q) \\
\[\{P\} \ S \ \{Q\}\]
\subsection*{SmallLang}
Es un lenguaje que nos permitirá poder validar la correctitud de un programa.
Solo tiene dos operaciones:
\begin{itemize}
\item x := E \(\equiv\) asignación
\item skip \(\equiv\) no hace nada
\end{itemize}
Nota: E es una expresión cualquiera. Un valor, una función, cualquier cosa.
\subsection*{Estructuras de Control en SmallLang}
\begin{itemize}
\item Secuencia de pasos: S1; S2 es un programa \(\iff\) S1 y S2 son dos programas.
\item Condicionales: if B then S1 else S2 endif es un programa \(\iff\) B es una condición lógica (guarda) y S1 y S2 son programas.
\item Ciclo: while B do S endwhile es un programa \(\iff\) B es una condición lógica y S un programa.
\end{itemize}
\subsection*{Validez de una tripla de Hoare}
\(\{x \ge 4\} \ x := x+1 \ \{x \ge 7\}\)
Donde,
\begin{itemize}
\item P = \(\{x \ge 4\}\)
\item S = \(x := x+1\)
\item Q = \(\{x \ge 7\}\)
\end{itemize}
¿Vale que \(\{P\} \ S \ \{Q\}\)?
Solo vale \(\iff x \ge 6\) por lo tanto, como la precondición P falla en los casos de x = 4, x = 5 podemos decir que la tripla de Hoare no es válida.\\
Esto que acabamos de hacer se llama demostrar la correctitud de un programa, y acabamos de demostrar que la precondición P para el programa S es demasiado débil pues no nos garantiza que llegaremos a Q cumpliendo P. \\
Existe una manera formal que nos permite conocer la precondición más débil de un algoritmo.
\newpage
\subsection*{Predicado def(E)}
Dada una expresión E, llamamos def(E) a las condiciones para que E esté definida. Todas las constantes están definidas, por lo tanto def(x) \(\equiv\) True. La idea es ir separando en términos e ir colocando las definiciones necesarias para esa operación específica.
\begin{itemize}
\item \(def(x+1) \equiv def(x) \land def(1) \equiv True \land True \equiv True\)
\item \(def(x/y) \equiv def(x) \land (def(y) \land y\neq0) \equiv True \land (True \land y>0) \equiv y\neq0\)
\item \(def(\sqrt{x}) \equiv (def(x) \land x\ge0)\)
\item \(def(a[i]+3) \equiv (def(a) \land def(i)) \yLuego 0 \le i<\longitud{a} \land def(3) \equiv (True \land True) \yLuego 0\le i<\longitud{a} \land True \equiv 0\le i<\longitud{a}\)
\end{itemize}
\subsection*{Predicado \(Q^{x}_{E}\)}
Cuando hablamos de este predicado hablamos de reemplazar las ocurrencias de x por E en el programa. Solo se reemplazan las ocurrencias libres, no las ligadas.
\subsection*{Axiomas}
\begin{itemize}
\item Axioma 1: \(wp(x := E, Q) \equiv def(E) \yLuego Q^{x}_{E}\)
\item Axioma 2: \(wp(skip, Q) \equiv Q\)
\item Axioma 3: \(wp(S1; S2, Q) \equiv wp(S_{1}, wp(S_{2}, Q))\)
\item Axioma 4: \(wp(S, Q) \equiv def(B) \yLuego ((B \land wp(S_{1}, Q)) \lor (\neg B \land wp(S_{2}, Q)))\)
\end{itemize}
\subsection*{Axioma 1 con secuencias}
El axioma 1 nos sirve para asignar una expresión a una variable; Sin embargo, si tenemos que guardar algo en una secuencia debemos utilizar el setAt.
\begin{itemize}
\item \(wp(b[i]:=E, Q) \\
\equiv def(setAt(b, i, E)) \yLuego Q^{b[i]}_{setAt(b, i, E)} \\
\equiv (def(b) \land def(i) \land def(E)) \yLuego 0\le i < \longitud{b} \yLuego Q^{b[i]}_{setAt(b, i, E)} \\
\equiv 0\le i < \longitud{b} \yLuego Q^{b[i]}_{setAt(b, i, E)} \\
\equiv setAt(b, i, E)[j] = \{E \ si \ i = j, b[j] \ si \ i \neq j\}\)
\end{itemize}
Algunos ejemplos:\\
\(wp(s[i]:=s[i-1], Q) \\
wp(setAt(s, i, s[i-1]), Q) \equiv \\
def(setAt(s, i, s[i-1])) \equiv (def(s) \land def(i)) \yLuego 0 \le i < \longitud{s} \yLuego def(s[i-1]) \equiv \\ 0\le i < \longitud{s} \yLuego (def(s) \land def(i)) \yLuego 0\le i-1 < \longitud{s} \equiv 0\le i < \longitud{s} \yLuego 1\le i < \longitud{s} + 1 \equiv \textcolor{blue}{1 \le i < \longitud{s}}
\)
TODO: Luego mostrar un ejercicio y aclarar que por cada condición se separan n cuantificadores.
\subsection*{Precondición más débil (Weakest Precondition)}
Es la precondición más débil que se necesita para poder ejecutar un algoritmo y satisfacer la postcondición Q. \\ \\
Notación: \(wp(S, Q)\) donde S es el programa y Q la postcondición. \\ \\
Teorema: Una tripla de Hoare \(\{P\} \ S \ \{Q\}\) es válida \(\iff P \implicaLuego wp(S, Q)\). \\ \\
Sea el siguiente enunciado, calcule la precondición más debil.
\begin{itemize}
\item P = \(\{x \ge 4\}\)
\item S = \(x := x+1\)
\item Q = \(\{x \ge 5\}\)
\end{itemize}
\(P \implicaLuego wp(S, Q) \equiv wp(x := x+1, x \ge 5) \equiv def(x+1) \yLuego Q^{x}_{x+1} \equiv def(x) \yLuego def(1) \yLuego x+1 \ge 5 \\ \equiv True \yLuego True \yLuego x \ge 4 \equiv x \ge 4\)
Luego, \(\{x \ge 4\} \implicaLuego \{x \ge 4\}\) es \(\True\)
Por lo tanto, \(wp(x:=x+1, x \ge 5) \equiv \{x \ge 4\}\)
Finalmente, probamos que para poder satisfacer Q la precondición más debil que cumple P es cuaqluier \(x \ge 4\). \\
Nota importante: Muchas veces puede ser que se nos solicite indicar que \(wp\) es incorrecta. Cuando se dice esto, significa que son precondiciones válidas pero hay algunas que no son la más débil.
\newpage
\section*{Precondición más débil en ciclos}
Consideremos el siguiente ejemplo
\(\{???\} S \{x=0\}\)
donde S es el siguiente programa
\begin{lstlisting}
while(x>0) do
x := x-1
endwhile
\end{lstlisting}
Recordemos que esto es una tripla de Hoare, pero no podemos utilizar wp(S, Q) porque el programa es en base a ciclos. La precondición P más débil acá sería que x\(\ge\)0 porque para cumplir la postcondición Q me da igual si entra al ciclo o no.
Eso tiene que quedar siempre claro, cuando estamos hablando en ciclos, si no entra al ciclo y satisface igual ya está.
\subsection*{Predicado \(H_{k}\)}
Definimos el predicado \(H_{k}\) para poder controlar la cantidad de iteraciones que hace un ciclo y poder calcular la precondición más débil.
\begin{itemize}
\item \(H_{0}(Q) \equiv def(B) \land \neg B \land Q\)
\item \(H_{k+1}(Q) \equiv def(B) \land B \land wp(S, H_{k}(Q)) \ para \ k \ge 0\)
\end{itemize}
\subsection*{Axiomas}
Definimos el Axioma 5 para poder verificar la correctitud de ciclos que hacen una cantidad de iteraciones fijas como \\
\[wp(while \ B \ do \ S \ endwhile, Q) \equiv (\exists_{i\ge0})(H_{i}(Q))\]
¿Cual es el problema del Axioma 5 y el Predicado \(H_{k}\)? El problema es que ambos nos sirven para poder validar la precondición de un ciclo que sabemos que finaliza luego de \(n\) iteraciones.
\subsection*{Predicado \(I\) - Invariante}
Definimos el Invariante de un ciclo como un predicado que:
\begin{itemize}
\item Demuestra que el ciclo realmente funciona y nos ayuda a demostrar la correctitud parcial "si termina el ciclo... entonces se cumple Q"
\item Vale antes de entrar al ciclo y luego de salir del ciclo.
\item En cada iteración, el invariante debe volver a ser válido. En el medio de la iteración puede que deje de valer.
\item Un buen invariante incluye el rango de la(s) variable(s) de control del ciclo.
\item Un buen invariante tiene alguna afirmación sobre el acumulador del ciclo.
\end{itemize}
\subsection*{Teorema del Invariante}
Para poder probar que un ciclo es correcto, usamos el teorema del invariante.
\begin{itemize}
\item \(P \equiv \) Las condiciones del requiere
\item \(P_{c} \equiv\) Las precondiciones que necesita el ciclo para poder ingresar, muchas veces son las líneas que están por encima del while y el requiere (¡no todas las del requiere!)
\item \(I \equiv \) Las condiciones que suceden antes y después de entrar al ciclo
\item \(B \equiv \) La guarda del ciclo
\item \(Q_{c} \equiv\) La postcondición del ciclo
\end{itemize}
Luego,
\begin{itemize}
\item \(\ \{P\} \ S \ \{P_{c}\}\)
\item \(\ P_{c} \implies I\)
\item \(\{I \land B\} \ S \ \{I\}\)
\item \(\{I \land \neg B\} \implies Q_{c}\)
\end{itemize}
\textbf{Recordatorio}: Si aparece la S es que tenemos que calcular la wp porque es parte de la Tripla de Hoare. \\
\textbf{Recordatorio importante}: Para todo lo que es wp usamos SmallLang, es decir, cuando tenemos que validar la tripla de Hoare. En todos los demás casos usamos lógica. \\
Ej: No sería válido tener un if en un invariante.
\subsection*{Teorema de Terminación de Ciclo}
Utilizamos el teorema de terminación de ciclo para garantizar que cumpliendo la precondición de un ciclo, el programa siempre termina.
Para poder probar esto, necesito una función variante \(f_{v}\). \\
Llamamos función variante \(f_{v}\) a una función que es siempre estrictamente decreciente y representa una cantidad que se va reduciendo a lo largo de las iteraciones.
La función \(f_{v}\) debe garantizar
\begin{itemize}
\item \(\{I \land B \land v_{0} = f_{v}\} \ S \ \{f_{v} < v_{0}\}\)
\item \(\{I \land f_{v} \le 0 \implies \neg B\}\)
\end{itemize}
\subsection*{Reglas generales para validar correctitud de ciclo y terminación}
\begin{itemize}
\item Cuando tenemos que iterar sobre listas, tendremos un índice i que irá hasta incluida la longitud (pues nos sirve) para negar la guarda, mientras que el j será para usar dentro del ciclo.
\((0\le i \le \longitud{s} \yLuego (0 \le j < i \implicaLuego res = \sum_{j=0}^{i}{s[j]}))\)
\item Si tengo algo en \(I\) deberá estar en \(P_{c}\) y/o \(Q_{c}\). Esto es porque cuando tengamos que hacer las implicancias, deberíamos comparar de ambos lados y si el invariante tiene algo que los demás no, es siempre falso.
\item Si tengo algo en \(P_{c}\) o \(Q_{c}\) no necesariamente tiene que estar en \(I\)
\item En \(Q_{c}\) se acostumbra a colocar que \(i = \longitud{s}\) porque nos ayuda a demostrar la terminación del ciclo.
\item En la función variante: i va negada si i crece en el ciclo; i va positivo si i decrece en el ciclo.
\item En \(Q_{c}\) rara vez tenemos que hablar de rangos de variables.
\end{itemize}
\section*{TADs (Tipos Abstractos de Datos)}
Es un tipo de datos porque define un conjunto de valores y las operaciones que se pueden realizar sobre ellos. Es abstracto ya que para utilizarlos no necesitamos saber como está implementado.
Describe el qué y no el cómo.
Son una forma de modularizar a nivel de los datos. \\ \\
Importante: En todo enunciado ambiguo, queda en nosotros interpretarlo y eliminar esas ambiguedades decidiendo como lo vamos a considerar.\\ \\
Ej: En varios ejercicios te piden que un punto deba cambiarse el centro y hay dos maneras de plantearlo
\begin{itemize}
\item Agarrar el centro que vienen por parámetro y sumar coordenada a coordenada con respecto a las de la instancia de mi TAD.
\item Agarrar el centro que viene por parámetro y considerarlo como el centro final en la instancia del TAD.
\end{itemize}
Importante: En un TAD podemos usar todos los mismos tipos de datos de especificación y sus funciones correspondientes.
\subsection*{Observadores}
Los observadores son una especie de atributo en un objeto en POO.
Nos permiten saber qué valores tienen y en qué momento.
\begin{itemize}
\item Sirven para describir el estado de una instancia de un TAD y nos permiten consultar su estado virtual.
\item Tenemos que poder observar todas las características que nos interesan de las instancias.
\item En un instante de tiempo, el estado de una instancia del TAD estará dado por el estado de todos sus observadores (como un debugger).
\item Nos permiten distinguir si dos instancias son distintas.
\item Todas las operaciones tienen que poder ser descriptas a partir de los observadores.
\end{itemize}
Ejemplo de un TAD con observadores de tipo dato:
\begin{lstlisting}
TAD lista {
obs elems: conj<T>
obs cantElems: int
}
\end{lstlisting}
Los observadores también pueden ser funciones auxiliares.
\begin{itemize}
\item Son las auxiliares de nuestro lenguaje de especificación
\item No pueden tener efectos colaterales ni modificar los parámetros
\item Pueden usar tipos de nuestro lenguaje de especificación
\end{itemize}
Ejemplo de un TAD con observadores de tipo dato:
\begin{lstlisting}
TAD lista {
obs elems: conj<T>
obs estaElem(e: T): bool
}
\end{lstlisting}
\subsection*{Igualdad Observacional}
Decimos que dos TADs son iguales \(\iff\) todos sus observadores son iguales.\\
Muchas veces no nos basta con la igualdad por defecto con los observadores, y tenemos que declarar nuestra propia igualdad observacional.
\subsection*{Operaciones de un TAD}
Las operaciones de un TAD deben estar especificadas y nos indican qué se puede hacer con una instancia.
Antes y una vez aplicada una operación tenemos que hablar del estado en el que quedó el TAD con respecto a sus observadores.
\section*{Diseño de un TAD}
Es una estructura de datos y una serie de algoritmos que nos indica cómo se representa y cómo se codifica alguna implementación del TAD.
\begin{itemize}
\item Debemos elegir la estructura de representación con tipos de datos.
\item Tendremos que escribir algoritmos para todas las operaciones.
\item Los algoritmos deben respetar la especificación del TAD. Es decir, cada procedimiento que hagamos debemos conocer los requiere y cumplir los asegura.
\end{itemize}
Nota: como hay diversas maneras de implementar un TAD, cada una puede diferir en complejidad espacial, tiempo, o sencillez de lectura / mantenibilidad.
\subsection*{Módulos}
\begin{itemize}
\item Son la representación / implementación física en código de un TAD.
\item Los módulos implementan el TAD.
\item Un procedimiento de un módulo puede llamar a otros procedimientos del módulo.
\end{itemize}
\subsection*{Variables de Estado}
Lo que en los TADs eran observadores, acá son variables de estado. Serán manipuladas por las operaciones mediante el código de los algoritmos.
Tipos válidos para variables de estado:
\begin{itemize}
\item int, real, bool, char, string
\item tupla\(<\)T1, T2, T3\(>\), struct\(<\)campo1: val1, campo2: val2\(>\)
\item Array\(<\)T\(>\) (arrays de tamaño fijo)
\item No es posible usar tipos de especificación como conj\(<\)T\(>\) o seq\(<\)T\(>\)
\end{itemize}
El módulo puede tener variables de estado que no hagan referencia a ningún observador de un TAD, por ejemplo: guardar un máximo en un módulo aunque el TAD no lo pida. A veces estas cosas se hacen solo por temas de a la hora de implementarlo en un lenguaje de programación. \\
\textbf{Nota: Una variable de estado también puede contener otro módulo.}
\subsection*{Invariante de Representación}
\begin{itemize}
\item Es un predicado.
\item Nos indica qué conjuntos de valores son instancias válidas de la implementación de un TAD.
\item Es equivalente al invariante en ciclos, pero acá aplicado a TAD's
\item Este es inicializado en el constructor una vez realizada la instancia
\item Es invariante pues vale siempre antes de llamar a los métodos y luego de cada método.
\begin{itemize}
\item Siempre y cuando tenemos un parámetro inout, tenemos que ver que los cambios que hagamos hagan seguir valiendo al invrep.
\end{itemize}
\item Todos los métodos pueden asumir que el invariante de representación siempre vale al ser llamados.
\item El parámetro que recibe el predicado de InvRep es una instancia del módulo. Esto nos sirve para poder utilizar sus variables de estado y colocarles las restricciones.
\end{itemize}
Para cualquier operación del módulo se debe de verificar la siguiente tripla de Hoare: $ \{InvRep(p')\} \ Operacion(p', ..) \ \{InvRep(p')\}$ \\ \\
Tips varios:
\begin{itemize}
\item ¡Prestar atencion a las verificaciones bidireccionales! En el sentido de que si tengo \(dict<Alarma, Conj<Sensores>>\) y \(dict<Sensor, Conj<Alarmas>>\) si en \(dict<Alarma, ...>\) tengo varios sensores, esos sensores estan en Sensor y ademas, si busco en las alarmas de cada sensor deberia estar la \(dict<Alarma, ...>\)
\item Cuando tengo un diccionario tengo que usar un cuantificador para hablar y tengo que acceder y hablar si o si por la key.
\end{itemize}
\subsection*{Función de Abstracción}
Dado el TAD y la implementación (módulo) podemos relacionarlos a ambos utilizando la función (predicado) de abstracción. Dada una instancia del módulo podemos ver a qué instancia del TAD corresponde y/o representa vinculando las variables de estado del módulo con los observadores del TAD.
\begin{itemize}
\item Es un predicado.
\item A la hora de definirlo, podemos asumir que vale el invrep. Es decir, no hace falta volver a mencionar cosas que ya están explícitos en el invrep.
\item Toma como parámetro una instancia del módulo y una instancia del TAD.
\item Hacemos referencia a observadores y a otros predicados, no procs
\item Todos los observadores del TAD deben tener un vinculo con una variable de estado pero no necesariamente al revés
\item Para poder escribir, usamos lenguaje de especificación como lo conocemos. Podemos usar por ejemplo $|a.data|$ o a.data.length da igual, pero hay que ser consistentes
\item Rara vez se colocan rangos acá, casi nunca, excepto cuando hay alguna variable de estado y observadora que hablan de capacidades.
\end{itemize}
Es importante considerar que todo lo que está en el invariante de representación ya está implícito en la función de abstracción. \\
Ej: Tenemos un módulo que tiene ventas, mayorProductoVentas, y mayorPrecio pero el TAD solo tiene ventas. En este caso, en el invariante de representación hablamos de mayorProductoVentas y mayorPrecio haciendo las relaciones con ventas (en temas de key del producto) pero en el predicado de astracción NO hablás de mayorProductoVentas y mayorPrecio primero porque no son observadores del TAD pero tampoco hay que garantizar nada porque ya está implicado por el invariante de representación
\subsection*{Operaciones de un Módulo}
Como un módulo implementa un TAD, todas sus operaciones deben estar implementados en código.
Para escribir las implementaciones de los procs usamos SmallLang.
\begin{itemize}
\item Declaración de variables:
\begin{itemize}
\item var x: int
\item var c: ConjuntoArr$<int>$
\end{itemize}
\item Asignación: x := valor
\item Condicional: if condicion then codigo else codigo endif
\item Ciclo: while condicion do codigo endwhile
\item Llamar a un proc:
\begin{itemize}
\item c.vacio()
\item b : = c.vacio();
\end{itemize}
\end{itemize}
\subsection*{Memoria dinámica}
Los tipos complejos son usados siempre por referencia. El valor indefinido es identificado por la palabra null. Acceder a algo null explota. \\
Las variables de tipos complejos deben ser inicializadas mediante el operador new.
Tipos nativos:
\begin{lstlisting}
var a: Array<int>
var b: int
if a == null then
...codigo
endif
a := new Array<int>(10)
b := a[0]
\end{lstlisting}
Tipos de otros módulos:
\begin{lstlisting}
var a: ConjArr<int>
a := new ConjArr(10) //longitud 10
\end{lstlisting}
Pasaje por referencia:
\begin{lstlisting}
var a: ConjArr<int>
var b: ConjArr<int>
a := new ConjArr(10); //longitud 10
b := a
\end{lstlisting}
Nota: El pasaje por referencia, a veces se le conoce comúnmente como aliasing y para evitarlo, hay que crear una nueva instancia con los valores de la otra instancia en vez de asignarla directamente.
\subsection*{Aliasing en procedimientos de Módulo}
Siempre hay que recordar que el módulo es lo más cercano a código que tenemos. Por lo tanto, en operaciones como concatenar, copiar, o quizá mover de un lado al otro para luego borrarlo hay que recordar el aliasing. \\
Porque por ejemplo, si tengo que mover cierta data hacia un lado y luego borrarla de de donde estaba, si la moví por referencia y después la borré, perdí todo.
\subsection*{Contrato entre métodos de un módulo}
Los métodos de un Módulo tienen un contrato entre sí.
Supongamos que necesitamos que la complejidad de búsqueda de un método de mi módulo sea o(log n) sabiendo que mi lista está ordenada. Aquí, el método busqueda podrá usar la búsqueda binaria para poder hacer el proceso lo más rápido posible, pero ¿qué sucede si al agregar un elemento en el método agregar() la lista de salida que se modifica en el módulo no está ordenada? \\ El contrato de búsqueda no se cumpliría porque la búsqueda binaria no funcionaría porque necesita que la lista esté ordenada. \\
En este caso, que la lista esté ordenada debería ser una condición del invariante de representación para evitar estos problemas. \\
Este módulo es bueno para la búsqueda de un elemento pero el agregar un elemento es mucho más costoso por el tema de reordenar la lista nuevamente. \\
\textbf{Mirada al futuro}: Los métodos deben cumplir una complejidad algorítimica dada. \\
Véase \hyperref[subsec:anexo_invrep_abs]{\underline{anexo}} para ejercicios de invariante de representación y función de abstracción
\section*{Estructuras de Datos}
\subsection*{Colección}
Representa un grupo de objetos. Provee de una arquitectura para su almacenamiento y manipulación.
\begin{itemize}
\item Secuencia
\item Conjunto
\item Multiconjunto
\item Diccionario
\end{itemize}
¿Por qué una tupla no es parte del grupo? Porque la tupla es fija, una vez definida e inicializada su longitud no cambia.
\subsection*{Tipos paramétricos}
Son variables de tipo, es decir, variables con tipo genérico.
La manera más común de indicar un genérico son T, K o V. \\
Hay que tener cuidado con las operaciones que se realizan con las variables genéricas porque algunos operadores nos restringuen su uso a ciertos tipos. \\
Ej: No puedo utilizar X - Y si X, Y son genéricos y nos envían X, Y como char.
\subsection*{Iteradores}
Nos permiten recorrer colecciones de una manera abstracta sin saber su estructura. \\
Un buen iterador se distingue de otro iterador por la velocidad de iterar la estructura. \\
Operaciones con iteradores:
\begin{itemize}
\item ¿Estoy sobre un elemento?
\item Obtener el elemento actual
\item Avanzar al siguiente elemento
\item Retroceder al elemento anterior (sii es bidireccional)
\end{itemize}
Los Iteradores son una clase privada que van dentro de la clase que queremos que se instancie y se pueda recorrer. \\
Es privada porque no queremos que sea instanciada más que por la instancia de la clase que queremos recorrer. \\
Los Iteradores no van hasta n, van hasta n+1.
\begin{figure}[h]
\includegraphics[]{assets/iterador.png}
\end{figure}
\begin{lstlisting}
public class Vector<T> implements List<T>{
private T[] elementos;
private int size;
private class Iterador implements Iterator<T>{
int indice;
Iterador(){
indice = 0;
}
public boolean hasNext(){
return indice != size;
}
public T next(){
int i = indice;
indice = indice + 1;
return elementos[i];
}
}
public Iterator<T> iterator(){
return new Iterador();
}
}
Iterator it = vector.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
\end{lstlisting}
\subsection*{Singly Linked Lists - Listas simplemente enlazadas}
Es una estructura que sirve para representar una secuencia de elementos donde cada elemento es un Nodo que tiene un valor y una referencia al siguiente. \\
Nota: Si el Nodo actual es el último de la Singly Linked List lo notamos porque el ultimo.siguiente = null.
El Nodo debe ser responsable de guardar al siguiente porque caso contrario, no existirá otra referencia al siguiente Nodo.
Ventajas de las Singly Linked Lists:
\begin{itemize}
\item Mas fino uso de la memoria.
\item Insertamos fácilmente al principio y al final.
\item El costo de insertar al final es o(n) pues debemos recorrer todos los nodos para insertar uno nuevo. Es por eso que es común guardar el último nodo almacenado en la clase.
\item Eficiente para reacomodar elementos.
\item Es malo en rendimiento o(n) si necesito buscar un elemento en específico. Es decir, perdemos el acceso aleatorio a los elementos.
\end{itemize}
\subsection*{Double Linked Lists - Listas doblemente enlazadas}
Exactamente igual que la Singly Linked Lists pero acá los Nodos tienen almacenado: Referencia al nodo previo, valor y referencia al nodo siguiente. \\
Nota: Si el Nodo actual es el último de la Double Linked List lo notamos porque el ultimo.siguiente = null.
\begin{lstlisting}
public class DoubleLinkedList<T> {
private Nodo primero;
private int longitud;
private class Nodo {
Nodo prev;
Nodo sig;
T valor;
public Nodo(T val){
this.val = val;
}
}
public DoubleLinkedList(){
this.longitud = 0;
}
}
\end{lstlisting}
\subsection*{Listas vs Linked Lists}
Ambas son bastante rápidas a la hora de iterar sobre ellas, sin embargo:
\begin{itemize}
\item Las listas nos permiten acceder rápidamente a un elemento mientras que las Linked Lists no.
\item Las Linked Lists nos permiten agregar elementos rápidamente al inicio (O(1)) y no necesitamos reacomodar nada más que decir que el anterior primero ahora es el segundo mientras que en la lista tenemos que reacomodar todos los elementos como antes pero colocar el nuevo al principio (O(n))
\end{itemize}
\section*{Complejidad Algorítmica}
\subsection*{Analisis de la Complejidad de Algoritmos}
Nos permite elegir entre distintos algoritmos para resolver el mismo problema o distintas formas de implementar un TAD.
Esto es importante porque nos permite optimizar:
\begin{itemize}
\item Tiempo de ejecución
\item Espacio (memoria)
\item Cantidad de procesadores (en caso de algoritmos paralelos)
\item Utilización de la red de comunicaciones (para algoritmos paralelos)
\end{itemize}
El análisis se puede hacer de forma experimental o teórica \\
\textbf{Ventajas del enfoque teórico}:
\begin{itemize}
\item Se hace antes de escribir código
\item Vale para todas las instancias del problema (no en un caso específico)
\item Es independiente del lenguaje de programación
\item Es independiente de la máquina donde se ejecuta (el tiempo no varía)
\item Es independiente del programador
\end{itemize}
\subsection*{Análisis Teórico}
\begin{itemize}
\item Se realiza en función del tamaño del input
\item Para distintos tipos de input
\item Análisis asintótico
\end{itemize}
\subsection*{Operaciones Elementales}
T(l) será una función que mida el número de operaciones elementales requeridas para la instancia l.
Las operaciones elementales (OE) serán aquellas que el procesador realiza en tiempo acotado por una constante (no depende del tamaño de la entrada).
Ej: x = 0, if(x = 2).
Es decir, podemos generalizar las operaciones elementales a:
\begin{itemize}
\item Operaciones aritméticas básicas (suma, resta, división, multiplicación)
\item Comparaciones y/o operaciones lógicas
\item Acceder a elemento de array
\item Asignaciones a variables de tipos básicos (las inicializaciones no consumen tiempo)
\end{itemize}
\subsection*{Cálculo de Operaciones Elementales}
\begin{itemize}
\item T(If C Then S1 Else S2 Endif;) = T(C) + max\{t(S1), t(S2)\}
\item T(Case C Of v1:S1 \(|\) v2:S2 \(|\) ... \(|\) vn:Sn End;) = T(C) + max\{t(S1), t(S2), ..., T(Sn)\}
\item T(While C Do S End;) = T(c) + (nº iteraciones) * (T(S) + T(C))
\item T(MiFuncion(P1, P2, ..., Pn)) = 1 + T(P1) + T(P2) + ... + T(Pn)
\end{itemize}
\subsection*{Tamaño de la entrada}
\begin{itemize}
\item T(n): complejidad temporal (o en tiempo) para una entrada de tamaño n
\item S(n): complejidad espacial para una entrada de tamaño n
\end{itemize}
\textbf{Importante}: NUNCA hay que restringir la longitud de entrada de una lista; Por ejemplo: no tiene decir que el mejor caso sea que la lista esté vacía, porque la complejidad se da en base a listas de longitud n sin ningún tipo de restricción. Por lo tanto, si algo está acotado en una longitud fija, es siempre complejidad O(1). \\
Véase \hyperref[subsec:ejemplos_complejidad_reales]{\underline{casos de uso}} reales en Complejidad Algorítmica.
\subsection*{Análisis de los casos}
\begin{itemize}
\item T\(_{mejor}(n)\) = min\(_{instancias \ l},\ _{\longitud{l} \ = \ n}\ \) \{t(l)\}. Recorriendo una lista, el mejor caso es que esté en primera posición.
\item T\(_{peor}(n)\) = max\(_{instancias \ l},\ _{\longitud{l} \ = \ n}\ \) \{t(l)\}. Recorriendo una lista, el peor caso es que el elemento no esté.
\item T\(_{prom}(n)\) = No lo vamos a usar pero, es algo más parecido a estadística.
\end{itemize}
Cuando el tamaño de datos es grande, los costos de los diferentes algoritmos pueden variar de manera significativa. En tamaño de datos chico, no nos interesa el tiempo de ejecución.
\subsection*{Principio de Invarianza}
Si dos algoritmos solo varían en una constante, entonces no nos importa porque la complejidad es la misma.
\[T_{1}(n) \le cT_{2}(n)\]
c: constante real \(c>0\) \\
\(n_{0} \in N\) tales que \(\forall n \ge n_{0}\)
\subsection*{Comportamiento Asintótico}
Comportamiento para los valores de la entrada suficientemente grandes. \\
Nota: Las funciones f y g dependen de un n. No existen funciones constantes menores que O(1).
\subsection*{O (cota superior)}
\[ f (n) \in O(g(n)) \iff \exists c \in R>0, n_{0} \in N \ tal \ que \
\forall n \ge n_{0} : f(n) \le c \ast g(n) \]
La notación f \(\in\) O(g) expresa que la función f no crece más rápido que alguna función proporcional a g. f está acotada superiormente por g. \\
\begin{itemize}
\item \(n \ast log(n) \in O(n^{2})\)
\item \(n \in O(n)\)
\item \(n^{2} \notin O(n)\)
\end{itemize}
\[\begin{minipage}[b]{1\textwidth}
\includegraphics[width=\linewidth]{assets/notacion_o.jpg}
\end{minipage}\]
Nota: Si para un algoritmo sabemos que $T_{peor} \in O(g)$ se puede asegurar que para inputs de tamaño creciente, en todos los casos el tiempo será a lo sumo proporcional a la cota.
\subsection*{\(\Omega\) (cota inferior)}
\[ f (n) \in \Omega(g(n)) \iff \exists c \in R>0, n_{0} \in N \ tal \ que \
\forall n \ge n_{0} : f(n) \ge c \ast g(n) \]
La notación f \(\in \Omega(g)\) expresa que la función f crece al menos como g. f está acotada inferiormente por g.
\begin{itemize}
\item \(n/2 \in \Omega(n)\)
\item \(n^{k} \in \Omega(n)\)
\item \(log(n) \notin \Omega(n)\)
\end{itemize}
\[\begin{minipage}[b]{1\textwidth}
\includegraphics[width=\linewidth]{assets/notacion_omega.jpg}
\end{minipage}\]
Nota: Si para un algoritmo sabemos que $T_{peor} \in \Omega(g)$ se puede asegurar que para inputs de tamaño creciente, el tiempo será en el pero caso al menos proporcional a la cota.
\subsection*{\(\theta\) (orden exacto)}
\[f (n) \in \theta(g(n)) \iff f (n) \in O(g(n)) \ y \ f (n) \in \Omega(g(n)).\ Es \ decir, \ \theta(g(n)) = O(g(n)) \cap \Omega(g(n))\]
Básicamente: Tiene que valer en O y en \(\Omega\), no debe pasarse de \(\Omega\) pero tampoco estar por debajo de O.
\[\begin{minipage}[b]{1\textwidth}
\includegraphics[width=\linewidth]{assets/notacion_theta.jpg}
\end{minipage}\]
\subsection*{Definición parecida a inducción corrida en Complejidad}
Tanto en la definición de \(O, \ \Omega \ y \ \theta\) se nombra para un \(n > n_{0}\). Esto es porque no todas las funciones cumplirán la definición, sino aquellas que son mayores a \(n_{0}\). Es por eso que en las imágenes se ven como inicialmente no cumple, pero a partir de un \(x_{0} \ (n_{0})\) vale para todo n.
\subsection*{Propiedades}
\(\star\) es comodín de O, $\Omega$, $\Theta$
\begin{itemize}
\item Suma: \(\star(f) + \star(g) = \star(f+g) = \star(max\{f,g\})\)
\item Producto: \(\star(f) \ast \star(g) = \star(f \ast g)\)
\item Reflexividad: \(f \in \star(f)\)
\item Simetría: Solo vale en \(\theta\) = \(f \in \theta(g) \implies g \in \theta(f)\)
\item Transitividad: \(f \in \star(g) \land g \in \star(h) \implies f \in \star(h)\)
\item Inclusión: \(f \in \star(g) \implies \star(f) \subseteq \star(g) \)
\item Igualdad: \(\star(f) = \star(g) \iff f \in \star(g) \land g \in \star(f)\)
\item Constantes: \(f \in \star(g) \implies k \ast f \in \star(g)\) k cte.
\end{itemize}
Véase \hyperref[subsec:complejidad_propiedades]{anexo} para ejercicios aplicando propiedades.
\subsection*{Intuición de Análisis de Complejidad posibles}
\begin{itemize}
\item Mejor caso != Peor caso (cuando el ciclo corta antes)
\item Mejor caso = Peor caso (cuando el ciclo no tiene condición de corte)
\item Cuando tengo ramas if else, la complejidad mayor se dará por la rama que tenga mayor cantidad de operaciones en cuanto a ciclos.
\item Cuando tenga guardas como por ejemplo, while $b>0$ y la variable se va dividiendo entre dos, la complejidad será algo parecida a log n.
\item Cuando una función recibe dos parámetros, pero uno de ellos no tiene un rol fundamental en algún ciclo o guarda, entonces raramente esté en el cálculo final de la complejidad.
\item Recordar que es normal ver casos donde se aplique la sumatoria de gauss o tengamos que masajear la expresión para que aparezca.
\item Si la guarda del ciclo se incrementa de manera: $i*2$ o $i/2$ entonces la complejidad del ciclo será logarítmica.
\item Para poder calcular la complejidad, si tenemos una suma basta con quedarnos con el término más grande. (ej: n + m tiene 2 casos)
\end{itemize}
\subsection*{Cota Ajustada}
Usamos \(\theta\) para indicar cotas ajustadas al momento de tener que calcular el peor y el mejor caso.
\subsection*{Complejidad Algorítmica en Polinomios}
Usamos límites para estos casos \\
Sean f, g: \(\mathds{N} \implies \float_{>0}\). Si existe:
\[ \lim_{n\to\infty} f(x)/g(n) = l \in \float_{\ge 0} \cup \{ +\infty \} \]
\begin{itemize}
\item \(f \in \theta(g) \iff 0<l<+\infty\)
\item \(f \in O(g) \ y \ f \notin \Omega(g) \iff l = 0\)
\item \(f \in \Omega(g) \ y \ f \notin O(g) \iff l = +\infty\)
\end{itemize}
\section*{Complejidad en procedimientos de módulo}
Se calculan igual que observando una función dada que hace algo. Lo único que quería aclarar acá, es que siempre para las eliminaciones o búsquedas nunca se usen (en caso de haber) punteros que tengan referencia al último valor. El caso excepcional es el primero, pero el último hay que buscarlo con la forma de recorrer la estructura tradicional que haya. \\
Véase \hyperref[subsec:complejidad_modulo_sll]{anexo} para ver un ejemplo al respecto.
\section*{Árboles / Árboles binarios}
Se definen recursivamente y sus operaciones necesitan de ella. Utilizamos nodos. \\
Terminología que vamos a usar: Raíz \& Hoja \\
Para poder obtener un elemento de un árbol se debe empezar por la raíz y recorrer cada una de las hojas recursivamente. \\ \\
OBS: Las hojas pueden tener o no nodos hijos; si tienen hijos entonces también son árboles. \\
\textbf{Árboles binarios}
Son aquellos en los cuales cada nodo tiene máximo 2 elementos.
\subsection*{Árboles binarios de búsqueda (ABB)}
Para todo nodo, los valores del subárbol izquierdo son menores que el valor del nodo y los valores del subárbol derecho son mayores. \\
\begin{minipage}[b]{0.8\textwidth}
\includegraphics[width=\linewidth]{assets/abb-1.jpg}
\centering
\label{fig:abb-1}
\end{minipage} \\
Nótese que de ninguna rama derecha puede haber un número mayor a la raíz; de ninguna rama izquierda puede haber un número menor a la raíz. \\
La complejidad de búsqueda en el peor caso es de $O(log n)$
Algoritmos comúnes en árboles binarios de búsqueda:
\begin{itemize}
\item Arbol vacío: Aquel que tiene la raíz en null
\item Búsqueda de elemento: Si el nodo es null o el elem es igual al valor del nodo devuelvo n. Caso contrario, si el valor a buscar es menor al dato del nodo entonces busco más a la izquierda, caso contrario busco a la derecha. Cuando busca, habla de llamar a la misma función recursivamente. Complejidad O(n) con n la altura del árbol porque pasa solo una vez por cada nodo.
\item Insertar elemento: Primero agarro la raiz del árbol, si la raiz es null entonces no hago nada. Si el árbol no tiene una raíz, entonces mi elem es la raíz; Si ya tiene una raíz me fijo si el elem a insertar es menor o mayor a la raíz, si es mayor entonces voy al primer hijo derecho de la raíz (no entiendo la parte final) dice algo como si el elem<prev.dato then padre.izq = newnodo osea tiene sentido pero en el pseudocodigo prev no existe enn ningun lado definido. Complejidad O(n) para agregar, aunque si hay distribución uniforme de las claves O(log n)
\item Eliminar elemento: hay 3 casos. Sea u una variable cualquiera
\begin{itemize}
\item u es una hoja: Si u es una hoja significa que no tiene hijos, por lo tanto lo puedo eliminar sin necesidad de reordenar nada.
\item u tiene un solo hijo: Si u es una hoja con un solo hijo (a la izq o a la derecha) basta con mover el hijo a la posición del nodo a eliminar.
\item u tiene dos hijos: Si u es un nodo con dos hijos hay que encontrar al predecesor inmediato (v) de u. \textbf{v no puede tener dos hijos, en caso contrario no es predecesor inmediato}, una vez encontrado copiar la clave de v en lugar de la de u, borrar el nodo v (acá hay que revisar qué sucede si tiene un hijo, iría al caso 2, caso contrario caso 1). Lo mismo se puede hacer con el sucesor inmediato (mismas reglas que predecesor inmediato).
\item Complejidad O(n) para cualquier tipo de borrado.
\end{itemize}
\end{itemize}
\textbf{Aclaración}: Predecesor inmediato (el más grande de los chiquitos), osea rama izquierda busco el último de la der a partir del nodo que estoy. \\
\textbf{Aclaración}: Sucesor inmediato (el más chiquito de los grandes), osea rama derecha busco el último a la izquierda a partir del nodo que estoy. \\
\subsection*{ABB con complejidad O(n) en ciertas operaciones}
Árbol binario con n nodos anidados todos mayores o todos menores.
\begin{center}
\begin{minipage}[b]{0.7\textwidth}
\includegraphics[width=\linewidth]{assets/peor_caso_abb.jpg}
\centering
\label{fig:peor_caso_abb}
\end{minipage}
\end{center}
¿Cuál es el problema de los ABB? que al hacer inserciones, eliminaciones o ver si un elemento dado pertenece al árbol, el costo en el peor caso es O(n). \\
Para optimizar esto, existen los AVL que nos ponen restricciones en base a la cantidad de nodos que puede haber anidados en base ciertas reglas de balanceo.
\subsection*{ABB balanceados (AVL)}
Un árbol AVL es un ABB balanceado en altura. Un árbol binario perfectamente balanceado de n nodos tiene una altura de: $ log_{2}(n) + 1 $ donde las hojas son más del 50\% de los nodos \\
La inserción y el borrado se hace exactamente igual que en un ABB pero acá, como extra se hace el rebalanceo. \\
Se define el factor de balanceo de un nodo v de un árbol binario como: \\
\[fb(v) = A_{D} - A_{I}\] donde $ A_{x}$ es la altura del subárbol x \\
Existen 3 posibles factores de balanceo para cada nodo:
\begin{itemize}
\item -1: La cantidad de ramas de la izquierda del nodo son más que la rama derecha.
\item 0: La cantidad de ramas a la izquierda o derecha del nodo son la misma cantidad.
\item 1: La cantidad de ramas de la derecha del nodo son más que la rama izquierda.
\end{itemize}
Llamamos entonces, árbol balanceado para cuando para cualquier nodo en él, la diferencia de longitud entre sus ramas izquierda y derecha difiere, a lo sumo, en una unidad. \\
\textbf{Nota: los caminos no nos importan mucho, considerar cada nodo como un árbol distinto. Si los tengo en misma altura pero uno está más abajo que otro, está desbalanceado. }
\begin{center}
\begin{minipage}[b]{0.5\textwidth}
\includegraphics[width=\linewidth]{assets/avl.png}
\centering
\label{fig:avl}
\end{minipage}
\end{center}
\begin{itemize}
\item Negro: Factor de balanceo 0.
\item Rojo: Factor de balanceo -1.
\item Azul: Factor de balanceo 1.
\end{itemize}
\subsection*{Mantener balanceo de AVL al insertar o eliminar}
Una de las dificultades más grandes de mantener el AVL invariante luego de cada operación es mantenerlo balanceado. \\
Viendo la figura de la izquierda: es un AVL porque si vemos, la rama de la izquierda del 10 son 2 nodos y la derecha 1 y difieren en un solo nodo, por lo tanto está balanceado.
\begin{center}
\begin{minipage}[b]{0.7\textwidth}
\includegraphics[width=\linewidth]{assets/avl_problematica.png}
\centering
\label{fig:avl_problematica}
\end{minipage}
\end{center}
Viendo la figura de la derecha, no está balanceado: porque la rama de la izquierda del 10 son 3 nodos (altura 3) y la derecha 1 (altura 1), y difieren en 2 nodos, por lo tanto no está balanceado. \\
¿Cómo solucionamos esto para mantener el invariante? Rotaciones
\newpage
\subsection*{Rotaciones}
Existen muchas rotaciones posibles para un árbol, pero las elegimos dependiendo de la estructura del árbol que tengamos que rebalancear. \\
Es importante que cada vez que hacemos una rotación, el invariante debe seguir valiendo. \\
\begin{center}
\begin{minipage}[b]{0.7\textwidth}
\includegraphics[width=\linewidth]{assets/avl_rotaciones.png}
\centering
\label{fig:avl_rotaciones}
\end{minipage}
\end{center}
Nótese que al rotar el árbol, sigue valiendo el invariante que $ A < x < B < y < C $, esto nos indica que al rotar conseguimos el mismo árbol. \\ \\
\textbf{¿Qué rotación tengo que usar?}
\begin{itemize}
\item Left-Left(LL o II): Ocurre cuando un nodo tiene un subárbol izquierdo que está desbalanceado a la izquierda. La solución es una rotación simple a la derecha. Véase \hyperref[subsec:rotaciones_avl]{anexo}
\item Right-Right(RR o DD): Ocurre cuando un nodo tiene un subárbol derecho que está desbalanceado a la derecha. La solución es una rotación simple a la izquierda. Véase \hyperref[subsec:rotaciones_avl]{anexo}
\item Left-Right(LR o ID): Ocurre cuando un nodo tiene un subárbol izquierdo que está desbalanceado a la derecha. La solución son dos rotaciones: primero una rotación a la izquierda en el subárbol izquierdo y luego una rotación a la derecha en el nodo. Véase \hyperref[subsec:rotaciones_avl]{anexo}
\item Right-Left(RL o DL): Ocurre cuando un nodo tiene un subárbol derecho que está desbalanceado a la izquierda. La solución son dos rotaciones: primero una rotación a la derecha del subárbol derecho y luego una rotación a la izquierda en el nodo. Véase \hyperref[subsec:rotaciones_avl]{anexo}
\end{itemize}
Consejos útiles:
\begin{itemize}
\item Muchas veces, una rotación no siempre es suficiente para rectificar un nodo que está desequilibrado.
\item El rebalanceo (realiza las rotaciones) se invoca para todos los nodos de la rama hasta la raíz (hay que mandar el árbol entero).
\item El máximo de rotaciones consecutivas para balancear un árbol es de 2.
\item Al insertar un nodo en un AVL, el costo es O(log n). El costo de inserción es O(log n) y luego para rebalancear tambien, O(log n).
\end{itemize}
\begin{center}
\begin{minipage}[b]{0.7\textwidth}
\includegraphics[width=\linewidth]{assets/balanceo_avl.jpg}
\centering
\label{fig:balanceo_avl}
\end{minipage}
\end{center}
\section*{Cola de Prioridad}
Es exactamente igual que el TAD Cola, pero difiere en la forma en que removemos elementos. No quitamos el primero que ingresó, sino que lo sacamos en base a un factor de prioridad que definimos a la hora de armar el TAD. \\
Si llegase a suceder que un mismo elemento tiene la misma prioridad, debería especificarse cual se quitaría. \\
La prioridad la expresamos con un entero, pero puede ser cualquier tipo $ \alpha$ que pueda ser comparado con un orden $<_{\alpha}$. \\
\textbf{Ej.}: Imaginemos que vamos a una guardia, y estamos primeros a punto de ser atendidos pero llega alguien que está en estado grave. Esta persona va a ser atendida antes que nosotros aunque hayamos llegado antes. En ese caso, es una cola de prioridad pues la prioridad está dada por la gravedad del paciente. \\ \\
Casos de uso:
\begin{itemize}
\item Sistemas operativos
\item Algoritmos de Scheduling
\item Gestión de colas
\end{itemize}
Véase \hyperref[subsec:cola_de_prioridad_avl]{\underline{anexo}} para un ejercicio de elección de estructuras para armar una cola de prioridad.
\section*{Heaps}
Es la implementación del TAD ColaPrioridad. Tiene la misma complejidad algorítmica que un árbol AVL pero es más fácil y elegante de implementar. \\
Invariante de representación:
\begin{itemize}
\item Árbol binario perfectamente balanceado (difiere a lo mucho en un único nodo)
\item El último nivel está lleno de nodos desde la izquierda (es izquierdista). Cuando el nivel del la rama izquierda ya difiere en uno con la derecha, agrego en las hojas de la rama derecha para dejar el árbol con misma altura, pero la idea es agregar de izquierda a derecha siempre.
\item La clave (prioridad) de cada nodo es \textbf{mayor o igual} que la de sus hijos (si es que tiene)
\item Todo súbarbol es otro heap.
\item NO es un ABB. En un ABB el hijo derecho es más grande que el padre. En un Heap ambos hijos de un Nodo son menores.
\end{itemize}
Min-Heap: Min hace referencia a que el proceso de extraer, se hace sacando el mínimo en O(1). Si sacamos el mínimo en O(1) significa que todos los nodos debajo de la raíz son menores estrictos a él. \\
Max-Heap: Max hace referencia a que el proceso de extraer, se hace sacando el máximo en O(1). Si sacamos el máximo en O(1) significa que todos los nodos debajo de la raíz son mayores estrictos a él. \\
Véase \hyperref[subsec:operaciones_heaps]{\underline{operaciones heaps}} para un ejemplo más visual de las operaciones \\
Véase \hyperref[subsec:implementaciones_heap]{\underline{implementaciones heaps}} de maneras diversas.
\subsection*{Complejidad en operaciones con Heaps}
Nota: acá hablamos de un max-heap. recordemos que los que están arriba son mayores estrictos que los hijos.
\begin{itemize}
\item Próximo: O(1) $\rightarrow$ raíz del árbol o primer elemento del array.
\item Encolar(elemento): O(log n) $\rightarrow$ Sin necesidad de conocer el elemento, lo que tenemos que tratar es llenar el árbol de manera izquierdista. Hay dos casos
\begin{itemize}
\item Si el elemento que ingresé en el lugar libre es \textbf{menor a su padre, dejo todo como está.}
\item Si el elemento que ingresé en el lugar libre es mayor a su padre, \textbf{hago el swap con el padre}. Por lo tanto ahora tengo garantizado que el elemento nuevo es el padre y los hijos son menores a este (vuelve a valer el invariante).
\item \textbf{Algoritmo}: Insertar elemento al final del heap, y luego subir(elemento)
\end{itemize}
\item Subir(elemento) o Sift-Up: Mientras que el elemento no sea la raíz, y la prioridad del elemento sea mayor al padre, entonces intercambio \textbf{el elemento con el padre}.
\item Desencolar(elemento): O(log n) $\rightarrow$ Son varios pasos