Related
I am writing a simple do...while statement using Javascript that's meant to look at the length of a decimal of a given input num and if the length of the decimal is greater than or equals to 1, to take the num and added it onto itself until the decimal length is equal to 0. Currently, it works for decimals with the length of 1, but anything greater, it stops.
The expected output, for example, when num is 8.75, should be 35, not 17.5.
The series should take 4 steps to reach 35.
8.75
17.5
26.25
35
Here's my code:
const num = 8.75;
var decimalLength = num.toString().split(".")[1].length;
let result = "";
let i = num;
do {
i = i + num;
result = result + num;
newLength = decimalLength;
} while (newLength < 0);
console.log(i);
You can use some fancy maths to get a more definitive answer that doesn't loop over and over again:
const num = 8.75;
var decimal = num % 1 //Decimal part
var decimalPlaces = num.toString().split(".")[1] ?.length || 0; //Get decimal places
//GCD function
const gcd = (x, y) => (!y ? x : gcd(y, x % y));
//GCD between decimal and 1, need to make it whole number to get multiplier, so multiply each with 10^[decimal places]
//Get what's remaining when divided by 10^[decimal places] as well, to see what to multiply by to get whole number
var multiplier = 10 ** decimalPlaces / gcd(10 ** decimalPlaces, decimal * 10 ** decimalPlaces)
//Use log change of base property to get value in power of 2
var outputExponent = Math.log(multiplier) / Math.log(2)
//Multiply number by multiplier
var outputValue = num * multiplier
console.log(outputExponent) //Power of 2 to multiply by to get whole number
console.log(outputValue) //The whole number itself
This version uses a recursive function and a tolerance to deal with floating point rounding errors.
const firstIntMultiple = (n, tol=1e-6, c = 1) =>
Math.abs(n * c - Math.floor(n * c)) < tol ? n * c : firstIntMultiple (n, tol, c + 1)
console .log (firstIntMultiple (8.75)) // 35/4
console .log (firstIntMultiple (8.333333333333334)) // 25/3
console .log (firstIntMultiple (14.058823529411764)) // 239/17
It finds the correct version by multiplication by successive integers instead of successive additions, but it's the same idea.
We can easily replace the recursive version by an iterative approach, which might be useful for numbers which don't have a good rational approximation. (Right now, passing Math.PI will run into recursion limits.)
That might look like this:
const firstIntMultiple = (n, tol=1e-6) => {
let c = 1;
while (Math.abs(n * c - Math.floor(n * c)) > tol) {
c += 1
}
return n * c
}
and for Math.PI, that would return 4272943.0000005495, the first multiple of pi that is within 1e-6 of an integer. And you can adjust the tolerance as needed.
Update -- an entirely different technique
Another technique would take advantage of the fact that continued fractions offer a straightforward way to find the best rational approximations to a number. We can use this to find the ten best rational approximations to pi, for instance like this:
bestApprox (Math.PI, 1, 10) .map (({n, d}) => `${n}/${d}`)
// => ["3/1", "22/7", "333/106", "355/113", "103993/33102", "104348/33215",
// "208341/66317", "312689/99532", "833719/265381", "1146408/364913"]
And then, using those approximations, we can find the first one that is within some small distance from our target value.
The code might look like this:
const contFrac = (n, d = 1, count = Infinity) =>
count < 1 || d == 0
? []
: [Math .floor (n / d)] .concat (contFrac (d, n % d, count - 1))
const bestApprox = (n, d = 1, c) =>
contFrac(n, d, c)
.reduce ((a, n, i) => [
...a,
{
n: a [i] .n + n * a [i + 1] .n,
d: a [i] .d + n * a [i + 1] .d
}
], [{n: 0, d: 1}, {n: 1, d: 0}])
.slice (2)
const firstIntMultiple = (x, ε = 1e-6) =>
bestApprox (x, 1)
.find (({n, d}, i, a) => i == a.length - 1 || Math.abs (n / d - x) < ε)
.n
console .log (firstIntMultiple (8.75)) // 35/4
console .log (firstIntMultiple (8.333333333333334)) // 25/3
console .log (firstIntMultiple (14.058823529411764)) // 239/17
console .log (firstIntMultiple (Math.PI)) // 353/113
console .log (firstIntMultiple (Math.PI, 1e-8)) // 103993/33102
I haven't tested for performance, but this should be reasonably good, especially for those number whose continued fractions contain large integers early. (pi for instance is <3; 7, 15, 1, 292,...> That 292 implies that 3 + (1 / (7 + (1 / (15 + 1 / 1)))) or 355 / 113 is an excellent approximation to pi, and in fact, it's good to six decimal places.
I don't know how helpful this is to the OP, but it shows that ancient math classes might one day come in handy!. ;-)
Update 2 - Now With More Explanation!
This version cleans up the issue in the second approach with small values, by checking not if the test value is within epsilon of the original value but if the ratio of the the test value to the original value is within epsilon of 1. It also has some minor clean-up and a smaller value for the default epsilon:
const contFrac = (n, d = 1, count = Infinity) =>
count < 1 || d == 0
? []
: [Math .floor (n / d)] .concat (contFrac (d, n % d, count - 1))
const bestApprox = (n, d = 1, count = Infinity) =>
contFrac(n, d, count)
.reduce ((a, n, i) => [
...a,
{
n: a [i] .n + n * a [i + 1] .n,
d: a [i] .d + n * a [i + 1] .d
}
], [{n: 0, d: 1}, {n: 1, d: 0}])
.slice (2)
const isClose = (x, y, ε) =>
y == 0 ? x < ε : (x / y > 1 - ε && x / y < 1 + ε)
const firstIntMultiple = (x, ε = 1e-8) =>
bestApprox (x, 1)
.find (({n, d}, i, a) => i == a.length - 1 || isClose (n / d, x, ε))
.n
console .log (firstIntMultiple (8.75)) // 35/4
console .log (firstIntMultiple (8.333333333333334)) // 25/3
console .log (firstIntMultiple (14.058823529411764)) // 239/17
console .log (firstIntMultiple (Math.PI)) // 103993/33102
console .log (firstIntMultiple (Math.PI, 1e-6)) // 353/113
console .log (firstIntMultiple (13.000000221)) // 58823532/4524887
console .log (firstIntMultiple (1.0000003333)) // 3000301/3000300
console .log (firstIntMultiple (1234/987654321)) // 6/4802209
.as-console-wrapper {min-height: 100% !important; top: 0}
Explanation
The main function here is still firstIntMultiple, and it's fairly simple, just searching the results of bestApprox for a rational approximation that is close enough to our target number and then returning the numerator of that result. "Close enough" is determined by isClose, which checks if the ratio of the two numbers is between 1 - ε and 1 + ε, where ε is an optional parameter that defaults to 1e-8.
So the question is how bestApprox works. For that, we need to discuss Continued Fractions. I cannot do them justice here, but hopefully I can describe enough to give a feel for them.
Here is a repeating infinite simple continued fraction:
1
1 + ---------------------------------
1
2 + ----------------------------
1
2 + ---------------------
1
2 + ---------------
1
2 + ---------
2 + ...
It's a continued fraction because we keep nesting fractions in the denominators of other fractions. It's infinite because... well because it continues on infinitely - in an obvious manner. It's simple because all
the numerators are 1s.
It's not hard to show with a little algebra that this represents the square root of 2.
This will usually be abbreviated with a notation like:
<1; 2, 2, 2, 2, ...>
where all the values are integers, and after the first one, all are positive.
These have a few advantages. All rational numbers have a finite representation as a continued fraction, and all quadratic numbers have a repeating infinite pattern. But more importantly, the prefixes of these continued fractions contain the best rational approximations to a number. (The proof is not terribly difficult, and should be something non-mathematicians can follow. But I won't try it here.)
By this, I mean that these numbers:
<1;> //=> 1
<1; 2> //=> 3/2
<1; 2, 2> //=> 7/5
<1; 2, 2, 2> //=> 17/12
<1; 2, 2, 2, 2> //=> 41/29
...
Are successively better approximations to sqrt(2), and there are no better approximations available except with higher denominators. That is, for example, among denominators greater than 12 and less than 29, there are not better approximations to sqrt(2).
So by calculating the partial continued fraction for a number, we can find all the best approximations, and eventually find one that gets the multiple we're looking for.
Now we need to know how to calculate these continued fractions and then how to turn them their partials into rational numbers. Luckily, both are pretty easy.
To find the elements of a continued fraction, all we need to do is find the floor of the number, add that to our list, then continue with the reciprocal of the remainder.
If we started with 27/11, then the first element would be floor(27/11) or 2; the remainder is 5/11, which has the reciprocal of 11/5, The next digit would be the floor of that, which is 2, with a remainder of 1/5, whose reciprocal is 5 with no remainder. And so 27/11 can be written as <2; 2, 5>.
If we started with pi, then our first element would be 3, then we'd continue with the reciprocal of 0.14159265358979312, which is 7.062513305931052, and the next element would be 7. Taking the reciprocal of the remainder, we get 15.996594406684103, and the next element is 15. The reciprocal of that remainder is 1.0034172310150002, so the next element is 1. Then the reciprocal of the remainder gets much larger, at 292.63459087501246. We could continue to get a result like:
<3; 7, 15, 1, 292, 1, 1, 1, 2, 1, 3, 1, 14, 3, 3, 2, 1, ...>
which has no obvious pattern. But the high value of 292 tells us that <3; 7, 15, 1>, or 355/113 is an excellent approximation for pi.
The function contFrac does this algorithm just as described.
Now, to turn the partials into rational numbers, we can use a simple recursion. In <a_0; a_1, a_2, a_3, ...>, the first approximation is a_0, which we write as a_0/1. The second one is a_0 + (1 / a_1) or (a_0 * a_1) / a_1. After that, we can find the (k + 2)nd value by this simple formulas: n_(k + 2) = a_(k + 2) * n_(k + 1) + n_k, and d_(k + 2) = a_(k + 2) * d_(k + 1) + d_k.
So for <3; 7, 15, 1>, we start with 3/1 then 22/7, then our next value is (15 * 22 + 3) / (15 * 7 + 1) or 333/106, and then (1 * 333 + 22) / (1 * 106 + 7) or 355/113. The code uses a little trick which extends the numerators and denominators back two steps so that we can use our recursion for every step and then simply slices those two value off the final result.
And there we have it. By using the best rational approximations to a number, we can quickly find the smallest integer which, within a small tolerance, is a multiple of that number.
Here's every answer combined. The advantage of this is that it has a 'fallback' for each kind of input. If there's a terminating decimal, Endothermic_Dragon's first method is sufficient. If it is a repeating decimal, then a new method that finds an answer definitively is sufficient. If it is not sufficient, then the continued fraction is used as a fallback.
Note that the only reason I included the continued fraction last is because of the errors it sometimes causes with smaller decimals. I would just 'fix' the method, but I don't understand how it works, so I used it as a fallback.
//At the top so it is viewable
console.log(findMultiple(54.46333333333333)) //16339/300
console.log(findMultiple(8.333333333333333)) //25/3
console.log(findMultiple(14.05882352941176)) //Irrational
console.log(findMultiple(94.03820382038203)) //Irrational
console.log(findMultiple(623.0549383482724)) //1009349/1620
console.log(findMultiple(Math.PI)) //Irrational
console.log(findMultiple(13.000000221)) //1829587379722249/140737488355328
console.log(findMultiple(1.0000003333)) //1125900282105063/1125899906842624
//Main control
function findMultiple(num, interpretLiteral = false, epsilon = 1e-6) {
if (num.toString().length < 17 || num % 1 == 0 || interpretLiteral) {
return EndothermicDragonFirstMethod(num) //Terminating decimal
}
var checkRepeatingNum = CheckRepeating(num)
if (checkRepeatingNum != false) {
return Math.round(EndothermicDragonSecondMethod(num, checkRepeatingNum) * num) //Repeating decimal
} else {
return ScottSauyetFirstMethod(num, epsilon) //Continued fraction
}
}
//Predifined functions
//GCD
function gcd(x, y){return !y ? x : gcd(y, x % y)};
//Check if digits repeat, if they do, return repeat period
function CheckRepeating(num) {
var totalSearchLength = (num % 1).toString().split('.')[1].slice(0, -1).length
var numTemp1 = (num % 1).toString().split('.')[1].slice(0, -1).split('').reverse().join('')
for (var i = 1; i < Math.floor(totalSearchLength / 3); i++) {
var numTemp2 = numTemp1.slice(0, 3 * i)
var searchLength = i
bool = numTemp2.slice(0, searchLength) == numTemp2.slice(searchLength, 2 * searchLength) && numTemp2.slice(0, searchLength) == numTemp2.slice(2 * searchLength, 3 * searchLength)
if (bool) {
return searchLength
}
}
return false
}
//Terminating decimal
function EndothermicDragonFirstMethod(num) {
var decimal = num % 1;
var decimalPlaces = num.toString().split(".")[1]?.length || 0;
var multiplier = 10 ** decimalPlaces / gcd(10 ** decimalPlaces, decimal * (10 ** decimalPlaces));
return num * multiplier;
}
//Repeating decimal
function EndothermicDragonSecondMethod(num, repeatInterval) {
var numArray = num.toString().split('.')[1].slice(-repeatInterval).split('').reverse()
var restOfNum = num.toString().split('.')[1].slice(0, -repeatInterval * 3).split('').reverse()
var counter = 0;
var extraRepeat = 0;
restOfNum.every(el => {
if (el == numArray[counter]) {
extraRepeat++;
counter++;
if (counter == numArray.length) {
counter = 0
}
return true
}
return false
})
var repeatingPart = num.toString().split('.')[1].slice(-repeatInterval * 3 - extraRepeat, -repeatInterval * 2 - extraRepeat)
var notRepeatingPart = num.toString().split('.')[1].slice(0, -repeatInterval * 3 - extraRepeat)
var numerator = (parseInt(notRepeatingPart) * (parseInt("9".repeat(repeatingPart.length)))) + parseInt(repeatingPart)
var denominator = (parseInt("9".repeat(repeatingPart.length)) * (10 ** notRepeatingPart.length))
return denominator / gcd(numerator, denominator)
}
//Otherwise (irrational numbers or other)
function ScottSauyetFirstMethod(num, epsilon = 1e-6) {
const contFrac = (n, d = 1, count = Infinity) =>
count < 1 || d == 0 ? [] : [Math.floor(n / d)].concat(contFrac(d, n % d, count - 1))
const bestApprox = (n, d = 1, c) =>
contFrac(n, d, c)
.reduce((a, n, i) => [
...a,
{
n: a[i].n + n * a[i + 1].n,
d: a[i].d + n * a[i + 1].d
}
], [{
n: 0,
d: 1
}, {
n: 1,
d: 0
}])
.slice(2)
const firstIntMultiple = (x, epsilon) =>
bestApprox(x, 1)
.find(({
n,
d
}, i, a) => i == a.length - 1 || Math.abs(n / d - x) < epsilon)
.n
return firstIntMultiple(num, epsilon)
}
This results in accurate answers, no matter what in the world in the input is (even if it is a small decimal)!
Two competing answers. Which is the better one? There is no way of knowing until you test it out. So, that's what this new answer attempts to do.
These 'tests' (if you will) run each function 50 times, timing each iteration before clearing the console and logging the times' average.
8 Decimal Precision
Endothermic_Dragon:
const num = 3.14159265;
//Function start
const firstIntMultiple = () => {
var decimal = num % 1;
var decimalPlaces = num.toString().split(".")[1] ?.length || 0;
const gcd = (x, y) => (!y ? x : gcd(y, x % y));
var multiplier = 10 ** decimalPlaces / gcd(10 ** decimalPlaces, decimal * (10 ** decimalPlaces));
return num * multiplier;
}
//Function end
var times = []
for (var i = 0; i < 50; i++) {
var startTime = new Date()
console.log(firstIntMultiple().toString())
times.push((new Date() - startTime) / 1000)
}
console.clear()
console.log((times.reduce((a, b) => a + b, 0) / times.length) + " seconds on average")
Scott Sauyet:
const num = 3.14159265;
//Function start
const firstIntMultiple = (tol = 1e-8) => {
let c = 1;
while (Math.abs(num * c - Math.floor(num * c)) > tol) {
c += 1
}
return num * c
}
//Function end
var times = []
for (var i = 0; i < 50; i++) {
var startTime = new Date()
console.log(firstIntMultiple().toString())
times.push((new Date() - startTime) / 1000)
}
console.clear()
console.log((times.reduce((a, b) => a + b, 0) / times.length) + " seconds on average")
10 Decimal Precision
Endothermic_Dragon:
const num = 3.1415926535;
//Function start
const firstIntMultiple = () => {
var decimal = num % 1;
var decimalPlaces = num.toString().split(".")[1] ?.length || 0;
const gcd = (x, y) => (!y ? x : gcd(y, x % y));
var multiplier = 10 ** decimalPlaces / gcd(10 ** decimalPlaces, decimal * (10 ** decimalPlaces));
return num * multiplier;
}
//Function end
var times = []
for (var i = 0; i < 50; i++) {
var startTime = new Date()
console.log(firstIntMultiple().toString())
times.push((new Date() - startTime) / 1000)
}
console.clear()
console.log((times.reduce((a, b) => a + b, 0) / times.length) + " seconds on average")
Scott Sauyet:
const num = 3.1415926535;
//Function start
const firstIntMultiple = (tol = 1e-10) => {
let c = 1;
while (Math.abs(num * c - Math.floor(num * c)) > tol) {
c += 1
}
return num * c
}
//Function end
var times = []
for (var i = 0; i < 50; i++) {
var startTime = new Date()
console.log(firstIntMultiple().toString())
times.push((new Date() - startTime) / 1000)
}
console.clear()
console.log((times.reduce((a, b) => a + b, 0) / times.length) + " seconds on average")
Note that the functions will return slightly different results, due to slight differences in their inner mechanics. Endothermic_Dragon's answer tries to find the multiple which will return an exact whole number, whereas Scott Sauyet's answer attempts to find the multiplier which makes the number within the specified tolerance of a whole number.
These examples can be further scaled by adding more digits of pi on each and reducing the tolerance on Scott Sauyet's answer.
I was initially going to use decimal.js while testing this (https://mikemcl.github.io/decimal.js/), however, it turned out to be quite slow on Scott's method - it took 5.1243 seconds to calculate each iteration, whereas on Endothermic_Dragon's it took 0.0002 seconds each iteration. Note that this was done with 10 iterations on w3schools.
All of this analysis can show two conclusions:
Endothermic_Dragon's answer is scalable and accurate on terminating decimals.
Scott Sauyet's answer consumes some time but produces an accurate answer. This is especially useful when dealing with decimals that do not terminate, whether they are irrational or repeating.
Also, here's an extra test, because why not.
15 Decimal Precision
Endothermic_Dragon:
const num = 3.141592653589793;
//Function start
const firstIntMultiple = () => {
var decimal = num % 1;
var decimalPlaces = num.toString().split(".")[1] ?.length || 0;
const gcd = (x, y) => (!y ? x : gcd(y, x % y));
var multiplier = 10 ** decimalPlaces / gcd(10 ** decimalPlaces, decimal * (10 ** decimalPlaces));
return num * multiplier;
}
//Function end
var times = []
for (var i = 0; i < 50; i++) {
var startTime = new Date()
console.log(firstIntMultiple().toString())
times.push((new Date() - startTime) / 1000)
}
console.clear()
console.log((times.reduce((a, b) => a + b, 0) / times.length) + " seconds on average")
Scott Sauyet:
const num = 3.141592653589793;
//Function start
const firstIntMultiple = (tol = 1e-15) => {
let c = 1;
while (Math.abs(num * c - Math.floor(num * c)) > tol) {
c += 1
}
return num * c
}
//Function end
var times = []
for (var i = 0; i < 50; i++) {
var startTime = new Date()
console.log(firstIntMultiple().toString())
times.push((new Date() - startTime) / 1000)
}
console.clear()
console.log((times.reduce((a, b) => a + b, 0) / times.length) + " seconds on average")
In the RSA asymmetric cryptography algorithm every user has a public key (n,e) and a private key (d) to send and receive encrypted messages from other users.
To encrypt a message it changes the characters to their ascii codes:
HELLO -> 72-69-76-76-79
and to send the message encrypted with RSA (c), has to calculate
c = m^e % n
for each character m in the message using the public keys n and e.
to decrypt a message that the user receives has to calculate:
m = c^d % n (is the same to say m is congruent to c^d mod n)
to each number c usign the private key d.
A little example:
user Beto got the public keys:
n = 64523
e = 127
and his private key:
d = 15583
if some user wants to send a message to Beto:
ABAC -> 65-66-65-67
to encrypt the message the user had to calculate
65^127 % 64523 = 27725
66^127 % 64523 = 6407
65^127 % 64523 = 27725
67^127 % 64523 = 2523
and the encrypted code was 27725-6407-27725-2523
Beto to decipher the message had to calculate:
27725^15583 % 64523 = 65
6407^15583 % 64523 = 66
27725^15583 % 64523 = 65
2523^15583 % 64523 = 67
and he got the decrypted message 65-66-65-67 => ABAC.
Now the question:
I have this code to solve the last part, but I cant use it with big numbers (like the ones in the example):
function getCongruence(c, d, n) {
return Math.pow(c,d) % n;
}
console.log(getCongruence(5,3,7)); // = 6 cuz 5^3=125 and 125 % 7 => 125 - 7*17 = 125 -119
console.log(getCongruence(19,11,17)); // = 8 cuz 19^11=116490258898219 % 17 = 8
console.log(getCongruence(27725,15583,64523)); // should be 65 but it shows NaN
.as-console-wrapper { max-height: 100% !important; top: 0; }
How can I get the result if use big numbers?
Can I use another algorithm to find the answer?
there is a library I can use to do it?
Edit
As per #harold's suggestion, iterative exponetiation by squaring is the fastest way to approach this, I'll leave my original naive recursive method below for comparison.
Edit 2
Added function to handle the inverse of a BigInt for very small numbers.
Used #harold's suggestion to move the modulo reduction inside the function for a performance boost.
Iterative exponentiation by squaring:
const handleBigInverse = x => {
const stringX = x.toString();
if (stringX.length > 21) {
const approximate = Number(stringX.slice(0, 21));
const e = stringX.length - 21;
const inverse = 1 / approximate;
const inverseString = inverse.toString();
const splitString = inverseString.split("e");
splitString[1] = (Number(splitString[1]) - e).toString();
return splitString.join("e");
} else {
const inverse = 1 / Number(x);
return inverse.toString();
}
};
const iterativeExpBySqWithMod = (x, n, mod) => {
let bigX = BigInt(x);
let bigN = BigInt(n);
if (n < 0) {
if (!mod || Math.abs(mod) >= 1) {
return handleBigInverse(iterativeExpBySqWithMod(x, -n));
} else {
return (
handleBigInverse(iterativeExpBySqWithMod(x, -n)) % mod
).toString();
}
}
if (mod) {
const bigMod = BigInt(mod);
let result = BigInt(1);
while (bigN > 0) {
if (bigN % BigInt(2) == 1) {
result = (result * bigX) % bigMod;
}
bigX = (bigX * bigX) % bigMod;
bigN /= BigInt(2);
}
return result;
} else {
let result = BigInt(1);
while (bigN > 0) {
if (bigN % BigInt(2) == 1) {
result *= bigX;
}
bigX *= bigX;
bigN /= BigInt(2);
}
return result;
}
};
// Big numbers output a bigInt
console.log(iterativeExpBySqWithMod(27725, 15583, 64523)); //65n
// Small numbers output a string
console.log(iterativeExpBySqWithMod(72552, -50102)); //"5.550317946486025e-243529"
Naive recursive:
Adjust the maxStack param depending on the environment it will be running in:
function getCongruence(c, d, n, maxStack) {
return getPow(BigInt(c), BigInt(d), BigInt(maxStack)) % BigInt(n);
}
const recursivePow = (value, exponent, total) => {
if (exponent > 1) {
exponent--;
return recursivePow(value, exponent, total) * value;
} else {
if (total) {
return total * value;
} else {
return value;
}
}
};
const getPow = (value, exponent, maxStack) => {
let total = BigInt(0);
while (exponent > maxStack) {
total = recursivePow(value, maxStack, total);
exponent -= maxStack;
}
return recursivePow(value, exponent, total);
};
console.log(getCongruence(27725, 15583, 64523, 3000)); //65n
I don't know much about RSA yet, I'm trying to learn it though.
But at least I know how to fix your code:
function getCongruence(c, d, n) {
return c**d % n;
}
console.log(getCongruence(5,3,7)); // = 6 cuz 5^3=125 and 125 % 7 => 125 - 7*17 = 125 -119
console.log(getCongruence(19,11,17)); // = 8 cuz 19^11=116490258898219 % 17 = 8
console.log(getCongruence(27725n,15583n,64523n)); // now returns 65n (n as in BigInt)
But harold said this (is that true?):
Even if that function worked it still wouldn't work (lol), I mean, you cannot separate the exponentiation and the modular reduction, the intermediate result would become too large, at least for reasonable message and key size. – harold
Regarding the rest of the code I can't get your calculation to work in JS, I don't know why (it works in a calculator though)?
console.log(65^127 % 64523) // 62 instead of 27725
console.log(27725^15583 % 64523) // 20626 instead of 65
// Trying the same with BigInt's:
console.log(65n^127n % 64523n) // 62 instead of 27725
console.log(27725n^15583n % 64523n) // 20626 instead of 65
// This works though:
console.log(65n**127n % 64523n) // 27725
console.log(27725n**15583n % 64523n) // 65
Ah, I get it now. c**d generates a super huge number in memory if used with real world cryptographic keys, hence it will not work. But I guess this will:
function getCongruence(c, d, n) {
return powermod(c,d,n)
}
// from: https://stackoverflow.com/questions/30694842/exponentiation-by-squaring
function powermod(base, exponent, modulus) {
if (base < 0n || exponent < 0n || modulus < 1n)
return -1n
let result = 1n
while (exponent > 0n) {
if ((exponent % 2n) == 1n) {
result = (result * base) % modulus
}
log(base * base)
base = (base * base) % modulus
exponent = exponent / 2n // floor
}
return result
}
Or maybe use the one by PeterN (above here), like this:
function getCongruence(c, d, n) {
return iterativeExpBySqWithMod(c,d,n)
}
I have tried this
function binToDec(num) {
let dec = 0;
for(let i = 0; i < num.length; i++) {
if(num[num.length - (i + 1)] === '1') {
dec += 2 ** i;
}
}
return dec;
}
console.log(binToDec('1010'));
this code is not mine and it works but i want to know how it converts the binary number to decimal and it will be very helpful is you could tell me another way to do it.
I have also tried this
function binToDec(num) {
let bin = parseInt(num, 2);
return bin;
}
console.log(binToDec(1010));
I know this also work but i am not looking for this answer.
thank you for your help.
I just starts with the last character of the string and adds the value of this position to the result.
string dec
------ -------
1010 0
0 0
1 0 + 2
0 2
1 2 + 8
------ ------
10
function binToDec(num) {
let dec = 0;
for (let i = 0; i < num.length; i++) {
if (num[num.length - (i + 1)] === '1') {
dec += 2 ** i;
}
}
return dec;
}
console.log(binToDec('1010')); // 10
Another way is to start with the left side of the sting and
multiply the converted value by the base (2) and
add the value of the string.
The result is now the converted number. This works for all bases, as long as the value at the index is converted to a number.
function binToDec(num) {
let dec = 0;
for (let i = 0; i < num.length; i++) {
dec *= 2;
dec += +num[i];
}
return dec;
}
console.log(binToDec('1101')); // 13
Explanation
Think of how base 10 works.
909 = 900 + 9
= (9 * 100) + (0 * 10) + (9 * 1)
= (9 * 10**2) + (0 * 10**1) + (9 * 10**0)
As you can see, a natural number in base 10 can be seen as a sum where each term is in the form of:
digit * base**digit_position
This is true for any base:
base 2 : 0b101 = (0b1 * 2**2) + (0b0 * 2**1) + (0b1 * 2**0)
base 16 : 0xF0F = (0xF * 16**2) + (0x0 * 16**1) + (0xF * 16**0)
Therefore, here is a possible abstraction of a natural number:
function natural_number (base, digits) {
var sum = 0;
for (var i = 0; i < digits.length; i++) {
digit = digits[i];
digit_position = digits.length - (i + 1);
sum += digit * base**digit_position;
}
return sum;
}
> | natural_number(2, [1, 0, 1]) // 1 * 2**2 + 1 * 2**0
< | 5
> | natural_number(10, [1, 0, 1]) // 1 * 10**2 + 1 * 10**0
< | 101
> | natural_number(16, [1, 0, 1]) // 1 * 16**2 + 1 * 16**0
< | 257
Your own function takes only binary numbers (base 2). In this case digit can be either 0 or 1, that's all. We know that it's useless to multiply something by 0 or 1, so the addition can be replaced with:
if (digit === 1) {
sum += 2**digit_position;
}
Which is the equivalent of:
if (num[num.length - (i + 1)] === '1') {
dec += 2 ** i;
}
Do you get it? :-)
Alternative
You don't feel confortable with the exponentiation operator (**)? There is a workaround. Did you ever notice that multiplying a number by 10 is nothing more than shifting its digits one time to the left?
909 * 10 = 9090
Actually, shifting a number to the left boils down to multiplying this number by its base:
number *= base
This is true for any base:
base 2 : 0b11 * 2 = 0b110
base 16 : 0xBEE * 16 + 0xF = 0xBEE0 + 0xF = 0xBEEF
Based on this, we can build an algorithm to convert an array of digits into a number. A trace of execution with [9,0,9] in base 10 as input would look like this:
init | 0 | n = 0
add 9 | 9 | n += 9
shift | 90 | n *= 10
add 0 | 90 | n += 0
shift | 900 | n *= 10
add 9 | 909 | n += 9
Here is a possible implementation:
function natural_number (base, digits) {
var n = 0;
for (var i = 0; i < digits.length; i++) {
n += digits[i];
if (i + 1 < digits.length) {
n *= base;
}
}
return n;
}
Of course this function works the same as before, and there is a good reason for that. Indeed, unroll the for loop that computes [9,0,9] in base 10, you get this:
return ((0 + 9) * 10 + 0) * 10 + 9;
Then expand this expression:
((0 + 9) * 10 + 0) * 10 + 9
= (0 + 9) * 10 * 10 + 0 * 10 + 9
= 9 * 10 * 10 + 0 * 10 + 9
= 9 * 10**2 + 0 * 10**1 + 9 * 10**0
Do you recognize the equation discussed earlier? :-)
Bonus
Reverse function:
function explode_natural_number (base, number) {
var remainder, exploded = [];
while (number) {
remainder = number % base;
exploded.unshift(remainder);
number = (number - remainder) / base;
}
return exploded.length ? exploded : [0];
}
> | explode_natural_number(2, 5)
< | [1, 0, 1]
> | explode_natural_number(3, 5) // base 3 (5 = 1 * 3**1 + 2 * 3**0) :-)
< | [1, 2]
> | explode_natural_number(16, natural_number(16, [11, 14, 14, 15])) // 0xBEEF
< | [11, 14, 14, 15]
String to number and number to string:
function parse_natural_number (number, base) {
var ZERO = 48, A = 65; // ASCII codes
return natural_number(base, number.split("").map(function (digit) {
return digit.toUpperCase().charCodeAt(0);
}).map(function (code) {
return code - (code < A ? ZERO : A - 10);
}));
}
function stringify_natural_number (number, base) {
var ZERO = 48, A = 65; // ASCII codes
return String.fromCharCode.apply(
String, explode_natural_number(base, number).map(function (digit) {
return digit + (digit < 10 ? ZERO : A - 10);
})
);
}
> | stringify_natural_number(parse_natural_number("48879", 10), 16)
< | "BEEF"
> | parse_natural_number("10", 8)
< | 8
More levels of abstraction for convenience:
function bin_to_dec (number) {
return parse_natural_number(number, 2);
}
function oct_to_dec (number) {
return parse_natural_number(number, 8);
}
function dec_to_dec (number) {
return parse_natural_number(number, 10);
}
function hex_to_dec (number) {
return parse_natural_number(number, 16);
}
function num_to_dec (number) {
switch (number[0] + number[1]) {
case "0b" : return bin_to_dec(number.slice(2));
case "0x" : return hex_to_dec(number.slice(2));
default : switch (number[0]) {
case "0" : return oct_to_dec(number.slice(1));
default : return dec_to_dec(number);
}
}
}
> | oct_to_dec("10")
< | 8
> | num_to_dec("010")
< | 8
> | 010 // :-)
< | 8
function dec_to_bin (number) {
return stringify_natural_number(number, 2);
}
> | dec_to_bin(8)
< | "1000"
Is there any faster alternative to the following expression:
Math.pow(2,Math.floor(Math.log(x)/Math.log(2)))
That is, taking the closest (smaller) integer power of 2 of a double? I have such expression in a inner loop. I suspect it could be much faster, considering one could just take the mantissa from the IEEE 754 representation of the double.
Making use of ES6's Math.clz32(n) to count leading zeros of a 32-bit integer:
// Compute nearest lower power of 2 for n in [1, 2**31-1]:
function nearestPowerOf2(n) {
return 1 << 31 - Math.clz32(n);
}
// Examples:
console.log(nearestPowerOf2(9)); // 8
console.log(nearestPowerOf2(33)); // 32
Here's another alternative, with benchmarks. While both seems to be comparable, I like being able to floor or ceil.
function blpo2(x) {
x = x | (x >> 1);
x = x | (x >> 2);
x = x | (x >> 4);
x = x | (x >> 8);
x = x | (x >> 16);
x = x | (x >> 32);
return x - (x >> 1);
}
function pow2floor(v) {
var p = 1;
while (v >>= 1) {
p <<= 1;
}
return p;
}
function pow2ceil(v) {
var p = 2;
while (v >>= 1) {
p <<= 1;
}
return p;
}
function MATHpow2(v) {
return Math.pow(2, Math.floor(Math.log(v) / Math.log(2)))
}
function nearestPowerOf2(n) {
return 1 << 31 - Math.clz32(n);
}
function runner(fn, val) {
var res;
var st = new Date().getTime()
for (var i = 0; i < 100000000; i++) {
fn(val);
}
return (new Date().getTime() - st)
}
var source = 300000;
var a = runner(pow2floor, source);
console.log("\n--- pow2floor ---");
console.log(" result: " + pow2floor(source));
console.log(" time: " + a + " ms");
var b = runner(MATHpow2, source);
console.log("\n--- MATHpow2 ---");
console.log(" result: " + MATHpow2(source));
console.log(" time: " + b + " ms");
var b = runner(nearestPowerOf2, source);
console.log("\n--- nearestPowerOf2 ---");
console.log(" result: " + nearestPowerOf2(source));
console.log(" time: " + b + " ms");
var b = runner(blpo2, source);
console.log("\n--- blpo2 ---");
console.log(" result: " + blpo2(source));
console.log(" time: " + b + " ms");
// pow2floor: 1631 ms
// MATHpow2: 13942 ms
// nearestPowerOf2: 937 ms
// blpo2 : 919 ms **WINNER**
Here is also a branchless 32 bit version which is the fastest (9x) (on cellphones even more!) as of now.
It can also be scaled to 64 or 128 bits adding 1 or two lines:
x = x | (x >> 64);
x = x | (x >> 128);
on my computer:
2097152,blpo2: 118 ms **FASTEST**
2097152,nearestPowerOf2: 973 ms
2097152,pow2floor: 2033 ms
on my phone:
2097152,blpo2: 216 ms **FASTEST**
2097152,nearestPowerOf2: 1259 ms
2097152,pow2floor: 2904 ms
function blpo2(x) {
x = x | (x >> 1);
x = x | (x >> 2);
x = x | (x >> 4);
x = x | (x >> 8);
x = x | (x >> 16);
x = x | (x >> 32);
return x - (x >> 1);
}
function pow2floor(v) {
var p = 1;
while (v >>= 1) {
p <<= 1;
}
return p;
}
function nearestPowerOf2(n) {
return 1 << 31 - Math.clz32(n);
}
function runner(fn, val) {
var res;
var st = new Date().getTime()
for (var i = 0; i < 100000000; i++) {
res = fn(val);
}
dt = new Date().getTime() - st;
return res + "," + fn.name + ": " + dt + " ms"
}
var source = 3000000;
console.log(runner(blpo2, source), "**FASTEST**")
console.log(runner(nearestPowerOf2, source))
console.log(runner(pow2floor, source))
Unfortunately, you would need an equivalent of the C function frexp. The best I've been able to find is in JSFiddle, and its code uses Math.pow.
There are a couple of alternatives you could benchmark, using real data, along with your current attempt:
Starting at 1.0, multiply repeatedly by 2.0 until it is greater than or equal to the input, then multiply by 0.5 until it is less than or equal to the input. You would need special handling for values at the ends of the double range.
Store an ascending value array of all the exact powers of two in the double range, and do a binary search.
The first one is likely to be fastest if your data is typically close to 1.0. The second one requires up to 11 conditional branches.
Without ES6...
x=Math.floor(Math.random()*500000); //for example
nearestpowerof2=2**(x.toString(2).length-1);
console.log(x,">>>",nearestpowerof2);
In other words: the result is 2 to the power of the length of the binary representation of the number subtracted by 1.
And this is another.
function nP2(n) {
return 1 << Math.log2(n);
}
console.log(nP2(345367));
console.log(nP2(34536));
console.log(nP2(3453));
console.log(nP2(345));
console.log(nP2(34));
And another way (this one is slow but it's fun to code recursive ones):
function calc(n, c) {
c = c || 0;
n = n >> 1;
return (n > 0) ? calc(n, c + 1) : 2 ** c;
}
console.log(calc(345367));
console.log(calc(34536));
console.log(calc(3453));
console.log(calc(345));
console.log(calc(34));
Oh and I forgot the one-liner:
a=3764537465
console.log(2**~~Math.log2(a))
In other words, here we raise 2 to the power of the rounded logarithm in base 2 of the number. But alas, this is 140 times slower than the winner: blpo2 https://stackoverflow.com/a/74916422/236062
I currently need to round numbers up to their nearest major number. (Not sure what the right term is here)
But see an example of what I'm trying to achieve
IE:
13 // 20
349 // 400
5645 // 6000
9892 // 10000
13988 // 20000
93456 // 100000
231516 // 300000
etc. etc.
I have implemented a way of doing this but its so painful and only handles numbers up to a million and if I want it to go higher I need to add more if statements (yeah see how i implmented it :P im not very proud, but brain is stuck)
There must be something out there already but google is not helping me very much probably due to me not knowing the correct term for the kind of rounding i want to do
<script type="text/javascript">
function intelliRound(num) {
var len=(num+'').length;
var fac=Math.pow(10,len-1);
return Math.ceil(num/fac)*fac;
}
alert(intelliRound(13));
alert(intelliRound(349));
alert(intelliRound(5645));
// ...
</script>
See http://jsfiddle.net/fCLjp/
One way;
var a = [13, // 20
349, // 400
5645, // 6000
9892, // 10000
13988, // 20000
93456, // 100000
231516 // 300000
]
for (var i in a) {
var num = a[i];
var scale = Math.pow(10, Math.floor(Math.log(num) / Math.LN10));
print([ num, Math.ceil(num / scale) * scale ])
}
13,20
349,400
5645,6000
9892,10000
13988,20000
93456,100000
231516,300000
The answer from #rabudde works well, but for those that need to handle negative numbers, here's an updated version:
function intelliRound(num) {
var len = (num + '').length;
var result = 0;
if (num < 0) {
var fac = Math.pow(10, len - 2);
result = Math.floor(num / fac) * fac;
}
else {
var fac = Math.pow(10, len - 1);
result = Math.ceil(num / fac) * fac;
}
return result;
}
alert(intelliRound(13));
alert(intelliRound(349));
alert(intelliRound(5645));
alert(intelliRound(-13));
alert(intelliRound(-349));
alert(intelliRound(-5645));
you can use Math.ceil function, as described here:
javascript - ceiling of a dollar amount
to get your numbers right you'll have to divide them by 10 (if they have 2 digits), 100 (if they have 3 digits), and so on...
The intelliRound function from the other answers works well, but break with negative numbers. Here I have extended these solutions to support decimals (e.g. 0.123, -0.987) and non-numbers:
/**
* Function that returns the floor/ceil of a number, to an appropriate magnitude
* #param {number} num - the number you want to round
*
* e.g.
* magnitudeRound(0.13) => 1
* magnitudeRound(13) => 20
* magnitudeRound(349) => 400
* magnitudeRound(9645) => 10000
* magnitudeRound(-3645) => -4000
* magnitudeRound(-149) => -200
*/
function magnitudeRound(num) {
const isValidNumber = typeof num === 'number' && !Number.isNaN(num);
const result = 0;
if (!isValidNumber || num === 0) return result;
const abs = Math.abs(num);
const sign = Math.sign(num);
if (abs > 0 && abs <= 1) return 1 * sign; // percentages on a scale -1 to 1
if (abs > 1 && abs <= 10) return 10 * sign;
const zeroes = `${Math.round(abs)}`.length - 1; // e.g 123 => 2, 4567 => 3
const exponent = 10 ** zeroes; // math floor and ceil only work on integer
const roundingDirection = sign < 0 ? 'floor' : 'ceil';
return Math[roundingDirection](num / exponent) * exponent;
}