Введение в анализ сложности алгоритмов (часть 3) / Хабр
От переводчика: данный текст даётся с незначительными сокращениями по причине местами излишней «разжёванности» материала. Автор абсолютно справедливо предупреждает, что отдельные темы могут показаться читателю чересчур простыми или общеизвестными. Тем не менее, лично мне этот текст помог упорядочить имеющиеся знания по анализу сложности алгоритмов. Надеюсь, что он окажется полезен и кому-то ещё.
Из-за большого объёма оригинальной статьи я разбила её на части, которых в общей сложности будет четыре.
Я (как всегда) буду крайне признательна за любые замечания в личку по улучшению качества перевода.
Опубликовано ранее:
Часть 1
Часть 2
Логарифмы
Если вы знаете, что такое логарифмы, то можете спокойно пропустить этот раздел. Глава предназначается тем, кто незнаком с данным понятием или пользуется им настолько редко, что уже забыл что там к чему.
f(n) = n
, красный — f(n) = sqrt(n)
, а наименее быстро возрастающий — f(n) = log(n)
. Далее: подобно тому, как взятие квадратного корня является операцией, обратной возведению в квадрат, логарифм — обратная операция возведению чего-либо в степень. Поясню на примере. Рассмотрим уравнение
2
x = 1024
Чтобы найти из него x
, спросим себя: в какую степень надо возвести 2, чтобы получить 1024? Ответ: в десятую. В самом деле, 2
10 = 1024
Решите уравнения ниже, записывая логарифмы, которые вы ищете. Используйте только логарифмы по основанию 2.
- 2x = 64
- (22)x = 64
- 4x = 4
- 2x = 1
- 2x + 2x = 32
- (2x) * (2x) = 64
Решение
Здесь не нужно ничего сверх идей, изложенных выше.
- Методом проб и ошибок, находим, что
x = 6
. Таким образом,log( 64 ) = 6
- По свойству степеней (22)x можно записать как 22x. Таким образом, получим, что
2x = 6
(потому чтоlog( 64 ) = 6
из предыдущего примера) и, следовательно,x = 3
- Используя знания, полученные при решении предыдущего примера, запишем 4 как 22. Тогда наше уравнение превратится в (22)x = 4, что эквивалентно 22x. Заметим, что log( 4 ) = 2 (поскольку 22 = 4), так что мы получаем
2x = 2
, откудаx = 1
. Это легко заметить по исходному уравнению, поскольку степень 1 даёт основание в качестве результата - Вспомнив, что степень 0 даёт результатом 1 (т.е.
log( 1 ) = 0
), получимx = 0
- Здесь мы имеем дело с суммой, поэтому непосредственно взять логарифм не получится. Однако, мы замечаем, что
2
x+ 2
x — это тоже самое, что и2 * (2
x)
. Умножение на 2 даст2
x + 1, и мы получим уравнение
x + 1= 32
. Находим, чтоlog( 32 ) = 5
, откудаx + 1 = 5
иx = 4
. - Мы перемножаем две степени двойки, следовательно, можем объединить их в 22x. Остаётся просто решить уравнение
2
2x= 64
, что мы уже делали выше. Ответ:x = 3
Практическая рекомендация: на соревнованиях алгоритмы часто реализуются на С++. Как только вы проанализировали сложность вашего алгоритма, так сразу можете получить и грубую оценку того, как быстро он будет работать, приняв, что в секунду выполняется 1 000 000 команд. Их количество считается из полученной вами функции асимптотической оценки, описывающей алгоритм. Например, вычисление по алгоритму с Θ( n ) займёт около секунды при n = 1 000 000.
Рекурсивная сложность
А теперь давайте обратимся к рекурсивным функциям. Рекурсивная функция — это функция, которая вызывает сама себя. Можем ли мы проанализировать её сложность? Следующая функция, написанная на Python, вычисляет факториал заданного числа. Факториал целого положительного числа находится как произведение всех предыдущих положительных целых чисел. Например, факториал 5 — это 5 * 4 * 3 * 2 * 1
. Обозначается он как «5!» и произносится «факториал пяти» (впрочем, некоторые предпочитают «ПЯТЬ!!!11»).
def factorial( n ): if n == 1: return 1 return n * factorial( n - 1 )
Давайте проанализируем эту функцию. Она не содержит циклов, но её сложность всё равно не является константной. Что ж, придётся вновь заняться подсчётом инструкций. Очевидно, что если мы применим эту функцию к некоторому n
, то она будет вычисляться
раз. (Если вы в этом не уверены, то можете вручную расписать ход вычисления при n = 5
, чтобы определить, как это на самом деле работает.) Таким образом, мы видим, что эта функция является Θ( n )
.
Если вы всё же не уверены в этом, то вы всегда можете найти точную сложность путём подсчёта количества инструкций. Примените этот метод к данной функции, чтобы найти её f( n ), и убедитесь, что она линейная (напомню, что линейность означает Θ( n )
).
Логарифмическая сложность
Одной из известнейших задач в информатике является поиск значения в массиве. Мы уже решали её ранее для общего случая. Задача становится интереснее, если у нас есть отсортированный массив, в котором мы хотим найти заданное значение. Одним из способов сделать это является бинарный поиск. Мы берём средний элемент из нашего массива: если он совпадает с тем, что мы искали, то задача решена. В противном случае, если заданное значение больше этого элемента, то мы знаем, что оно должно лежать в правой части массива.
А если меньше — то в левой. Мы будем разбивать эти подмассивы до тех пор, пока не получим искомое.Вот реализация такого метода в псевдокоде:
def binarySearch( A, n, value ): if n = 1: if A[ 0 ] = value: return true else: return false if value < A[ n / 2 ]: return binarySearch( A[ 0...( n / 2 - 1 ) ], n / 2 - 1, value ) else if value > A[ n / 2 ]: return binarySearch( A[ ( n / 2 + 1 )...n ], n / 2 - 1, value ) else: return true
Приведённый псевдокод — упрощение настоящей реализации. На практике этот метод описать проще, чем воплотить, поскольку программисту нужно решить некоторые дополнительные вопросы. Например, защиту от ошибок на одну позицию (
floor()
или ceil()
. Однако, предположим, что в нашем случае деление всегда будет успешным, наш код защищён от ошибок off-by-one, и всё, что мы хотим — это проанализировать сложность данного метода. Если вы никогда раньше не реализовывали бинарный поиск, то можете сделать это на вашем любимом языке программирования. Такая задача по-настоящему поучительна.Если вы не уверены, что метод работает в принципе, то отвлекитесь и решите вручную какой-нибудь простой пример.
Теперь давайте попробуем проанализировать этот алгоритм. Ещё раз, в этом случае мы имеем рекурсивный алгоритм. Давайте для простоты предположим, что массив всегда разбивается ровно пополам, игнорируя +1 и -1 части в рекурсивном вызове. К этому моменту вы должны быть уверены, что такое небольшое изменение, как игнорирование +1 и -1, не повлияет на конечный результат оценки сложности. В принципе, обычно этот факт необходимо доказывать, если мы хотим проявить осторожность с математической точки зрения. Но на практике это очевидно на уровне интуиции. Также для простоты давайте предположим, что наш массив имеет размер одной из степеней двойки. Опять-таки, это предположение никоим образом не повлияет на конечный результат расчёта сложности алгоритма.
Наихудшим сценарием для данной задачи будет вариант, когда массив в принципе не содержит искомое значение. В этом случае мы начинаем с массива размеромn
на первом рекурсивном вызове, n / 2
на втором, n / 4
на третьем и так далее. В общем, наш массив разбивается пополам на каждом вызове до тех пор, пока мы не достигнем единицы. Давайте запишем количество элементов в массиве на каждом вызове:n
1-я итерация:
n / 2
2-я итерация:
n / 4
3-я итерация:
n / 8
…
i-я итерация:
n / 2
i…
последняя итерация:
1
Заметьте, что на i-й итерации массив имеет n / 2
i элементов. Мы каждый раз разбиваем его пополам, что подразумевает деление количества элементов на два (равноценно умножению знаменателя на два). Проделав это i
раз, получим n / 2
i. Процесс будет продолжаться, и из каждого большого i
мы будем получать меньшее количество элементов до тех пор, пока не достигнем единицы. Если мы захотим узнать, на какой итерации это произошло, нам нужно будет просто решить следующее уравнение:
1 = n / 2
i
Равенство будет истинным только тогда, когда мы достигнем конечного вызова функции binarySearch()
, так что узнав из него i
, мы будем знать номер последней рекурсивной итерации. Умножив обе части на 2
i, получим:
2
i = n
Если вы прочли раздел о логарифмах выше, то такое выражение будет для вас знакомым. Решив его, мы получим:
i = log( n )
Этот ответ говорит нам, что количество итераций, необходимых для бинарного поиска, равняется log( n )
, где n
— размер оригинального массива.
Если немного подумать, то это имеет смысл. Например, возьмём n = 32
(массив из 32-х элементов). Сколько раз нам потребуется разделить его, чтобы получить один элемент? Считаем: 32 → 16 → 8 → 4 → 2 → 1 — итого 5 раз, что является логарифмом 32. Таким образом, сложность бинарного поиска равна Θ( log( n ) )
.
Последний результат позволяет нам сравнивать бинарный поиск с линейным (нашим предыдущим методом). Несомненно, log( n )
намного меньше n
, из чего правомерно заключить, что первый намного быстрее второго. Так что имеет смысл хранить массивы в отсортированном виде, если мы собираемся часто искать в них значения.
Практическая рекомендация: улучшение асимптотического времени выполнения программы часто чрезвычайно повышает её производительность. Намного сильнее, чем небольшая «техническая» оптимизация в виде использования более быстрого языка программирования.
3-8
Чему равен пятый квадратный корень из 1024?
Корень пятой степени из 1024 равен 4 , так как 4 х 4 х 4 х 4 х 4 равно 1024.
Запрос на удаление |
Посмотреть полный ответ на Captaincalculator.com
Каким будет значение √ 1024?
Следовательно, квадратный корень из 1024 равен 32.
Запрос на удаление |
Посмотреть полный ответ на byjus.com
Как извлечь корень пятой степени?
Шаг 1: На числовой прямой отнимите 2 единицы от О и обозначьте точку как А. Шаг 2: В точке А проведите перпендикуляр и отметьте В так, чтобы АВ = 1 единица. Шаг 3: Теперь, используя O в качестве центра и OB в качестве радиуса, нарисуйте дугу, чтобы пересечь числовую прямую в точке C. Шаг 4: Точка C представляет √5 на числовой прямой.
Запрос на удаление |
Посмотреть полный ответ на cuemath.com
На что можно разделить 1024?
Следовательно, делители числа 1024 равны 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024.
Запрос на удаление |
Посмотреть полный ответ на cuemath.com
На что делится число 1024?
Числа, на которые делится 1024, это 1, 2, 4, 8, 16, 32, 64, 128, 256, 512 и 1024.
Запрос на удаление |
Посмотреть полный ответ на divisible.info
КВАДРАТНЫЙ КОРЕНЬ ИЗ 1024
Как получить 1024?
- 1024 — составное число.
- Простая факторизация: 1024 = 2 × 2 × 2 × 2 × 2 × 2 × 2 × 2 × 2 × 2, что можно записать как 1024 = 2¹⁰
- Показатель степени в простой факторизации равен 10. …
- Факторы числа 1024: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024.
- Пары факторов: 1024 = 1 × 1024, 2 × 512, 4 × 256, 8 × 128, 16 × 64 или 32 × 32,
|
Посмотреть полный ответ на findthefactors.com
Как записать число 1024 в виде степени?
1024=4×4×4×4×4=45. В.
Запрос на удаление |
Посмотреть полный ответ на byjus.com
Является ли число 1024 идеальным кубом?
Ответ: 1024 не является идеальным кубом.
Запрос на удаление |
Полный ответ можно найти на сайте questionnut.com
Сколько кратно 1024?
Итак, первые 10 кратных 1024: 1024, 2048, 3072, 4096, 5120, 6144, 7168, 8192, 9216 и 10240.
Запрос на удаление |
Посмотреть полный ответ на hellothinkster.com
Каковы множители числа 1024?
Таким образом, простые делители числа 1024 равны 2,2,2,2,2,2,2,2,2,2.
Запрос на удаление |
Посмотреть полный ответ на byjus.com
Сколько цифр в числе 1024?
Простые числа в криптографии
Простые числа, используемые в криптографических системах, обычно имеют длину 1024 бита (около 308 цифр).
Запрос на удаление |
Посмотреть полный ответ на blog.1password.com
Что такое √ 5 как дробь?
Квадратный корень из 5 принадлежит к длинному списку иррациональных алгебраических чисел. Он был помещен в такой список, потому что квадратный корень из 5 не может быть выражен в виде дроби и имеет бесконечное количество десятичных знаков.
Запрос на удаление |
Посмотреть полный ответ на byjus.com
Есть ли 5-й корень?
Пятый корень числа — это цифра, которую нужно умножить на 5 бит, чтобы получить реальную цифру. Например, корень пятой степени из 32 равен 2, поскольку 2 × 2 × 2 × 2 × 2 равен 32. Корень пятой степени из 3125 равен 5, поскольку 5 × 5 × 5 × 5 × 5 равен 3125.
Запрос на удаление |
Посмотреть полный ответ на geeksforgeeks.org
Сколько стоит 5 √?
Таким образом, значение корня 5 равно √5 = 2,2360… Вы можете найти значение квадратного корня из всех несовершенных квадратных чисел с помощью метода деления в длину. Это старый метод, который дает точное значение корня чисел.
Запрос на удаление |
Посмотреть полный ответ на byjus.com
Каков порядок √ 5?
Итак, √5 — это сурд порядка 2 или квадратичный сурд.
Запрос на удаление |
Посмотреть полный ответ на math-only-math.com
Что за число √ 5?
Это иррациональное алгебраическое число.