less_retarded_wiki/log.md

185 lines
13 KiB
Markdown
Raw Normal View History

2025-01-25 20:04:48 +01:00
# Logarithm
*For computer logs see [logging](logging.md).*
Logarithm (from Greek "logo arithmos", roughly "ratio/word number", often shortened to *log*, *lg* or *ln*) is a very important [mathematical](math.md) [function](function.md) telling us to what [power](power.md) a [number](number.md) has to be raised to in order to obtain another number; i.e. in terms of mathematics it is the inverse function to exponentiation. Logarithms are enormously important and useful: for example they allow fast and efficient multiplication and division of numbers (see e.g. [sliding rule](sliding_rule.md)), solving certain kinds of equations but also, very notably, they introduce **logarithmic scales** -- a type of alternative measuring scales (to the traditional linear scales found on rulers etc.) in which steps are not spaced by constant distance but by constant ratio, and it turns out this is exceptionally handy for measuring and plotting various values (such as loudness, earthquake intensity etc.). This is because in nature it often happens that the step to "the next level" is not an additive constant, but rather some ratio of previous step (or imagine a video [game](game.md) where leveling up each subsequent level takes more and more experience, let's say three halves of that required for the previous one). So dealing with logarithms sort of takes us from the realm of additions and differences to one where multiplications and ratios rule.
Human senses are known to perceive logarithmically (and this is exploited in lossy [compression](compression.md) algorithms) -- one of the best examples are musical tones: what we hear as an increase in pitch by one semitone is actually a frequency that's 12th root of 2 TIMES increased frequency of the previous one. [Interestingly](interesting.md) some studies even suggested that "logarithmic thinking" is possibly even more natural to us and only at school we're forced to adopt the traditional "linear thinking". However it might be, the first confrontation (normally during high school) with the mathematics of logarithms usually scares people off and average [IQs](iq.md) never fully grasp them (because they "don't need it [in real life](irl.md)"), but they're really not hard, just require some getting used to. To anyone dealing with math in any way (including [programmers](programming.md)) logarithms are absolutely required basic knowledge, don't try to avoid them.
Logarithms were introduced in 1614 by John Napier (white male).
## Details
There are different kinds of logarithms distinguished by their **base**: every logarithm has a base, so we have a "base 10 logarithm", "base 2 logarithm" etc. (one note here: *base* in this context does NOT signify the numeral system base, it's just a name for the number that gets raised to some power). The base is written as a subscript; here we'll write base *N* logarithm simply as *logN* (log10, log2 etc.). You may see logarithm written without the base, which either means some [implicit](implicit.md) base is assumed (often 10 or [e](e.md), see below) or that the base doesn't matter.
So logarithm is not a single function but rather a class of functions. Alternatively you MAY imagine logarithm as a single function of two arguments: the input number *x* and the base. However conventionally we prefer to view logarithms with different bases as different functions of one argument, perhaps because it rarely happens we need the base to be variable (quite often the base doesn't even matter and it's only a convention of choosing one).
Now finally for the **definition** itself: base *N* logarithm of number *x* gives number *y*, which we write as
*logN(x) = y*
so that the following holds:
*N^y = x*
NOTE: Don't confuse the function *N^x* with *x^N* (variable in exponent vs variable in base); inverse function of the former is logarithm *N* whereas the inverse of the latter is *Nth [root](root.md)*. These are similar and even the graphs of logarithm and Nth root look similar, but they aren't the same.
So answering the question "What's the base *N* logarithm of number x?" means answering **"N to WHAT power gives x?"**. Of course we know that both *N* and *y* don't have to be just integers, but can be any [real number](real_number.md) (including fractions, negative numbers etc., however excluding both being zero!), so logarithm is a **continuous function**, but we also know that (in the realm or real numbers) the operation of raising anything to a power can't ever yield a negative number (and neither [zero](zero.md), unless the base itself was zero), so **logarithm is only defined for positive numbers**.
NOTE: there exist generalizations such as [complex](complex_number.md) logarithms and it's also possible to have logarithms with bases smaller than 1, but for the sake of simplicity we'll now assume only real number logarithms with a base greater than 1 (which, however, may still be a non-integer).
A small graph will make the best demonstration:
```
^ y
4 +
|
3 +
| ___...''' log2(x)
2 + _---''
| _.-'
1 + .' _____ log10(x)
| / ...----''''''''
----+---|'''|---|---|---|---|--> x
0| :1 2 3 4
| ::
-1 + :
| :
```
Here we can see *log2(x)* and *log10(x)* plotted, things to observe are mainly these:
- Graph of function *logN(x)* is a graph of function *N^x* flipped by the 45 degree axis because logarithm is an inverse of that function (it switches the *x* and *y* axes).
- Both go through the point [1,0]. **All logarithms give value 0 for input 1** because any number to 0 gives 1.
- **Logarithm with base N gives 1 for input N** because *N^1 = N*.
- Similarly both turn from negative to positive *y* after the point *x = 1*. Again this is logical: to get values smaller than 1 we have to raise *N* to a negative power (which makes it a reciprocal fraction).
- Both get closer and closer to minus [infinity](infinity.md) as they're approaching *x = 0* from the right. This represents the fact that raising *1/N* to higher and higher powers gets us closer and closer to zero, but we never get there.
- The functions are always increasing, but increase ever more slowly.
By bases some important logarithms are:
- **log2** (base 2): Important to programmers (since computers work in [base 2](binary.md)), *log2(x)* for example says how many [bits](bit.md) we need to represent *x* different values, or how many times we can split number *x* in halves, which is important for computing [time complexity](complexity.md).
- **log10** (base 10), so called **common logarithm**: Important because in real life we often use base 10.
- **ln** (base [e](e.md)), so called **natural logarithm**: How some nice mathematical properties (for example nice [derivative](derivative.md)). This is often the default base for a logarithm (i.e. if you see just *log*, it's usually implied *e* is the base).
And here is a small table of some logarithm values to further aid making a picture of it all (*ln(x)* is natural logarithm):
| *x* | *ln(x)* |*log2(x)* |*log10(x)*|
| --- | -------- | -------- | -------- |
| 0 | ? | ? | ? |
| 0.5 |-0.693147 |-1.000000 |-0.301030 |
| 1 | 0.000000 | 0.000000 | 0.000000 |
| 1.5 | 0.405465 | 0.584963 | 0.176091 |
| 2 | 0.693147 | 1.000000 | 0.301030 |
| 2.5 | 0.916291 | 1.321928 | 0.397940 |
| 3 | 1.098612 | 1.584963 | 0.477121 |
| 3.5 | 1.252763 | 1.807355 | 0.544068 |
| 4 | 1.386294 | 2.000000 | 0.602060 |
| 4.5 | 1.504077 | 2.169925 | 0.653213 |
| 5 | 1.609438 | 2.321928 | 0.698970 |
| 5.5 | 1.704748 | 2.459432 | 0.740363 |
| 6 | 1.791759 | 2.584963 | 0.778151 |
| 6.5 | 1.871802 | 2.700440 | 0.812913 |
| 7 | 1.945910 | 2.807355 | 0.845098 |
| 7.5 | 2.014903 | 2.906891 | 0.875061 |
| 8 | 2.079442 | 3.000000 | 0.903090 |
| 8.5 | 2.140066 | 3.087463 | 0.929419 |
| 9 | 2.197225 | 3.169925 | 0.954243 |
| 9.5 | 2.251292 | 3.247928 | 0.977724 |
| 10 | 2.302585 | 3.321928 | 1.000000 |
One of the most important properties of logarithms, which you absolutely MUST burn into your brain right now, is that **logarithm of a product equals sum of logarithms** and **logarithm of a quotient equals difference of logarithms**, i.e.:
*logN(a * b) = logN(a) + logN(b)*
and
*logN(a / b) = logN(a) - logN(b)*
Why is this awesome? Well, because now if we can somehow quickly compute logarithm of a number -- for example with the help of precomputed tables, or even just [approximately](approximation.md) reading it off of the function plot -- we can very quickly multiply or divide numbers simply by looking up the logarithm of each and then adding them or subtracting them (which is not as time consuming as doing multiplication or division) and then mapping the logarithm of the result back. This can be used both by [computers](computer.md) and humans to [optimize](optimization.md) the speed of calculations; in fact this is how the [sliding rule](sliding_rule.md) works. This way raising a number to a power or finding its root can also be simplified to multiplication or division. (NOTE: someone smart could say we might as well have a precomputed table for multiplication, but that would be a much bigger table due to the fact that we'd need a value for any TWO input numbers, logarithm has just one).
Other important formulas with logarithms include these:
- *logN(a^b) = b * logN(a)* (simplifies exponentiation to multiplication)
- *logA(b) * logB(a) = 1*
- *logB(x) = logA(x) * 1/logA(B)* (allows **transforming logarithms between bases**)
- *logN(N) = 1*
- *logN(1) = 0*
- *N^logN(x) = x*
- *logN(N^x) = x*
- ...
To get back to the **logarithmic scales** for a moment: these are scales whose value at each step increases not by a constant added number, but by multiplying the value of the previous step by some fixed fraction. In graphs such scale may be used on the *x* or *y* axis or both, depending on the need -- imagine for instance we were about to plot some exponentially increasing phenomenon, i.e. something that over each period of time (such as a year) grows by some fixed PERCENTAGE (fraction). Example may be the [Moore's law](moores_law.md) stating that the number of [transistors](transistor.md) in integrated circuits doubles every two years. Plotting this with linear scales we'll see a curve that very quickly shoots up, turning steeper and steeper, creating a very inconvenient, hard to read graph. If instead we used logarithmic scale on the *y* axis (number of transistors), we'd get a nice straight line! This is because now as we're moving by years on the *x* axis, we are jumping by orders of magnitude on the *y* axis, and since that is logarithmic, a jump by order of magnitude shift us a constant step up. This is therefore very useful for handling phenomena that "up closer" need higher resolution and "further away" rather need more more space and bigger "zoom out" on detriment of resolution, such as the map of our Universe perhaps.
## Programming
It won't come as a surprise that we'll find the logarithm function built in most of the popular [programming_languages](programming_language.md), most often present as part of the standard math [library](library.md)/module. Make sure to check which base it uses etc. [C](c.md) for example has the functions *log(x)* (natural logarithm), *log10(x)* and *log2(x)* under *math.h* -- if you need logarithm with different base, the simple formula given somewhere above will serve you to convert between arbitrary bases (also shown in an example below).
Should you decide for any reason to implement your own logarithm, consider first your requirements. If integer logarithm [is enough](good_enough.md), the straightforward "[brute force](brute_force.md)" way of searching for the correct result in a for loop is quite usable since the number of iterations can't get too high (as by repeated exponentiation we quickly cover the whole range of even 64 bit integers). In C this may be done as follows:
```
int logIntN(int base, int x)
{
int r = 0, n = base;
while (n <= x)
{
n *= base;
r++;
}
return r;
}
```
If we don't insist on having the base a variable, the function will probably [get faster](optimization.md), and especially so in the case of *log2* where multiplication can be replaced by a bit shift:
```
int logInt2(int x)
{
int r = 0, n = 2;
while (n <= x)
{
n <<= 1;
r++;
}
return r;
}
```
As always, [look up tables](lut.md) may help create extremely fast versions for the price of some memory.
Mainstream way of implementing [floating point](float.md) logarithm is probably through [Taylor series](taylor_series.md) such as (for natural logarithm):
*ln(x) = 2 * ((x-1)/(x+1) + 1/3 * ((x-1)/(x+1))^3 + 1/5 * ((x-1)/(x+1))^5 + ...)*
Here this formula is used to implement a somewhat workable floating point natural logarithm and general base logarithm (a bit dirty due to hardcoded constants, but can be a start):
```
double logFloatE(double x)
{
double r = 0;
if (x > 2) // for larger values precision decreases
return logFloatE(x * 3.0/4.0) - logFloatE(3.0/4.0);
x = (x - 1) / (x + 1);
for (double i = 1; i < 8; i += 2) // 8 arbitrarily chosen
{
r += x / i;
x *= x * x;
}
return 2 * r;
}
double logFloatN(double base, double x)
{
return logFloatE(x) / logFloatE(base);
}
```
If you have the *pow* function at hand, you can probably implement floating point logarithm also through [binary search](binary_search.md) with delta.