Правка: Основна редакція в редакції 3.
Оскільки я ніколи не викладав класу, я не думаю, що можу щось переконливо заявити про те, чого ми повинні навчати. Тим не менш, ось що я подумав про це.
Є природні приклади, коли «обмежувальний трюк», як написано, неможливо застосувати. Наприклад, припустимо, що ви реалізуєте "вектор змінної довжини" (наприклад, вектор <T> в C ++), використовуючи масив фіксованої довжини з подвоєнням розміру (тобто кожного разу, коли ви збираєтеся перевищити розмір масиву, ви перерозподіліть масив у два рази більше, ніж зараз, і скопіюйте всі елементи). Розмір S ( n ) масиву, коли ми зберігаємо n елементів у векторі, є найменшою потужністю на 2, більшою або дорівнює n . Ми хочемо сказати, що S ( n ) = O ( n ), але використання "граничного трюку", як записано як визначення, не дозволило би нам це зробити, оскільки S ( n) / n коливається щільно в діапазоні [1,2). Те саме стосується Ω () та Θ ().
Як дещо окремий випадок, коли ми використовуємо ці позначення для опису складності алгоритму, я вважаю, що ваше визначення Ω () іноді незручне (хоча, мабуть, це визначення є загальним). Зручніше визначити, що f ( n ) = Ω ( g ( n )) тоді і лише тоді, коли limsup f ( n ) / g ( n )> 0. Це тому, що деякі проблеми тривіальні для нескінченно багатьох значень n ( наприклад, ідеальна задача обміну на графіку з непарним числом n вершин). Те саме стосується Θ () і ω ().
Тому я особисто вважаю, що наступні визначення найзручніше використовувати для опису складності алгоритму: для функцій f , g : ℕ → ℝ > 0 ,
- f ( n ) = o ( g ( n )) тоді і лише тоді, коли limsup f ( n ) / g ( n ) = 0. (Це еквівалентно lim f ( n ) / g ( n ) = 0.)
- f ( n ) = O ( g ( n )) тоді і тільки тоді, коли limsup f ( n ) / g ( n ) <∞.
- f ( n ) = Θ ( g ( n )) тоді і тільки тоді, коли 0 <limsup f ( n ) / g ( n ) <∞.
- f ( n ) = Ω ( g ( n )) тоді і тільки тоді, коли limsup f ( n ) / g ( n )> 0. (Це еквівалентно тому, що f ( n ) не є o ( g ( n )).)
- f ( n ) = ω ( g ( n )) тоді і тільки тоді, коли limsup f ( n ) / g ( n ) = ∞. (Це еквівалентно тому, що f ( n ) не є O ( g ( n )).)
або рівнозначно,
- f ( n ) = o ( g ( n )) тоді і тільки тоді, коли для кожного c > 0, для досить великого n , f ( n ) ≤ c ⋅ g ( n ).
- f ( n ) = O ( g ( n )) тоді і тільки тоді, коли для деякого c > 0, для досить великих n , f ( n ) ≤ c ⋅ g ( n ).
- f ( n ) = Θ ( g ( n )) тоді і тільки тоді, коли f ( n ) = O ( g ( n )) і f ( n ) = Ω ( g ( n )).
- f ( n ) = Ω ( g ( n )) тоді і лише тоді, коли для деякого d > 0, для нескінченно багатьох n , f ( n ) ≥ d ⋅ g ( n ).
- f ( n ) = ω ( g ( n )) тоді і тільки тоді, коли для кожного d > 0, для нескінченно багатьох n , f ( n ) ≥ d ⋅ g ( n ).
Але я не знаю, звичайна це практика чи ні. Також не знаю, чи підходить вона для викладання. Проблема полягає в тому, що ми іноді хочемо натомість визначити Ω () liminf (як ви це робили в першому визначенні). Наприклад, коли ми говоримо «Імовірність помилки цього рандомізованого алгоритму дорівнює 2 Ω ( n ) », ми не маємо на увазі, що ймовірність помилки експоненціально мала лише для нескінченно багатьох n !