Calculate logarithm by hand - javascript

I'd like to calculate the mathematical logarithm "by hand"...
... where stands for the logarithmBase and stands for the value.
Some examples (See Log calculator):
The base 2 logarithm of 10 is 3.3219280949
The base 5 logarithm of 15 is 1.6826061945
...
Hoever - I do not want to use a already implemented function call like Math.ceil, Math.log, Math.abs, ..., because I want a clean native solution that just deals with +-*/ and some loops.
This is the code I got so far:
function myLog(base, x)  {
let result = 0;
do {
x /= base;
result ++;
} while (x >= base)
return result;
}
let x = 10,
base = 2;
let result = myLog(base, x)
console.log(result)
But it doesn't seems like the above method is the right way to calculate the logarithm to base N - so any help how to fix this code would be really appreciated.
Thanks a million in advance jonas.

You could use a recursive approach:
const log = (base, n, depth = 20, curr = 64, precision = curr / 2) =>
depth <= 0 || base ** curr === n
? curr
: log(base, n, depth - 1, base ** curr > n ? curr - precision : curr + precision, precision / 2);
Usable as:
log(2, 4) // 2
log(2, 10) // 3.32196044921875
You can influence the precision by changing depth, and you can change the range of accepted values (currently ~180) with curr
How it works:
If we already reached the wanted depth or if we already found an accurate value:
depth <= 0 || base ** curr === n
Then it just returns curr and is done. Otherwise it checks if the logarithm we want to find is lower or higher than the current one:
base ** curr > n
It will then continue searching for a value recursively by
1) lowering depth by one
2) increasing / decreasing curr by the current precision
3) lower precision
If you hate functional programming, here is an imperative version:
function log(base, n, depth = 20) {
let curr = 64, precision = curr / 2;
while(depth-- > 0 && base ** curr !== n) {
if(base ** curr > n) {
curr -= precision;
} else {
curr += precision;
}
precision /= 2;
}
return curr;
}
By the way, the algorithm i used is called "logarithmic search" commonly known as "binary search".

First method: with a table of constants.
First normalize the argument to a number between 1 and 2 (this is achieved by multiplying or dividing by 2 as many times as necessary - keep a count of these operations). For efficiency, if the values can span many orders of magnitude, instead of equal factors you can use a squared sequence, 2, 4, 16, 256..., followed by a dichotomic search when you have bracketed the value.
F.i. if the exponents 16=2^4 works but not 256=2^8, you try 2^6, then one of 2^5 and 2^7 depending on outcome. If the final exponent is 2^d, the linear search takes O(d) operations and the geometric/dichotomic search only O(log d). To avoid divisions, it is advisable to keep a table of negative powers.
After normalization, you need to refine the mantissa. Compare the value to √2, and if larger multiply by 1/√2. This brings the value between 1 and √2. Then compare to √√2 and so on. As you go, you add the weights 1/2, 1/4, ... to the exponent when a comparison returns greater.
In the end, the exponent is the base 2 logarithm.
Example: lg 27
27 = 2^4 x 1.6875
1.6875 > √2 = 1.4142 ==> 27 = 2^4.5 x 1.1933
1.1933 > √√2 = 1.1892 ==> 27 = 2^4.75 x 1.0034
1.0034 < √√√2 = 1.0905 ==> 27 = 2^4.75 x 1.0034
...
The true value is 4.7549.
Note that you can work with other bases, in particular e. In some contexts, base 2 allows shortcuts, this is why I used it. Of course, the square roots should be tabulated.
Second method: with a Taylor series.
After the normalization step, you can use the standard series
log(1 + x) = x - x²/2 + x³/3 - ...
which converges for |x| < 1. (Caution: we now have natural logarithms.)
As convergence is too slow for values close to 1, it is advisable to use the above method to reduce to the range [1, √2). Then every new term brings a new bit of accuracy.
Alternatively, you can use the series for log((1 + x)/(1 - x)), which gives a good convergence speed even for the argument 2. See https://fr.wikipedia.org/wiki/Logarithme_naturel#D%C3%A9veloppement_en_s%C3%A9rie
Example: with x = 1.6875, y = 0.2558 and
2 x (0.2558 + 0.2558³/3 + 0.2558^5/5) = 0.5232
lg 27 ~ 4 + 0.5232 / ln 2 = 4.7548

Related

Creating ratios from numbers Less than 1 but greater than 0 using Javascript [duplicate]

For example, if my function was called getlowestfraction(), this is what I expect it to do:
getlowestfraction(0.5) // returns 1, 2 or something along the lines of that
Another example:
getlowestfraction(0.125) // returns 1, 8 or something along the lines of that
Using Continued Fractions one can efficiently create a (finite or infinite) sequence of fractions hn/kn that are arbitrary good approximations to a given real number x.
If x is a rational number, the process stops at some point with hn/kn == x. If x is not a rational number, the sequence hn/kn, n = 0, 1, 2, ... converges to x very quickly.
The continued fraction algorithm produces only reduced fractions (nominator and denominator are relatively prime), and the fractions are in
some sense the "best rational approximations" to a given real number.
I am not a JavaScript person (programming in C normally), but I have tried to implement the algorithm with the following JavaScript function. Please forgive me if there are stupid errors. But I have checked the function and it seems to work correctly.
function getlowestfraction(x0) {
var eps = 1.0E-15;
var h, h1, h2, k, k1, k2, a, x;
x = x0;
a = Math.floor(x);
h1 = 1;
k1 = 0;
h = a;
k = 1;
while (x-a > eps*k*k) {
x = 1/(x-a);
a = Math.floor(x);
h2 = h1; h1 = h;
k2 = k1; k1 = k;
h = h2 + a*h1;
k = k2 + a*k1;
}
return h + "/" + k;
}
The loop stops when the rational approximation is exact or has the given precision eps = 1.0E-15. Of course, you can adjust the precision to your needs. (The while condition is derived from the theory of continued fractions.)
Examples (with the number of iterations of the while-loop):
getlowestfraction(0.5) = 1/2 (1 iteration)
getlowestfraction(0.125) = 1/8 (1 iteration)
getlowestfraction(0.1+0.2) = 3/10 (2 iterations)
getlowestfraction(1.0/3.0) = 1/3 (1 iteration)
getlowestfraction(Math.PI) = 80143857/25510582 (12 iterations)
Note that this algorithm gives 1/3 as approximation for x = 1.0/3.0. Repeated multiplication of x by powers of 10 and canceling common factors would give something like 3333333333/10000000000.
Here is an example of different precisions:
With eps = 1.0E-15 you get getlowestfraction(0.142857) = 142857/1000000.
With eps = 1.0E-6 you get getlowestfraction(0.142857) = 1/7.
You could keep multiplying by ten until you have integer values for your numerator and denominator, then use the answers from this question to reduce the fraction to its simplest terms.
Try this program instead:
function toFrac(number) {
var fractional = number % 1;
if (fractional) {
var real = number - fractional;
var exponent = String(fractional).length - 2;
var denominator = Math.pow(10, exponent);
var mantissa = fractional * denominator;
var numerator = real * denominator + mantissa;
var gcd = GCD(numerator, denominator);
denominator /= gcd;
numerator /= gcd;
return [numerator, denominator];
} else return [number, 1];
}
function gcd(numerator, denominator) {
do {
var modulus = numerator % denominator;
numerator = denominator;
denominator = modulus;
} while (modulus);
return numerator;
}
Then you may use it as follows:
var start = new Date;
var PI = toFrac(Math.PI);
var end = new Date;
alert(PI);
alert(PI[0] / PI[1]);
alert(end - start + " ms");
You can see the demo here: http://jsfiddle.net/MZaK9/1/
Was just fiddling around with code, and got the answer myself:
function getlowestfraction (num) {
var i = 1;
var mynum = num;
var retnum = 0;
while (true) {
if (mynum * i % 1 == 0) {
retnum = mynum * i;
break;
}
// For exceptions, tuned down MAX value a bit
if (i > 9000000000000000) {
return false;
}
i++;
}
return retnum + ", " + i;
}
In case anybody needed it.
P.S: I'm not trying to display my expertise or range of knowledge. I actually did spend a long time in JSFiddle trying to figure this out (well not a really long time anyway).
Suppose the number is x = 0 . ( a_1 a_2 ... a_k ) ( a_1 a_2 ... a_k ) .... for simplicity (keep in mind that the first few digits may not fit the repeating pattern, and that we need a way to figure out what k is). If b is the base, then
b ^ k * x - x = ( b ^ k - 1 ) * x
on one hand, but
b ^ k * x - x = ( a_1 a_2 ... a_k )
(exact, ie this is an integer) on the other hand.
So
x = ( a_1 ... a_k ) / ( b ^ k - 1 )
Now you can use Euclid's algorithm to get the gcd and divide it out to get the reduced fraction.
You would still have to figure out how to determine the repeating sequence. There should be an answer to that question. EDIT - one answer: it's the length of \1 if there's a match to the pattern /([0-9]+)\1+$/ (you might want to throw out the last digit before matching bc of rounding). If there's no match, then there's no better "answer" than the "trivial" representation" (x*base^precision/base^precision).
N.B. This answer makes some assumptions on what you expect of an answer, maybe not right for your needs. But it's the "textbook" way of getting reproducing the fraction from a repeating decimal representation - see e.g. here
A very old but a gold question which at the same time an overlooked one. So i will go and mark this popular one as a duplicate with hopes that new people end up at the correct place.
The accepted answer of this question is a gem of the internet. No library that i am aware of uses this magnificient technique and ends up with not wrong but silly rationals. Having said that, the accepted answer is not totally correct due to several issues like;
What exactly is happening there?
Why it still returns '140316103787451/7931944815571' instead of '1769/100' when the input is 17.69?
How do you decide when to stop the while loop?
Now the most important question is, what's happening there and howcome this algorithm is so very efficient.
We must know that any number can also be expressed as a continuous fraction. Say you are given 0.5. You can express it like
1
0 + ___ // the 0 here is in fact Math.floor(0.5)
2 // the 2 here is in fact Math.floor(1/0.5)
So say you are given 2.175 then you end up with
1
2 + _______________ // the 2 here is in fact Math.floor(2.175)
1
5 + ___________ // the 5 here is in fact Math.floor(1/0.175 = 5.714285714285714)
1
1 + _______ // the 1 here is in fact Math.floor(1/0.714285714285714 = 1.4)
1
2 + ___ // the 2 here is in fact Math.floor(1/0.4 = 2.5)
2 // the 2 here is in fact Math.floor(1/0.5)
We now have our continued fraction coefficients like [2;5,1,2,2] for 2.175. However the beauty of this algorithm lies behind how it calculates the approximation at once when we calculate the next continued fraction constant without requiring any further calculations. At this very moment we can compare the currently reached result with the given value and decide to stop or iterate once more.
So far so good however it still doesn't make sense right? Let us go with another solid example. Input value is 3.686635944700461. Now we are going to approach this from Infinity and very quickly converge to the result. So our first rational is 1/0 aka Infinity. We denote this as a fraction with a numerator p as 1 and denominator q as 0 aka 1/0. The previous approximation would be p_/q_ for the next stage. Let us make it 0 to start with. So p_ is 0 and q_ is 1.
The important part is, once we know the two previous approximations, (p, q, p_ and q_) we can then calculate the next coefficient m and also the next p and q to compare with the input. Calculating the coefficient m is as simple as Math.floor(x_) whereas x_ is reciprocal of the next floating part. The next approximation p/q would then be (m * p + p_)/(m * q + q_) and the next p_/q_ would be the previous p/q. (Theorem 2.4 # this paper)
Now given above information any decent programmer can easily resolve the following snippet. For curious, 3.686635944700461 is 800/217 and gets calculated in just 5 iterations by the below code.
function toRational(x){
var m = Math.floor(x),
x_ = 1/(x-m),
p_ = 1,
q_ = 0,
p = m,
q = 1;
if (x === m) return {n:p,d:q};
while (Math.abs(x - p/q) > Number.EPSILON){
m = Math.floor(x_);
x_ = 1/(x_-m);
[p_, q_, p, q] = [p, q, m*p+p_, m*q+q_];
}
return isNaN(x) ? NaN : {n:p,d:q};
}
Under practical considerations it would be ideal to store the coefficients in the fraction object as well so that in future you may use them to perform CFA (Continuous Fraction Arithmetics) among rationals. This way you may avoid huge integers and possible BigInt usage by staying in the CF domain to perform invertion, negation, addition and multiplication operations. Sadly, CFA is a very overlooked topic but it helps us to avoid double precision errors when doing cascaded arithmetic operations on the rational type values.

How can I get integer in Math.pow(10, 10000000)

I always get infinity from:
let power = Math.pow(2, 10000000);
console.log(power); //Infinity
So, can I get integer from this?
Maybe I don't understand this task https://www.codewars.com/kata/5511b2f550906349a70004e1/train/javascript? Who knows, show me how to decide that?
The link that you give asks for the last digit of the number. In order to find such a thing, it would be insane to compute an extremely large number (which might exceed the storage capacity of the known universe to write down (*)) just to find the final digit. Work mod 10.
Two observations:
1) n^e % 10 === d^e % 10 // d = last digit of n
2) If e = 10q+r then n^e % 10 === (n^10)^q * n^d %10
This allows us to write:
const lastDigit = function(str1, str2){
//in the following helper function d is an integer and exp a string
const lastDigitHelper = function(d,exp){
if(exp.length === 1){
let e = parseInt(exp);
return Math.pow(d,e) % 10;
} else {
let r = parseInt(exp.slice(-1));
let q = exp.slice(0,-1);
return lastDigitHelper(Math.pow(d,10) % 10,q) * Math.pow(d,r) % 10;
}
}
let d = parseInt(str1.slice(-1));
return lastDigitHelper(d,str2);
}
This passes all of the tests, but isn't as efficient as it could be. The recursive helper function could be replaced by a loop.
(*) For fun: one of the test cases was to compute the last digit of
1606938044258990275541962092341162602522202993782792835301376 ^ 2037035976334486086268445688409378161051468393665936250636140449354381299763336706183397376
If written in base 2, this number would be approximately 4.07 x 10^92 bits long. Since there are fewer than that many atoms in the universe, the number is much too large to store, not to mention too time consuming to compute.
Javascript has a maximum safe integer:
Number.MAX_SAFE_INTEGER
9007199254740991
safe = the ability to represent integers exactly and to correctly compare them
In your case, the number is greater:
Math.pow(2, 10000000) >= Number.MAX_SAFE_INTEGER
true
Or not smaller:
Math.pow(2, 10000000) <= Number.MAX_SAFE_INTEGER
false
You can use an arbitrary size integer library like big-integer to work with larger integers

Codility Ladder javascript - not understanding a detail that jumps the answer from 37 to 100%

I'm trying to solve all the lessons on codility but I failed to do so on the following problem: Ladder by codility
I've searched all over the internet and I'm not finding a answer that satisfies me because no one answers why the max variable impacts so much the result.
So, before posting the code, I'll explain the thinking.
By looking at it I didn't need much time to understand that the total number of combinations it's a Fibonacci number, and removing the 0 from the Fibonacci array, I'd find the answer really fast.
Now, afterwards, they told that we should return the number of combinations modulus 2^B[i].
So far so good, and I decided to submit it without the var max, then I got a score of 37%.. I searched all over the internet and the 100% result was similar to mine but they added that max = Math.pow(2,30).
Can anyone explain to me how and why that max influences so much the score?
My Code:
// Powers 2 to num
function pow(num){
return Math.pow(2,num);
}
// Returns a array with all fibonacci numbers except for 0
function fibArray(num){
// const max = pow(30); -> Adding this max to the fibonaccy array makes the answer be 100%
const arr = [0,1,1];
let current = 2;
while(current<=num){
current++;
// next = arr[current-1]+arr[current-2] % max;
next = arr[current-1]+arr[current-2]; // Without this max it's 30 %
arr.push(next);
}
arr.shift(); // remove 0
return arr;
}
function solution(A, B) {
let f = fibArray(A.length + 1);
let res = new Array(A.length);
for (let i = 0; i < A.length; ++i) {
res[i] = f[A[i]] % (pow(B[i]));
}
return res;
}
console.log(solution([4,4,5,5,1],[3,2,4,3,1])); //5,1,8,0,1
// Note that the console.log wont differ in this solution having max set or not.
// Running the exercise on Codility shows the full log with all details
// of where it passed and where it failed.
The limits for input parameters are:
Assume that:
L is an integer within the range [1..50,000];
each element of array A is an integer within the range [1..L];
each element of array B is an integer within the range [1..30].
So the array f in fibArray can be 50,001 long.
Fibonacci numbers grow exponentially; according to this page, the 50,000th Fib number has over 10,000 digits.
Javascript does not have built-in support for arbitrary precision integers, and even doubles only offer ~14 s.f. of precision. So with your modified code, you will get "garbage" values for any significant value of L. This is why you only got 30%.
But why is max necessary? Modulo math tells us that:
(a + b) % c = ([a % c] + [b % c]) % c
So by applying % max to the iterative calculation step arr[current-1] + arr[current-2], every element in fibArray becomes its corresponding Fib number mod max, without any variable exceeding the value of max (or built-in integer types) at any time:
fibArray[2] = (fibArray[1] + fibArray[0]) % max = (F1 + F0) % max = F2 % max
fibArray[3] = (F2 % max + F1) % max = (F2 + F1) % max = F3 % max
fibArray[4] = (F3 % max + F2 % max) = (F3 + F2) % max = F4 % max
and so on ...
(Fn is the n-th Fib number)
Note that as B[i] will never exceed 30, pow(2, B[i]) <= max; therefore, since max is always divisible by pow(2, B[i]), applying % max does not affect the final result.
Here is a python 100% answer that I hope offers an explanation :-)
In a nutshell; modulus % is similar to 'bitwise and' & for certain numbers.
eg any number % 10 is equivalent to the right most digit.
284%10 = 4
1994%10 = 4
FACTS OF LIFE:
for multiples of 2 -> X % Y is equivalent to X & ( Y - 1 )
precomputing (2**i)-1 for i in range(1, 31) is faster than computing everything in B when super large arrays are given as args for this particular lesson.
Thus fib(A[i]) & pb[B[i]] will be faster to compute than an X % Y style thingy.
https://app.codility.com/demo/results/trainingEXWWGY-UUR/
And for completeness the code is here.
https://github.com/niall-oc/things/blob/master/codility/ladder.py
Here is my explanation and solution in C++:
Compute the first L fibonacci numbers. Each calculation needs modulo 2^30 because the 50000th fibonacci number cannot be stored even in long double, it is so big. Since INT_MAX is 2^31, the summary of previously modulo'd numbers by 2^30 cannot exceed that. Therefore, we do not need to have bigger store and/or casting.
Go through the arrays executing the lookup and modulos. We can be sure this gives the correct result since modulo 2^30 does not take any information away. E.g. modulo 100 does not take away any information for subsequent modulo 10.
vector<int> solution(vector<int> &A, vector<int> &B)
{
const int L = A.size();
vector<int> fibonacci_numbers(L, 1);
fibonacci_numbers[1] = 2;
static const int pow_2_30 = pow(2, 30);
for (int i = 2; i < L; ++i) {
fibonacci_numbers[i] = (fibonacci_numbers[i - 1] + fibonacci_numbers[i - 2]) % pow_2_30;
}
vector<int> consecutive_answers(L, 0);
for (int i = 0; i < L; ++i) {
consecutive_answers[i] = fibonacci_numbers[A[i] - 1] % static_cast<int>(pow(2, B[i]));
}
return consecutive_answers;
}

Generating large random numbers between an inclusive range in Node.js

So I'm very familiar with the good old
Math.floor(Math.random() * (max - min + 1)) + min;
and this works very nicely with small numbers, however when numbers get larger this quickly becomes biased and only returns numbers one zero below it (for ex. a random number between 0 and 1e100 will almost always (every time I've tested, so several billion times since I used a for loop to generate lots of numbers) return [x]e99). And yes I waited the long time for the program to generate that many numbers, twice. By this point, it would be safe to assume that the output is always [x]e99 for all practical uses.
So next I tried this
Math.floor(Math.pow(max - min + 1, Math.random())) + min;
and while that works perfectly for huge ranges it breaks for small ones. So my question is how can do both - be able to generate both small and large random numbers without any bias (or minimal bias to the point of not being noticeable)?
Note: I'm using Decimal.js to handle numbers in the range -1e2043 < x < 1e2043 but since it is the same algorithm I displayed the vanilla JavaScript forms above to prevent confusion. I can take a vanilla answer and convert it to Decimal.js without any trouble so feel free to answer with either.
Note #2: I want to even out the odds of getting large numbers. For example 1e33 should have the same odds as 1e90 in my 0-1e100 example. But at the same time I need to support smaller numbers and ranges.
Your Problem is Precision. That's the reason you use Decimal.js in the first place. Like every other Number in JS, Math.random() supports only 53 bit of precision (Some browser even used to create only the upper 32bit of randomness). But your value 1e100 would need 333 bit of precision. So the lower 280 bit (~75 decimal places out of 100) are discarded in your formula.
But Decimal.js provides a random() method. Why don't you use that one?
function random(min, max){
var delta = new Decimal(max).sub(min);
return Decimal.random( +delta.log(10) ).mul(delta).add(min);
}
Another "problem" why you get so many values with e+99 is probability. For the range 0 .. 1e100 the probabilities to get some exponent are
e+99 => 90%,
e+98 => 9%,
e+97 => 0.9%,
e+96 => 0.09%,
e+95 => 0.009%,
e+94 => 0.0009%,
e+93 => 0.00009%,
e+92 => 0.000009%,
e+91 => 0.0000009%,
e+90 => 0.00000009%,
and so on
So if you generate ten billion numbers, statistically you'll get a single value up to 1e+90. That are the odds.
I want to even out those odds for large numbers. 1e33 should have the same odds as 1e90 for example
OK, then let's generate a 10random in the range min ... max.
function random2(min, max){
var a = +Decimal.log10(min),
b = +Decimal.log10(max);
//trying to deal with zero-values.
if(a === -Infinity && b === -Infinity) return 0; //a random value between 0 and 0 ;)
if(a === -Infinity) a = Math.min(0, b-53);
if(b === -Infinity) b = Math.min(0, a-53);
return Decimal.pow(10, Decimal.random(Math.abs(b-a)).mul(b-a).add(a) );
}
now the exponents are pretty much uniformly distributed, but the values are a bit skewed. Because 101 to 101.5 10 .. 33 has the same probability as 101.5 to 102 34 .. 100
The issue with Math.random() * Math.pow(10, Math.floor(Math.random() * 100)); at smaller numbers is that random ranges [0, 1), meaning that when calculating the exponent separately one needs to make sure the prefix ranges [1, 10). Otherwise you want to calculate a number in [1eX, 1eX+1) but have e.g. 0.1 as prefix and end up in 1eX-1. Here is an example, maxExp is not 100 but 10 for readability of the output but easily adjustable.
let maxExp = 10;
function differentDistributionRandom() {
let exp = Math.floor(Math.random() * (maxExp + 1)) - 1;
if (exp < 0) return Math.random();
else return (Math.random() * 9 + 1) * Math.pow(10, exp);
}
let counts = new Array(maxExp + 1).fill(0).map(e => []);
for (let i = 0; i < (maxExp + 1) * 1000; i++) {
let x = differentDistributionRandom();
counts[Math.max(0, Math.floor(Math.log10(x)) + 1)].push(x);
}
counts.forEach((e, i) => {
console.log(`E: ${i - 1 < 0 ? "<0" : i - 1}, amount: ${e.length}, example: ${Number.isNaN(e[0]) ? "none" : e[0]}`);
});
You might see the category <0 here which is hopefully what you wanted (the cutoff point is arbitrary, here [0, 1) has the same probability as [1, 10) as [10, 100) and so on, but [0.01, 0.1) is again less likely than [0.1, 1))
If you didn't insist on base 10 you could reinterpret the pseudorandom bits from two Math.random calls as Float64 which would give a similar distribution, base 2:
function exponentDistribution() {
let bits = [Math.random(), Math.random()];
let buffer = new ArrayBuffer(24);
let view = new DataView(buffer);
view.setFloat64(8, bits[0]);
view.setFloat64(16, bits[1]);
//alternatively all at once with setInt32
for (let i = 0; i < 4; i++) {
view.setInt8(i, view.getInt8(12 + i));
view.setInt8(i + 4, view.getInt8(20 + i));
}
return Math.abs(view.getFloat64(0));
}
let counts = new Array(11).fill(0).map(e => []);
for (let i = 0; i < (1 << 11) * 100; i++) {
let x = exponentDistribution();
let exp = Math.floor(Math.log2(x));
if (exp >= -5 && exp <= 5) {
counts[exp + 5].push(x);
}
}
counts.forEach((e, i) => {
console.log(`E: ${i - 5}, amount: ${e.length}, example: ${Number.isNaN(e[0]) ? "none" : e[0]}`);
});
This one obviously is bounded by the precision ends of Float64, there are some uneven parts of the distribution due to some details of IEEE754, e.g. denorms/subnorms and i did not take care of special values like Infinity. It is rather to be seen as a fun extra, a reminder of the distribution of float values. Note that the loop does 1 << 11 (2048) times a number iterations, which is about the exponent range of Float64, 11 bit, [-1022, 1023]. That's why in the example each bucket gets approximately said number (100) hits.
You can create the number in increments less than Number.MAX_SAFE_INTEGER, then concatenate the generated numbers to a single string
const r = () => Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
let N = "";
for (let i = 0; i < 10; i++) N += r();
document.body.appendChild(document.createTextNode(N));
console.log(/e/.test(N));

How to simplify a decimal into the smallest possible fraction?

For example, if my function was called getlowestfraction(), this is what I expect it to do:
getlowestfraction(0.5) // returns 1, 2 or something along the lines of that
Another example:
getlowestfraction(0.125) // returns 1, 8 or something along the lines of that
Using Continued Fractions one can efficiently create a (finite or infinite) sequence of fractions hn/kn that are arbitrary good approximations to a given real number x.
If x is a rational number, the process stops at some point with hn/kn == x. If x is not a rational number, the sequence hn/kn, n = 0, 1, 2, ... converges to x very quickly.
The continued fraction algorithm produces only reduced fractions (nominator and denominator are relatively prime), and the fractions are in
some sense the "best rational approximations" to a given real number.
I am not a JavaScript person (programming in C normally), but I have tried to implement the algorithm with the following JavaScript function. Please forgive me if there are stupid errors. But I have checked the function and it seems to work correctly.
function getlowestfraction(x0) {
var eps = 1.0E-15;
var h, h1, h2, k, k1, k2, a, x;
x = x0;
a = Math.floor(x);
h1 = 1;
k1 = 0;
h = a;
k = 1;
while (x-a > eps*k*k) {
x = 1/(x-a);
a = Math.floor(x);
h2 = h1; h1 = h;
k2 = k1; k1 = k;
h = h2 + a*h1;
k = k2 + a*k1;
}
return h + "/" + k;
}
The loop stops when the rational approximation is exact or has the given precision eps = 1.0E-15. Of course, you can adjust the precision to your needs. (The while condition is derived from the theory of continued fractions.)
Examples (with the number of iterations of the while-loop):
getlowestfraction(0.5) = 1/2 (1 iteration)
getlowestfraction(0.125) = 1/8 (1 iteration)
getlowestfraction(0.1+0.2) = 3/10 (2 iterations)
getlowestfraction(1.0/3.0) = 1/3 (1 iteration)
getlowestfraction(Math.PI) = 80143857/25510582 (12 iterations)
Note that this algorithm gives 1/3 as approximation for x = 1.0/3.0. Repeated multiplication of x by powers of 10 and canceling common factors would give something like 3333333333/10000000000.
Here is an example of different precisions:
With eps = 1.0E-15 you get getlowestfraction(0.142857) = 142857/1000000.
With eps = 1.0E-6 you get getlowestfraction(0.142857) = 1/7.
You could keep multiplying by ten until you have integer values for your numerator and denominator, then use the answers from this question to reduce the fraction to its simplest terms.
Try this program instead:
function toFrac(number) {
var fractional = number % 1;
if (fractional) {
var real = number - fractional;
var exponent = String(fractional).length - 2;
var denominator = Math.pow(10, exponent);
var mantissa = fractional * denominator;
var numerator = real * denominator + mantissa;
var gcd = GCD(numerator, denominator);
denominator /= gcd;
numerator /= gcd;
return [numerator, denominator];
} else return [number, 1];
}
function gcd(numerator, denominator) {
do {
var modulus = numerator % denominator;
numerator = denominator;
denominator = modulus;
} while (modulus);
return numerator;
}
Then you may use it as follows:
var start = new Date;
var PI = toFrac(Math.PI);
var end = new Date;
alert(PI);
alert(PI[0] / PI[1]);
alert(end - start + " ms");
You can see the demo here: http://jsfiddle.net/MZaK9/1/
Was just fiddling around with code, and got the answer myself:
function getlowestfraction (num) {
var i = 1;
var mynum = num;
var retnum = 0;
while (true) {
if (mynum * i % 1 == 0) {
retnum = mynum * i;
break;
}
// For exceptions, tuned down MAX value a bit
if (i > 9000000000000000) {
return false;
}
i++;
}
return retnum + ", " + i;
}
In case anybody needed it.
P.S: I'm not trying to display my expertise or range of knowledge. I actually did spend a long time in JSFiddle trying to figure this out (well not a really long time anyway).
Suppose the number is x = 0 . ( a_1 a_2 ... a_k ) ( a_1 a_2 ... a_k ) .... for simplicity (keep in mind that the first few digits may not fit the repeating pattern, and that we need a way to figure out what k is). If b is the base, then
b ^ k * x - x = ( b ^ k - 1 ) * x
on one hand, but
b ^ k * x - x = ( a_1 a_2 ... a_k )
(exact, ie this is an integer) on the other hand.
So
x = ( a_1 ... a_k ) / ( b ^ k - 1 )
Now you can use Euclid's algorithm to get the gcd and divide it out to get the reduced fraction.
You would still have to figure out how to determine the repeating sequence. There should be an answer to that question. EDIT - one answer: it's the length of \1 if there's a match to the pattern /([0-9]+)\1+$/ (you might want to throw out the last digit before matching bc of rounding). If there's no match, then there's no better "answer" than the "trivial" representation" (x*base^precision/base^precision).
N.B. This answer makes some assumptions on what you expect of an answer, maybe not right for your needs. But it's the "textbook" way of getting reproducing the fraction from a repeating decimal representation - see e.g. here
A very old but a gold question which at the same time an overlooked one. So i will go and mark this popular one as a duplicate with hopes that new people end up at the correct place.
The accepted answer of this question is a gem of the internet. No library that i am aware of uses this magnificient technique and ends up with not wrong but silly rationals. Having said that, the accepted answer is not totally correct due to several issues like;
What exactly is happening there?
Why it still returns '140316103787451/7931944815571' instead of '1769/100' when the input is 17.69?
How do you decide when to stop the while loop?
Now the most important question is, what's happening there and howcome this algorithm is so very efficient.
We must know that any number can also be expressed as a continuous fraction. Say you are given 0.5. You can express it like
1
0 + ___ // the 0 here is in fact Math.floor(0.5)
2 // the 2 here is in fact Math.floor(1/0.5)
So say you are given 2.175 then you end up with
1
2 + _______________ // the 2 here is in fact Math.floor(2.175)
1
5 + ___________ // the 5 here is in fact Math.floor(1/0.175 = 5.714285714285714)
1
1 + _______ // the 1 here is in fact Math.floor(1/0.714285714285714 = 1.4)
1
2 + ___ // the 2 here is in fact Math.floor(1/0.4 = 2.5)
2 // the 2 here is in fact Math.floor(1/0.5)
We now have our continued fraction coefficients like [2;5,1,2,2] for 2.175. However the beauty of this algorithm lies behind how it calculates the approximation at once when we calculate the next continued fraction constant without requiring any further calculations. At this very moment we can compare the currently reached result with the given value and decide to stop or iterate once more.
So far so good however it still doesn't make sense right? Let us go with another solid example. Input value is 3.686635944700461. Now we are going to approach this from Infinity and very quickly converge to the result. So our first rational is 1/0 aka Infinity. We denote this as a fraction with a numerator p as 1 and denominator q as 0 aka 1/0. The previous approximation would be p_/q_ for the next stage. Let us make it 0 to start with. So p_ is 0 and q_ is 1.
The important part is, once we know the two previous approximations, (p, q, p_ and q_) we can then calculate the next coefficient m and also the next p and q to compare with the input. Calculating the coefficient m is as simple as Math.floor(x_) whereas x_ is reciprocal of the next floating part. The next approximation p/q would then be (m * p + p_)/(m * q + q_) and the next p_/q_ would be the previous p/q. (Theorem 2.4 # this paper)
Now given above information any decent programmer can easily resolve the following snippet. For curious, 3.686635944700461 is 800/217 and gets calculated in just 5 iterations by the below code.
function toRational(x){
var m = Math.floor(x),
x_ = 1/(x-m),
p_ = 1,
q_ = 0,
p = m,
q = 1;
if (x === m) return {n:p,d:q};
while (Math.abs(x - p/q) > Number.EPSILON){
m = Math.floor(x_);
x_ = 1/(x_-m);
[p_, q_, p, q] = [p, q, m*p+p_, m*q+q_];
}
return isNaN(x) ? NaN : {n:p,d:q};
}
Under practical considerations it would be ideal to store the coefficients in the fraction object as well so that in future you may use them to perform CFA (Continuous Fraction Arithmetics) among rationals. This way you may avoid huge integers and possible BigInt usage by staying in the CF domain to perform invertion, negation, addition and multiplication operations. Sadly, CFA is a very overlooked topic but it helps us to avoid double precision errors when doing cascaded arithmetic operations on the rational type values.

Categories