Edit: Revisão principal na revisão 3.
Como nunca lecionei uma aula, não creio que possa reivindicar algo convincente sobre o que devemos ensinar. No entanto, aqui está o que eu pensei sobre isso.
Existem exemplos naturais em que o "truque de limite", como está escrito, não pode ser aplicado. Por exemplo, suponha que você implemente um "vetor de comprimento variável" (como o vetor <T> em C ++) usando uma matriz de comprimento fixo com duplicação de tamanho (ou seja, toda vez que estiver prestes a exceder o tamanho da matriz, você realoque a matriz duas vezes maior que agora e copie todos os elementos). O tamanho S ( n ) da matriz quando armazenamos n elementos no vetor é a menor potência de 2 maior ou igual a n . Queremos dizer que S ( n ) = O ( n ), mas o uso do “truque de limite”, como está escrito como definição, não nos permitiria fazer isso porque S ( n) / n oscila densamente no intervalo [1,2). O mesmo se aplica a Ω () e Θ ().
Como uma questão um pouco separada, quando usamos essas notações para descrever a complexidade de um algoritmo, acho que sua definição de Ω () às vezes é inconveniente (embora eu ache que essa definição seja comum). É mais conveniente definir que f ( n ) = Ω ( g ( n )) se e somente se limsup f ( n ) / g ( n )> 0. Isso ocorre porque alguns problemas são triviais para infinitos valores de n ( como o problema de usinagem perfeito em um gráfico com um número ímpar n de vértices). O mesmo se aplica a Θ () e ω ().
Portanto, eu pessoalmente acho que as seguintes definições são as mais convenientes a serem usadas para descrever a complexidade de um algoritmo: para funções f , g : ℕ → ℝ > 0 ,
- f ( n ) = o ( g ( n )) se e somente se limsup f ( n ) / g ( n ) = 0. (Isso é equivalente a lim f ( n ) / g ( n ) = 0.)
- f ( n ) = O ( g ( n )) se e somente se limsup f ( n ) / g ( n ) <∞.
- f ( n ) = Θ ( g ( n )) se e somente se 0 <limsup f ( n ) / g ( n ) <∞.
- f ( n ) = Ω ( g ( n )) se e somente se limsup f ( n ) / g ( n )> 0. (Isso é equivalente a que f ( n ) não é o ( g ( n )).)
- f ( n ) = ω ( g ( n )) se e somente se limsup f ( n ) / g ( n ) = ∞. (Isso é equivalente a que f ( n ) não é O ( g ( n )).)
ou equivalente,
- f ( n ) = o ( g ( n )) se e somente se para cada c > 0, para n suficientemente grande , f ( n ) ≤ c ⋅ g ( n ).
- f ( n ) = O ( g ( n )) se e somente se para alguns c > 0, para n suficientemente grande , f ( n ) ≤ c ⋅ g ( n ).
- f ( n ) = Θ ( g ( n )) se e somente se f ( n ) = O ( g ( n )) ef ( n ) = Ω ( g ( n )).
- f ( n ) = Ω ( g ( n )) se e somente se para alguns d > 0, para infinitamente muitos n , f ( n ) ≥ d ⋅ g ( n ).
- f ( n ) = ω ( g ( n )) se e somente se para cada d > 0, para infinitamente muitos n , f ( n ) ≥ d ⋅ g ( n ).
Mas não sei se isso é uma prática comum ou não. Também não sei se é adequado para o ensino. O problema é que algumas vezes queremos definir Ω () por liminf (como você fez na primeira definição). Por exemplo, quando dizemos “A probabilidade de erro desse algoritmo aleatório é 2 −Ω ( n ) ”, não queremos dizer que a probabilidade de erro seja exponencialmente pequena apenas para infinitos n !