Конечно, ваш подход кажется нормальным.
Существует альтернативный взгляд на вопрос.
for (int i = 0; i < n; i++) { // loop 1
for (int j = i; j > 0; j /= 2){ // loop 2
std::cout << j << endl;
}
}
Анализ цикла 1
Здесь нечего видеть, просто O(n)
Анализ цикла 2
Немного более интересно, учитывая число j (= i), это гонка, чтобы увидеть, сколько раз вы можете разделить на 2, прежде чем доберетесь до 0. Что, конечно, не что иное, как логарифмическая база 2 числа.
Комбинируя оба анализа для каждого n, вы выполняете журнал (n, 2) -> log (число, база).
То есть O(n * log(n,2))
.
чем-то, что называется Приближение Стирлинга , вы можете доказать, что O(nlogn)
есть O(log(n!))
.
Теперь необходимо указать, что вы попросили T(n)
, что немного отличается O(n)
.
T(n)
- это то, что вы используете для математического обозначения алгоритма относительно его следующих шагов обработки и текущего шага.
Для чего-то вроде сортировки слиянием
T(n) = 2 * T(n/2)(divide) + O(n)(conquer)
.
В нашем случае я бы написал T(n)
как
T(n) = T(n + 1)(next step) + O(logn)(race to zero) , 0 <= n < N
= 0 , n >= N
(я знаю, что T (n) обычно обозначается в терминах предыдущих терминов, а не будущих терминов, но я просто обрисовываю точку, это не строгое математическое определение)