You are on page 1of 5

NOTACION O-GRANDE

1.-Introducción
Supongan el siguiente juego: “un jugador piensa un número que está entre 1 y 1.000, el
otro debe tratar de adivinarlo haciéndole preguntas al primero, a las que sólo se puede
responder con un Sí o un No.”
Una manera de hacerlo es preguntando por cada número secuencialmente: “¿es el 1?”,
respuesta: “No”. “¿Es el 2?”, “No”. “Es el 3”, “No”. etc.
Si la persona pensó en el 999 o en el 1.000 esta no es la forma más eficiente de adivinar el
número. Podemos preguntar en forma “saltada” o aleatoria: “¿Es el 523?”, “No”. “¿Es el
256?”, “No”…
Esa forma requiere que anotemos en algún cuaderno o libreta cuales números hemos
preguntado. El problema de hacer eso es que al generar los números de forma aleatoria
tendríamos que ir revisando la lista que llevamos escrita para evitar repetirlos.
Una mejora sería tener una hoja de papel con los números del 1 al 1000 donde vamos
tachando los ya preguntados, esto permite ver de forma directa lo preguntado sin tener que
revisar nuestra libreta de notas.
Tanto si hacemos las preguntas de forma secuencial, como si las hacemos de forma
aleatoria, la cantidad de preguntas puede ir entre 1 a 999. Haremos sólo 1 pregunta si
adivinamos el número a la primera, si tenemos mala suerte tendremos que hacer 999
preguntas. A menos que estemos muy desconcentrados nunca haremos más de 1.000
preguntas. Si el juego consiste en adivinar un número entre 1 y N, la cantidad de preguntas
que haremos también será un número entre 1 y N, y nunca haremos más de N preguntas.
Pero hay otra manera de resolver este problema. Podemos partir por el medio y hacer la
siguiente pregunta: “¿El número que piensas es menor que 500?”, el otro jugador podría
responder “Sí”, lo que significa que el número tiene que estar entre 1 y 499, con esta
simple pregunta hemos reducido el universo de valores posibles a adivinar a la mitad.
Podemos volver a preguntar: “¿El número es menor que 250?”, un “Sí” como respuesta
reduce el universo al rango 1 a 249. Preguntamos por tercera vez: “¿es el número menor
que 125?”, y si la respuesta es un “No”, sabemos que el número tiene que estar entre 125
y 249 y podemos seguir así, siempre dividiendo el rango por la mitad.
Lo interesante es que de esta forma nunca haremos más de 10 preguntas para adivinar el
número. Si tenemos que adivinar un número entre 1 y un millón tendremos que hacer a lo
más unas 20 preguntas. En general, para cualquier número N haremos a lo más una
cantidad de preguntas menor o igual al logaritmo en base 2 de N. Esto, porque siempre
dividimos el rango de búsqueda en dos. Además con esto hemos descubierto un algoritmo
eficiente para adivinar el número.

2.-Definición
La notación Big O es la representación
relativa de la complejidad de un algoritmo.
¿Qué significa?
Representación: Big O trata de simplificar la
comparación entre algoritmos a una sola
variable
relativa: esta comparación solo tiene sentido
si se trata de algoritmos similares, no sirve
de mucho comparar un algoritmo de
búsqueda con un algoritmo de ordenamiento.
Complejidad: ¿cómo se comporta el
algoritmo cuando la entrada de datos aumenta?, ¿necesita más memoria? ¿Necesita más
tiempo?
3.-Caracteristicas
 El análisis de algoritmos estima el consumo de recursos de un algoritmo.
 Esto nos permite comparar los costos relativos de dos o más algoritmos para resolver el
mismo problema.
 El análisis de algoritmos también les da una herramienta a los diseñadores de algoritmos
para estimar si una solución propuesta es probable que satisfaga las restricciones de
recursos de un problema.
 El concepto de razón de crecimiento, es la razón a la cual el costo de un algoritmo crece
conforme el tamaño de la entrada crece.
 Usualmente se mide el tiempo de ejecución de un algoritmo, y el almacenamiento primario
y secundario que consume.
 Consideración principal para estimar el desempeño de un algoritmo, es el número de
operaciones básicas requeridas por el algoritmo para procesar una entrada de cierto
tamaño.
4.-Complejidad del algoritmo
O(1) Complejidad Constante
Items Tiempo
Significa que la cantidad de datos es irrelevante para el
algoritmo, siempre va a tardar el mismo tiempo en terminar.
1 k segundos
Lamentablemente es difícil que un algoritmo no trivial caiga
en esta categoría.
Asumiendo que k es una cantidad constante 10 k segundos
Ejemplos
–Acceder al elemento de un arreglo 100 k segundos
–Insertar en elemento en una lista ligada
–Añadir o sacar un elemento de una pila
O(log n) Complejidad Logarítmica Items Tiempo
En este caso el tiempo incrementa conforme aumenta el
número de elementos, pero de forma logarítmica por lo que 1 1 segundo
para grandes cantidades de datos el tiempo es
relativamente corto.
Ejemplos 10 2 segundos
–Búsqueda binaria
–Encontrar menor/mayor elemento en un árbol binario de 100 3 segundos
búsqueda
–Algunos algoritmos “divide y vencerás” 1000 4 segundos

10000 5 segundos

O(n) Complejidad Lineal Items Tiempo


¿Vas a incrementar la cantidad de elementos? Seguro, pero
el tiempo va a incrementar de forma proporcional. 1 1 segundo
Ejemplos
–Recorrer un arreglo o lista ligada 10 10 segundos
–Búsqueda lineal
–Comparar dos cadenas 100 100 segundos
O(n log n)
Esta es una combinación de las dos anteriores, es común Items Tiempo
encontrar varios algoritmos de ordenamiento en esta
categoría. 1 1 segundo
Ejemplos
–Merge Sort 10 20 segundos
–Heap Sort
–Quick Sort 100 300 segundos

O(n^2) Complejidad Cuadrática Items Tiempo


La cantidad de tiempo que tarda el algoritmo comienza a
dispararse, no es muy eficiente con una gran cantidad de 1 1 segundo
datos.
Ejemplos 10 100 segundos
–Bubble sort
–Selection sort
100 10000 segundos
–Iterar un arreglo de 2 dimensiones

5.-Consideraciones
Ahora veamos qué sucede cuando
un algoritmo no se representa
exactamente en una de las
categorías anteriores, por
ejemplo: O(n + 2)
Aquí sólo nos interesa O(n), porque
conforme el número de elementos
aumente la constante 2 será cada
vez más insignificante.
Lo mismo ocurriría con una
complejidad como O(n^2 + 2n + 1).
Donde solo tomamos en cuenta la
potencia más grande O(n^2)
Al analizar un algoritmo existen tres
casos comunes:
–Mejor caso (best case)
–Caso promedio (average case)
–Peor caso (worst case)
Generalmente el más representativo es el peor caso porque nos indica la categoría de
complejidad más alta de un algoritmo, para entender esto considera un algoritmo de
búsqueda lineal, su complejidad es O(n) por lo tanto en el peor de los casos tenemos que
recorrer todo el arreglo, en el caso promedio solo la mitad de elementos O(n/2), y en el
peor de los casos el primer elemento es el que buscamos O(1).
6.-Aplicación e importancia
Para captar la importancia relativa de los órdenes de complejidad conviene echar algunas
cuentas.

Sea un problema que sabemos resolver con algoritmos de diferentes complejidades. Para
compararlos entre sí, supongamos que todos ellos requieren 1 hora de ordenador para
resolver un problema de tamaño N=100.
¿Qué ocurre si disponemos del doble de tiempo? Nótese que esto es lo mismo que
disponer del mismo tiempo en un ordenador el doble de potente, y que el ritmo actual de
progreso del hardware es exactamente ese:

"duplicación anual del número de instrucciones por segundo".

¿Qué ocurre si queremos resolver un problema de tamaño 2n?

O(f(n)) N=100 t=2h N=200


log n 1h 10000 1.15 h
n 1h 200 2h
n log n 1h 199 2.30 h
n2 1h 141 4h
n3 1h 126 8h
2n 1h 101 1030 h

Los algoritmos de complejidad O(n) y O(n log n) son los que muestran un comportamiento
más "natural": prácticamente a doble de tiempo, doble de datos procesables.
Los algoritmos de complejidad logarítmica son un descubrimiento fenomenal, pues en el
doble de tiempo permiten atacar problemas notablemente mayores, y para resolver un
problema el doble de grande sólo hace falta un poco más de tiempo (ni mucho menos el
doble).
Los algoritmos de tipo polinómico no son una maravilla, y se enfrentan con dificultad a
problemas de tamaño creciente. La práctica viene a decirnos que son el límite de lo
"tratable".
Sobre la tratabilidad de los algoritmos de complejidad polinómica habría mucho que hablar,
y a veces semejante calificativo es puro eufemismo. Mientras complejidades del orden
O(n2) y O(n3) suelen ser efectivamente abordables, prácticamente nadie acepta algoritmos
de orden O(n100), por muy polinómicos que sean. La frontera es imprecisa.
Cualquier algoritmo por encima de una complejidad polinómica se dice "intratable" y sólo
será aplicable a problemas ridículamente pequeños.
A la vista de lo anterior se comprende que los programadores busquen algoritmos de
complejidad lineal. Es un golpe de suerte encontrar algo de complejidad logarítmica. Si se
encuentran soluciones polinomiales, se puede vivir con ellas; pero ante soluciones de
complejidad exponencial, más vale seguir buscando.
No obstante lo anterior...
 ... si un programa se va a ejecutar muy pocas veces, los costes de codificación y
depuración son los que más importan, relegando la complejidad a un papel
secundario.
 ... si a un programa se le prevé larga vida, hay que pensar que le tocará mantenerlo
a otra persona y, por tanto, conviene tener en cuenta su legibilidad, incluso a costa
de la complejidad de los algoritmos empleados.
 ... si podemos garantizar que un programa sólo va a trabajar sobre datos pequeños
(valores bajos de N), el orden de complejidad del algoritmo que usemos suele ser
irrelevante, pudiendo llegar a ser incluso contraproducente.
Por ejemplo, si disponemos de dos algoritmos para el mismo problema, con tiempos
de ejecución respectivos:
algoritmo tiempo Complejidad
f 100 n O(n)
g n 2 O(n2)

Asintóticamente, "f" es mejor algoritmo que "g"; pero esto es cierto a partir de N >
100.
Si nuestro problema no va a tratar jamás problemas de tamaño mayor que 100, es
mejor solución usar el algoritmo "g".
El ejemplo anterior muestra que las constantes que aparecen en las fórmulas para
T(n), y que desaparecen al calcular las funciones de complejidad, pueden ser
decisivas desde el punto de vista de ingeniería. Pueden darse incluso ejemplos más
dramáticos:
algoritmo tiempo Complejidad
f n O(n)
g 100 n O(n)
aún siendo dos algoritmos con idéntico comportamiento asintótico, es obvio que el
algoritmo "f" es siempre 100 veces más rápido que el "g" y candidato primero a ser
utilizado.
 ... usualmente un programa de baja complejidad en cuanto a tiempo de ejecución,
suele conllevar un alto consumo de memoria; y viceversa. A veces hay que sopesar
ambos factores, quedándonos en algún punto de compromiso.
 ... en problemas de cálculo numérico hay que tener en cuenta más factores que su
complejidad pura y dura, o incluso que su tiempo de ejecución: queda por considerar
la precisión del cálculo, el máximo error introducido en cálculos intermedios, la
estabilidad del algoritmo, etc. etc.

You might also like