Что это? Свойства логарифмов многих пугают своей непонятной записью и туманным практическим применением. Всё это только потому, что в школе, видимо, плохо объясняли эту тему. На самом деле в них нет ничего ужасного. А введены они были Джоном Нейпиром еще в 1614 году как средство упрощения вычислений.
Зачем? Сфера применения логарифмов очень широка: от очевидного (ускорения математических вычислений для научных выкладок) до чисто практических вещей высокого порядка: ракетостроение, химия, программирование.
В статье рассказывается:
- Что такое логарифм
- Где применяются свойства логарифмов
- Практическое применение логарифмом в программировании
- Быстрое вычисление логарифмов с помощью float
-
Пройди тест и узнай, какая сфера тебе подходит:
айти, дизайн или маркетинг.Бесплатно от Geekbrains
Что такое логарифм
Пожалуй, для большинства из нас логарифмы – самый «страшный» раздел математики. Как их считать? В каких сферах применять? И вообще, зачем свойства логарифмов нужны за пределами страниц учебника?
Логарифм – это функция, обратная возведению в степень. Иначе говоря, он позволяет понять, в какую степень нужно возвести одно число, чтобы получить другое.
В переводе на язык математики, это выглядит следующим образом:
logaB=x, Ax=B
Таким образом, чтобы рассчитать число В, число А необходимо возвести в степень х.
Чтобы стало понятнее, стоит обратиться к примеру с числами: необходимо вычислить степень, в которую требуется возвести число 2, чтобы получилось 8. Для тех, кто помнит таблицу степеней двойки, сразу очевидно, что 2³ = 8. Иными словами, число 2 необходимо возвести в третью степень. Путем этих нехитрых вычислений получилось найти логарифм числа 8 по основанию 2:
Log28 = 3, так как 23 = 8
Начиная знакомство с логарифмами, многие путают основание и степень. Чтобы быстрее запомнить, что есть что, нужно держать в голове следующее правило: основание у логарифма, как и у возведения в степень, находится внизу.
входят в ТОП-30 с доходом
от 210 000 ₽/мес
Скачивайте и используйте уже сегодня:
Топ-30 самых востребованных и высокооплачиваемых профессий 2023
Поможет разобраться в актуальной ситуации на рынке труда
Подборка 50+ бесплатных нейросетей для упрощения работы и увеличения заработка
Только проверенные нейросети с доступом из России и свободным использованием
ТОП-100 площадок для поиска работы от GeekBrains
Список проверенных ресурсов реальных вакансий с доходом от 210 000 ₽
В общем виде запись logAB читается как «логарифм В по основанию А». Число А, которое необходимо возвести в некую степень, – это основание логарифма.
Чтобы закрепить в памяти структуру записи логарифмов, стоит проанализировать математические выражения из списка, приведенного ниже, и понять, что они означают:
- log39 = 2
- log464 = 3
- log5625 = 4
- log7343 = 3
- log10100 = 2
- log2128 = 7
- log20,25 = −2
- log625125 = 0,75
В математике чаще всего используются десятичные и натуральные логарифмы.
Десятичный логарифм (lg), как следует из названия, имеет в основании число 10. То есть, вычисляя подобные выражения, необходимо определить, в какую степень требуется возвести 10, чтобы получилось желаемое число.
lg100 = log10100 = 2, поскольку 102 = 100
Основание натуральных логарифмов (ln) – число Эйлера (е = 2,71828). В математике число «е» по важности схоже с числом Пи в геометрии. Логарифмы по основанию е часто используются для решения разнообразных математических задач.
logeB=lnB
Где применяются свойства логарифмов
Возможно, для многих удивительно, но даже в повседневной жизни рассматриваемые выражения окружают нас практически повсюду. Наглядные примеры свойств логарифмов рассмотрены ниже.
С помощью десятичных логарифмов рассчитывается относительная громкость любых звуков (измеряется в децибелах). Относительной она называется потому, что считается от минимального порога громкости, которую может расслышать человеческое ухо. К примеру, если звук обладает громкостью 20 дБ, это означает, что он громче самого тихого в 100 раз. А если 30 дБ – в 1 000 раз.
В химии логарифмическая шкала применяется для определения активности ионов водорода.
Популярны логарифмические тождества и у фотографов: каждое новое значение при замене выдержки или диафрагмы больше или меньше предшествующего в определенное число раз.
Важную роль основные свойства логарифмов играют в ракетостроении. В этой отрасли, чтобы определить скорость движения ракеты, пользуются уравнением Циолковского, которое построено на логарифмической зависимости от массы машины с топливом и без него.
Натуральные логарифмы позволяют вычислить приблизительную массу кристаллов через конкретный промежуток времени (в частности, через 3, 5 и 10 лет). Для этих целей число е требуется возвести в определенную степень.
e3 ≈ 20,0855 кг.
e5 ≈ 148,4132 кг.
e10 ≈ 22 026,4658 кг.
Можно решить и обратную задачу: рассчитать, через сколько лет масса кристалла достигнет, например, одной тонны. Для этого нужно составить уравнение:
ex = 1 000
Поскольку известны основание логарифма и число, полученное в результате возведения в степень, можно без особых затруднений найти ее показатель. Очевидно, что речь идет о логарифме x = loge1 000. В сокращенном варианте: x = ln 1 000.
С помощью нехитрых вычислений на калькуляторе можно высчитать, что x ≈ 6,9. То есть именно через этот промежуток времени вес кристалла составит одну тонну.
Практическое применение логарифмом в программировании
Свойства логарифмов нашли широкое применение в программировании. На соревнованиях код зачастую пишется на С++. Проанализировав сложность алгоритма, можно ориентировочно предположить, сколько времени потребуется для выполнения программы (считается, что в секунду реализуется 1 000 000 команд).
Скачать файлИх количество для конкретного случая рассчитывается на основе полученной функции асимптотической оценки, которая описывает исходный код. Таким образом, при n = 1 000 000, для вычисления по алгоритму с Θ(n) потребуется одна секунда.
Рекурсивная сложность
Как известно, рекурсивная функция – это особая функция, вызывающая саму себя. Каким образом можно проанализировать ее сложность? Ниже приведен пример исходного кода на Python, который позволяет вычислить факториал заданной переменной. Принцип расчета факториала целого положительного числа многим знаком из курса математики: нужно перемножить все предшествующие положительные целые числа. В частности, факториал 5 = 5 * 4 * 3 * 2 * 1. Его принято обозначать символом «!» (например, «5!» – «факториал пяти»).
def factorial( n ):
if n == 1:
return 1
return n * factorial( n — 1 )
Данная функция не содержит циклы. Однако ее сложность нельзя назвать константной. Если применить эту функцию к какому-либо числу n, она будет вычисляться на протяжении n раз. Если у кого-то возникают сомнения, что это на самом деле так, можно вручную расписать ход вычисления при n = 5. Стало быть, эта функция представляет собой Θ(n).
на курсы от GeekBrains до 01 декабря
Точную сложность можно определить с помощью подсчета количества инструкций. Использование данного метода применительно к указанной функции позволяет вычислить ее 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
Читайте также!
Данный псевдокод представляет собой упрощенный вариант настоящей реализации. Как показывает практика, описать этот метод проще, чем воплотить в жизнь. Это связано с несколькими нюансами. В частности, защита от ошибок на одну позицию (off-by-one error, OBOE). К тому же результатом деления на два не всегда является целое число, соответственно, это влечет за собой применение функций floor() или ceil().
В идеальных условиях деление всегда проходит успешно, а код защищен от ошибок off-by-one. Опытные программисты рекомендуют тем, кто до этого никогда не выполнял бинарный поиск, научиться решать эту задачу с помощью хотя бы одного из языков программирования.
Также для того, чтобы упростить поиск решения, стоит предположить, что размер массива – одна из степеней двойки. Данная гипотеза не повлияет на итоговое значение сложности алгоритма. Пессимистичный сценарий для данной задачи – массив в принципе не содержит искомое значение. При таком раскладе необходимо начать с массива n на первом рекурсивном вызове, n / 2 – на втором, n / 4 – на третьем и т.д.
Иными словами, массив нужно разбивать пополам на каждом вызове до того момента, когда будет достигнута единица. Количество элементов в массиве на каждом вызове записывается следующим образом:
0-я итерация: n
1-я итерация: n / 2
2-я итерация: n / 4
3-я итерация: n / 8
…
i-я итерация: n / 2i
…
последняя итерация: 1
Стоит иметь в виду, что на i-той итерации у массива n / 2i элементов. Каждый раз массив разбивается пополам, то есть количество элементов делится на два (это равнозначно умножению знаменателя на два).
В результате осуществления данной манипуляции i раз, получается n / 2i . В ходе этого процесса из каждого большого i будет получаться меньшее количество элементов (пока не будет достигнута единица). Чтобы понять, на какой итерации это случилось, необходимо решить уравнение, основанное на свойствах логарифмов.
1 = n / 2i
Истинным равенство является только в том случае, когда достигнут конечный вызов функции binarySearch(). Выяснив из него i, вы сможете узнать номер последней рекурсивной итерации. Если умножить каждую из частей уравнения на два, получается:
2i = n
Решение данного выражения представляет собой следующую запись:
i = log(n)
Таким образом, количество итераций, которые потребуются для бинарного поиска, равняется log(n), где n — размер оригинального массива.
Это вполне логично. К примеру, можно взять массив, состоящий из 32 элементов (n = 32). Сколько раз необходимо его разделить, чтобы получить один элемент? 32 → 16 → 8 → 4 → 2 → 1. Всего 5 раз, что представляет собой логарифм 32. Соответственно, сложность бинарного поиска равняется Θ( log( n ) ).
Благодаря этому можно сравнить бинарный поиск с линейным. Разумеется, log(n) значительно меньше n. Таким образом, бинарный поиск гораздо эффективнее. Если приходится часто искать значения, массивы целесообразно хранить в отсортированном виде.
Не стоит забывать о том, что, если улучшить асимптотическое время выполнения программы, можно существенно повысить ее производительность. Причем намного сильнее, чем если в тех же условиях использовать более быстрый язык программирования.
Быстрое вычисление логарифмов с помощью float
Применение float дает возможность быстро вычислить приближенные значения некоторых математических функций:
union ADVFLOAT
{
float x;
struct
{
unsigned int mant : 23; /* Mantissa without leading one */
unsigned int exp : 8; /* Exponential part */
unsigned int sign : 1; /* Indicator of the negative number */
};
};
Читайте также!
Число с плавающей десятичной запятой (по стандарту IEEE 754) записывается следующим образом: x = (1 + mant) * 2 ^ (exp — 127). Соответственно, если требуется удвоить число, необходимо выполнить такую операцию:
AdvFloat.exp++;
Чтобы получить модуль числа, нужно наложить AND’ом маску 0x7FFFFFFF.
С помощью float можно вычислять и сложные математические функции, в том числе логарифмы. Опираясь на формулы свойств логарифмов и используя первый член из ряда Тейлора, без особых сложностей у вас получится высчитать довольно точное значение:
log2(x) = log2((1 + mant) * 2 ^ (exp — 127)) = log2(1 + mant) + log2(2 ^ (exp — 127)) =
= ln(1 + mant) / ln(2) + (exp — 127) = ln(1 + mant) / ln(2) + (exp — 127)
= mant * log2(e) + (exp — 127).
Чтобы посчитать логарифм, необходимо воспользоваться данной функцией:
#define LOG2E 1.44269504088896340736f
float fastLog2( float x )
{
ADVFLOAT ax;
int exp;
ax.x = x;
exp = ax.exp — 127;
ax.sign = 0;
ax.exp = 127;
return (ax.x — 1.0f) * LOG2E + exp;
}
Как показывает практика, ошибка чаще всего возникает во втором или третьем знаке после запятой. Достаточно неплохой результат, с учетом того, что это максимально примитивная аппроксимация. Если необходимо, можно получить и более точные значения. Однако, для этого потребуется потратить время на кропотливый математический анализ, чтобы подобрать более нетривиальные коэффициенты, чем 1.0f и LOG2E.
Данную функцию можно применять в первоначальном виде или использовать побитовые операции (чтобы ускорить ее выполнение). К тому же, приближенное значение требуется не только для решения свойств логарифмов. Не стоит забывать про функцию fastSqrt() – она также основана на принципе приближенного вычисления.
Итак, роль рассматриваемой функции в нашей жизни сложно переоценить. Свойства натуральных и десятичных логарифмов нашли широкое применение во многих сферах, в частности, в программировании.