Editar: revisión principal en la revisión 3.
Como nunca he enseñado una clase, no creo que pueda reclamar nada convincente sobre lo que deberíamos enseñar. Sin embargo, aquí está lo que pensé al respecto.
Hay ejemplos naturales en los que no se puede aplicar el "truco límite" tal como está escrito. Por ejemplo, suponga que implementa un "vector de longitud variable" (como el vector <T> en C ++) usando una matriz de longitud fija con duplicación de tamaño (es decir, cada vez que está a punto de exceder el tamaño de la matriz, usted reasignar la matriz dos veces más grande que ahora y copiar todos los elementos). El tamaño S ( n ) de la matriz cuando almacenamos n elementos en el vector es la potencia más pequeña de 2 mayor o igual que n . Queremos decir que S ( n ) = O ( n ), pero usar el "truco límite" como está escrito como definición no nos permitiría hacerlo porque S ( n) / n oscila densamente en el rango [1,2). Lo mismo se aplica a Ω () y Θ ().
Como cuestión un tanto separada, cuando usamos estas notaciones para describir la complejidad de un algoritmo, creo que su definición de Ω () a veces es inconveniente (aunque supongo que esa definición es común). Es más conveniente definir que f ( n ) = Ω ( g ( n )) si y solo si limsup f ( n ) / g ( n )> 0. Esto se debe a que algunos problemas son triviales para infinitos valores de n ( como el problema de mecanizado perfecto en un gráfico con un número impar n de vértices). Lo mismo se aplica a Θ () y ω ().
Por lo tanto, personalmente considero que las siguientes definiciones son las más convenientes para describir la complejidad de un algoritmo: para las funciones f , g : ℕ → ℝ > 0 ,
- f ( n ) = o ( g ( n )) si y solo si limsup f ( n ) / g ( n ) = 0. (Esto es equivalente a lim f ( n ) / g ( n ) = 0.)
- f ( n ) = O ( g ( n )) si y solo si limsup f ( n ) / g ( n ) <∞.
- f ( n ) = Θ ( g ( n )) si y solo si 0 <limsup f ( n ) / g ( n ) <∞.
- f ( n ) = Ω ( g ( n )) si y solo si limsup f ( n ) / g ( n )> 0. (Esto es equivalente a que f ( n ) no es o ( g ( n )).)
- f ( n ) = ω ( g ( n )) si y solo si limsup f ( n ) / g ( n ) = ∞. (Esto es equivalente a que f ( n ) no es O ( g ( n )).)
o equivalente,
- f ( n ) = o ( g ( n )) si y solo si para cada c > 0, para n suficientemente grande , f ( n ) ≤ c ⋅ g ( n ).
- f ( n ) = O ( g ( n )) si y solo si para algunos c > 0, para n suficientemente grande , f ( n ) ≤ c ⋅ g ( n ).
- f ( n ) = Θ ( g ( n )) si y solo si f ( n ) = O ( g ( n )) yf ( n ) = Ω ( g ( n )).
- f ( n ) = Ω ( g ( n )) si y solo si para algún d > 0, para infinitamente n , f ( n ) ≥ d ⋅ g ( n ).
- f ( n ) = ω ( g ( n )) si y solo si por cada d > 0, por infinitamente n , f ( n ) ≥ d ⋅ g ( n ).
Pero no sé si esta es una práctica común o no. Además, no sé si es adecuado para la enseñanza. El problema es que a veces queremos definir Ω () liminf en su lugar (como lo hizo en la primera definición). Por ejemplo, cuando decimos "La probabilidad de error de este algoritmo aleatorio es 2 −Ω ( n ) ", ¡no queremos decir que la probabilidad de error sea exponencialmente pequeña simplemente para infinitos n !