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")
Let's say you have a function that takes both x and y, real numbers that are integers, as arguments.
What would you put inside that function, using only mathematical operators, so that no two given sequences of arguments could ever return the same value, be it any kind of value?
Example of a function that fails at doing this:
function myfunction(x,y){
return x * y;
}
// myfunction(2,6) and myfunction(3,4) will both return 12
// myfunction(2,6) and myfunction(6,2) also both return 12.
As already noted in comments, at the level of JavaScript numbers such a function can't exist, simply because assuming that we're working with integer-valued IEEE 754 binary64 floats there are more possible input pairs than possible output values.
But to the mathematical question of whether there is a simple, injective function from pairs of integers to a single integer, the answer is yes. Here's one such function that uses only addition and multiplication, so should fit the questioner's "using only mathematical operators" constraint.
First we map each of the inputs from the domain of integers to the domain of nonnegative integers. The polynomial map x ↦ 2*x*x + x will do that for us, and maps distinct values to distinct values. (Sketch of proof: if 2*x*x + x == 2*y*y + y for some integers x and y, then rearranging and factoring gives (x - y) * (2*x + 2*y + 1) == 0; the second factor can never be zero for integers x and y, so the first factor must be zero and x == y.)
Second, given a pair of nonnegative integers (a, b), we map that pair to a single (nonnegative) integer using (a, b) ↦ (a + b)*(a + b) + a. It's easy to see that this, too, is injective: given the value of (a + b)*(a + b) + a, I can recover the value of a + b by taking the integer square root, and from there recover a and b.
Here's some Python code demonstrating the above:
def encode_pair(x, y):
""" Encode a pair of integers as a single (nonnegative) integer. """
a = 2*x*x + x
b = 2*y*y + y
return (a + b)*(a + b) + a
We can easily check that there are no repetitions for small x and y: here we take all pairs (x, y) with -500 <= x < 500 and -500 <= y < 500, and find the set containing encode_pair(x, y) for each combination. If all goes well, we should end up with a set with exactly 1 million entries, one per input combination.
>>> all_outputs = {encode_pair(x, y) for x in range(-500, 500) for y in range(-500, 500)}
>>> len(all_outputs)
1000000
>>> min(all_outputs)
0
But perhaps a more convincing way to establish the injectivity is to give an explicit inverse, showing that the original (x, y) can be recovered from the output. Here's that inverse function. It makes use of Python's integer square root operation math.isqrt, which is available only for Python >= 3.8, but is easy to implement yourself if you need it.
from math import isqrt
def decode_pair(n):
""" Decode an integer produced by encode_pair. """
a_plus_b = isqrt(n)
a = n - a_plus_b*a_plus_b
b = a_plus_b - a
c = isqrt(8*a + 1)
d = isqrt(8*b + 1)
return ((2 - c%4) * c - 1) // 4, ((2 - d%4) * d - 1) // 4
Example usage:
>>> encode_pair(3, 7)
15897
>>> decode_pair(15897)
(3, 7)
Depending on what you allow as a "mathematical operator" (which isn't really a particularly well-defined term), there are tighter functions possible. Here's a variant of the above that provides not just an injection but a bijection: every integer appears as the encoding of some pair of integers. It extends the set of mathematical operators used to include subtraction, division and absolute value. (Note that all divisions appearing in encode_pair are exact integer divisions, without any remainder.)
def encode_pair(x, y):
""" Encode a pair of integers as a single integer.
This gives a bijective map Z x Z -> Z.
"""
ax = (abs(2 * x + 1) - 1) // 2 # x if x >= 0, -1-x if x < 0
sx = (ax - x) // (2 * ax + 1) # 0 if x >= 0, 1 if x < 0
ay = (abs(2 * y + 1) - 1) // 2 # y if y >= 0, -1-y if y < 0
sy = (ay - y) // (2 * ay + 1) # 0 if y >= 0, 1 if y < 0
xy = (ax + ay + 1) * (ax + ay) // 2 + ax # encode ax and ay as xy
an = 2 * xy + sx # encode xy and sx as an
n = an - (2 * an + 1) * sy # encode an and sy as n
return n
def decode_pair(n):
""" Inverse of encode_pair. """
# decode an and sy from n
an = (abs(2 * n + 1) - 1) // 2
sy = (an - n) // (2 * an + 1)
# decode xy and sx from an
sx = an % 2
xy = an // 2
# decode ax and ay from xy
ax_plus_ay = (isqrt(8 * xy + 1) - 1) // 2
ax = xy - ax_plus_ay * (ax_plus_ay + 1) // 2
ay = ax_plus_ay - ax
# recover x from ax and sx, and y from ay and sy
x = ax - (1 + 2 * ax) * sx
y = ay - (1 + 2 * ay) * sy
return x, y
And now every integer appears as the encoding of exactly one pair, so we can start with an arbitrary integer, decode it to a pair, and re-encode to recover the same integer:
>>> n = -12345
>>> decode_pair(n)
(67, -44)
>>> encode_pair(67, -44)
-12345
The encode_pair function above is deliberately quite verbose, in order to explain all the steps involved. But the code and the algebra can be simplified: here's exactly the same computation expressed more compactly.
def encode_pair_cryptic(x, y):
""" Encode a pair of integers as a single integer.
This gives a bijective map Z x Z -> Z.
"""
c = abs(2 * x + 1)
d = abs(2 * y + 1)
e = (2 * y + 1) * ((c + d)**2 * c + 2 * (c - d) * c - 4 * x - 2)
return (e - 2 * c * d) // (4 * c * d)
encode_pair_cryptic gives exactly the same results as encode_pair. I'll give one example, and leave the reader to figure out the equivalence.
>>> encode_pair(47, -53)
-9995
>>> encode_pair_cryptic(47, -53)
-9995
I'm no math wiz but found this question kinda fun so I gave it a shot. This is by no means scalable to large number since I'm using prime numbers as exponents and gets out of control really quick. But tested up to 90,000 combinations and found no duplicates.
The code below has a couple extra functions generateValues() and hasDuplicates() that is just there to run and test multiple values coming from the output of myFunction()
BigNumber.config({ EXPONENTIAL_AT: 10 })
// This function is just to generate the array of prime numbers
function getPrimeArray(num) {
const array = [];
let isPrime;
let i = 2;
while (array.length < num + 1) {
for (let j = 2; (isPrime = i === j || i % j !== 0) && j <= i / 2; j++) {}
isPrime && array.push(i);
i++;
}
return array;
}
function myFunction(a, b) {
const primes = getPrimeArray(Math.max(a, b));
// Using the prime array, primes[a]^primes[b]
return BigNumber(primes[a]).pow(primes[b]).toString();
}
function generateValues(upTo) {
const results = [];
for (let i = 1; i < upTo + 1; i++) {
for (let j = 1; j < upTo + 1; j++) {
console.log(`${i},${j}`)
results.push(myFunction(i,j));
}
}
return results.sort();
}
function hasDuplicates(arr) {
return new Set(arr).size !== arr.length;
}
const values = generateValues(50)
console.log(`Checked ${values.length} values; duplicates: ${hasDuplicates(values)}`)
<script src="https://cdnjs.cloudflare.com/ajax/libs/bignumber.js/8.0.2/bignumber.min.js"></script>
Explanation of what's going on:
Using the example of myFunction(1,3)
And the array of primes [2, 3, 5, 7]
This would take the 2nd and 4th items, 3 and 7 which would result in 3^7=2187
Using 300 as the max generated 90,000 combinations with no duplicates (However it took quite some time.) I tried using a max of 500 but the fan on my laptop sounded like a jet engine taking off so gave up on it.
If x and y are some fixed size integers (eg 8 bits) then what you want is possible if the return of f has at least as many bits as the sum of the number of bits of x an y (ie 16 in the example) and not otherwise.
In the 8 bit example f(x,y) = (x<<8)+y would do. This is because if g(z) = ((z>>8), z&255) then g(f(x,y)) = (x,y). The impossibility comes from the pigeon hole principle: if we want (in the example) to map the pairs (x,y) (of which there 2^16) 1-1 to some integer type, then we must have at least 2^16 values of this type.
function myfunction(x,y){
x = 1/x;
y = 1/y;
let yLength = ("" + y).length
for(let i = 0; i < yLength; i++){
x*=10;
}
return (x + y)
}
console.log(myfunction(2,12))
console.log(myfunction(21,2))
Based on your question and you comments, I understood the following:
You want to pass 2 real numbers into a function. The function should use mathematical operators to generate a new result.
Your question is, if there is any kind of mathematical equation/function you could use, that would ALWAYS deliver a unique result.
If that's so, then the answer is no. You can make your function as complicated as possible and get a result(c) using the two numbers (a & b).
In this case I would look for another combination which could give me the result(c) using the same equation/function. Therefore I would use the system of linear equation to solve this mathematical issue.
In general, a system with fewer equations than unknowns has infinitely many solutions, but it may have no solution. Such a system is known as an underdetermined system.
In our case we would have one equation which gives us one result and two unknowns, therefore it would have infinitely many solutions because we already have a solution, so there is no way for the system to have no solutions at all.
More about this topic.
Edit:
I just recognized that some of us understood the domain of the function in a different way. I was thinking about real numbers (R) but it seems many assumed you talk about integers (Z) only.
Well I guess
real integers
wasnt clear enough, at least for me.
So if we would use integers only, I have no idea if that is possible to always have different results. Some users suggested a topic about that here I am also interested to take a look into that too.
I was given a quiz and I had gotten the answer wrong and It's been bugging me ever since so I thought I'd ask for your thoughts
I needed to optimise the following function
function sumOfEvenNumbers(n) {
var sum = 0;
for(let i = 2; i < n;i++){
if(i % 2 == 0) sum += i;
}
return sum;
}
console.log(sumOfEvenNumbers(5));
I came up with
function sumOfEvenNumbers(n) {
var sum = 0;
while(--n >= 2) sum += n % 2 == 0? n : 0
return sum;
}
console.log(sumOfEvenNumbers(5));
What other ways were there?
It's a bit of a math question. The sum appears to be the sum of an arithmitic sequence with a common difference of 2. The sum is:
sum = N * (last + first) / 2;
where N is the number of the numbers in the sequence, last is the last number of those numbers, and first is the first.
Translated to javascript as:
function sumOfEvenNumbers(n) {
return Math.floor(n / 2) * (n - n % 2 + 2) / 2;
}
Because the number of even numbers between 2 and n is Math.floor(n / 2) and the last even number is n - n % 2 (7 would be 7 - 7 % 2 === 6 and 8 would be 8 - 8 % 2 === 8). and the first is 2.
Sum of n numbers:
var sum = (n * (n+1)) / 2;
Sum of n even numbers:
var m = Math.floor(n/2);
var sum = 2 * (m * (m+1) /2);
You can compute these sums using an arithmetic sum formula in constant time:
// Return sum of positive even numbers < n:
function sumOfEvenNumbers(n) {
n = (n - 1) >> 1;
return n * (n + 1);
}
// Example:
console.log(sumOfEvenNumbers(5));
Above computation avoids modulo and division operators which consume more CPU cycles than multiplication, addition and bit-shifting. Pay attention to the limited range of the bit-shifting operator >> though.
See e.g. http://oeis.org/A002378 for this and other formulas leading to the same result.
First thing is to eliminate the test in the loop:
function sumOfEvenNumbers(n) {
var sum = 0;
var halfN= Math.floor(n/2);
for(let i = 1; i < n/2;i++) {
sum += i;
}
return sum * 2;
}
Then we can observe that is just calculating the sum of all the integers less than a limit - and there is a formula for that (but actually formula is for less-equal a limit).
function sumOfEvenNumbers(n) {
var halfNm1= Math.floor(n/2)-1;
var sum = halfNm1 * (halfNm1+1) / 2;
return sum * 2;
}
And then eliminate the division and multiplication and the unnecessary addition and subtraction:
function sumOfEvenNumbers(n) {
var halfN= Math.floor(n/2);
return (halfN-1) * halfN;
}
Your solution computes in linear (O(N)) time.
If you use a mathematical solution, you can compute it in O(1) time:
function sum(n) {
let half = Math.ceil(n/2)
return half * (half + 1)
}
Because the question is tagged ecmascript-6 :)
const sumEven = x => [...Array(x + 1).keys()].reduce((a, b) => b % 2 === 0 ? a + b : a, 0);
// set max number
console.log(sumEven(10));
In math how do I obtain the closest number of a number that is divisible by 16?
For example I get the random number 100 and I want to turn that number (using a math function) into the closest number to 100 that is divisible by 16 (In this case its 96)
I'm trying to do this in JavaScript but if I knew the math formula for it I would easily do it in any language.
Thank you,
Regards
Generate a random integer. Multiply it by 16.
Divide by 16, round, and multiply by 16:
n = Math.round(n / 16) * 16;
function GetRandomNumberBetween(lo, hi) {
return Math.floor(lo + Math.random() * (hi - lo));
}
Number.prototype.FindClosestNumberThatIsDivisibleBy = function(n) {
return Math.round(this / n) * n; //simplify as per Guffa
/* originally:
var c = Math.ceil(n);
var f = Math.floor(n);
var m = num % n;
var r = f * n;
if (m > (n / 2))
r = c * n;
return r;
*/
};
var r = GetRandomNumberBetween(10, 100);
var c = r.FindClosestNumberThatIsDivisibleBy(16);
function closest(n) {
var r = 0, ans = 0;
r = n % 16
if r < 8 {
ans = n - r
} else {
ans = n + (16 - r)
}
return ans;
}
Here's how I understand your question. You're given a number A, and you have to find a number B that is the closest possible multiple of 16 to A.
Take the number given, "A" and divide it by 16
Round the answer from previous step to the nearest whole number
multiply the answer from previous step by 16
there's the pseudocode, hope it's what you're looking for ;-)
A general JS solution
var divisor = 16;
var lower = 0;
var upper = 100;
var randDivisible = (Math.floor(Math.random()*(upper-lower))+lower)*divisor;
alert(randDivisible);