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")
Related
I had to find all the factors of positive number that evenly divide into a number and then return a p^th element of the list, sorted ascending.
If there is no p^th return 0.
I tested almost all the answers i could solve and found online:
Example:
function pthFactor(n, k) {
let arr = [];
for (let i = 1; i <= n; i++) {
if (n % i === 0) {
arr.push(i);
}
if (arr.length === k) {
return arr[arr.length - 1];
}
}
if (arr.length !== k) {
return 1;
}
};
or
var kthFactor = function(n, k) {
let factors = [1]
for(let i=2;i<=Math.floor(n/2);i++){
if(n%i == 0) factors.push(i)
}
factors.push(n)
return factors.length < k?-1:factors[k-1]
};
buts its failing 10 sec time limit.
What i am doing wrong ?
By the way i also tried Math.sqrt etc in order not to loop n times. Didn't work as well.
Do i need to know more than for loop ? Like dynamic programming etc to solve this ?
I couldn't find this challenge on HackerRank, but the 1492. The kth Factor of n challenge on LeetCode seems to be the same as you describe:
Given two positive integers n and k.
A factor of an integer n is defined as an integer i where n % i == 0.
Consider a list of all factors of n sorted in ascending order, return the kth factor in this list or return -1 if n has less than k factors.
It is strange that you used the name pthFactor in your first code block, while the name of the relevant argument is k and not p. That is quite confusing.
I also tried Math.sqrt etc in order not to loop n times
You cannot only consider factors up to the square root of 𝑛. For instance, 6 is a factor of 12, and even 12 is a factor of 12.
However, about half of all factors are in the range up to the square root. And those factors that are greater, are equal to the quotients found by performing the divisions with those smaller factors. So in the end, you can stop at the square root, provided that you collect all those quotients, and pick the right one from those if you didn't arrive at the 𝑘th factor yet.
So here is how that idea could be implemented:
var kthFactor = function(n, k) {
let bigFactors = [];
let quotient = n + 1;
for (let factor = 1; factor < quotient; factor++) {
if (n % factor === 0) {
quotient = n / factor;
k--;
if (k <= 0) return factor;
if (factor >= quotient) break;
bigFactors.push(quotient);
}
}
return bigFactors[bigFactors.length - k] ?? -1;
};
// A few test cases:
console.log(kthFactor(12, 3)); // 3
console.log(kthFactor(7, 2)); // 7
console.log(kthFactor(16, 5)); // 16
console.log(kthFactor(27, 5)); // -1
console.log(kthFactor(927, 1)); // 1
This code uses the same algorithm as in trincot's answer, but I think it's expressed more simply with recursion:
const kthFactor = (n, k, f = 1, r = []) =>
f * f > n
? r [k - 1] ?? -1
: n % f == 0
? k == 1 ? f : kthFactor (n, k - 1, f + 1, f * f == n ? r : [n / f, ...r])
: kthFactor (n, k, f + 1, r)
console .log (kthFactor (12, 3)); //=> 3
console .log (kthFactor (7, 2)); //=> 7
console .log (kthFactor (16, 5)); //=> 16
console .log (kthFactor (27, 5)); //=> -1
console .log (kthFactor (927, 1)); //=> 1
Here we track the factor we're testing (f) and the remaining numbers (r) to be tested, decreasing k whenever we find a new factor and adding the quotient n / f to our remaining numbers, increasing f on every step, stopping when we've gotten too big (f * f > n) and then finding the index in the ordered list of remaining numbers, returning the factor when k is 1, and simply recurring with an incremented factor otherwise.
The only tricky bit is the handling of perfect squares, where the square root should only be counted once, so we don't prepend it to the remaining numbers if f * f == n.
Because of the recursion, this is only suitable for a k so large. While we could make this tail recursive in a simple manner, it doesn't matter much as of this writing, since tail call optimization has still not happened in most engines.
I solved the following leetCode problem with some code :
You have d dice, and each die has f faces numbered 1, 2, ..., f.
Return the number of possible ways modulo 10^9 + 7 to roll the dice so the sum of the face up numbers equals t.
I made two versions of the solution code, one in node.js using mathjs, and one in python using the math module .
In node.js
const { combinations: comb, bignumber: Big } = require("mathjs");
function dice(d, f, t) {
if (t > d * f || t < d) return 0;
var result = Big(0);
var i = 0;
var sign = 1;
var n = t - 1;
var k = t - d;
while (k >= 0 && i <= d) {
result = result.add(
comb(Big(d), Big(i))
.times(comb(Big(n), Big(k)))
.times(sign)
);
i++;
n -= f;
k -= f;
sign *= -1;
}
return result;
}
console.log(
dice(30, 30, 500).mod(
Big(10)
.pow(9)
.add(7)
)
);
In python :
import math
def dice(d, f, t):
if t > d * f or t < d:
return 0
result = 0
i = 0
sign = 1
n = t - 1
k = t - d
while k >= 0 and i <= d:
result += math.comb(d, i) * math.comb(n, k) * sign
i += 1
n -= f
k -= f
sign *= -1
return result
print(dice(30, 30, 500) % (math.pow(10, 9) + 7))
Now when i run the code with these parameters : d=30 f=30 t=500 (the last line of each version of the code), i expect the result to be 222616187 .
In the node.js version , that's exactly what i get .
But in the python version , i'm getting 811448245.0 i can't figure out why is that happening .
So why is there a difference in the results ?
The math module usesfloat, not arbitrary precision int.
math - Mathematical functions
[...]
The following functions are provided by this module. Except when
explicitly noted otherwise, all return values are floats.
Since math.pow returns a float, the leading argument to % is converted to a float as well.
The result of dice(30, 30, 500) is too large to be accurately represented as a float. Its float representation is off by -14999044413600247749080617.
The ** operator and its function version operator.pow do not force float conversion and provide an integer if all parameters are integers.
>>> print(dice(30, 30, 500) % (10 ** 9 + 7))
222616187
Solved, in a weird way. It turns out, math.pow returns a float instead of int and somehow bugged. I think int % float has a different cast operation in it and treated differently by the compiler. It can be investigated further. If you cast it to int, that would be your answer.
import math
def dice(d, f, t):
if t > d * f or t < d:
return 0
result = 0
i = 0
sign = 1
n = t - 1
k = t - d
while k >= 0 and i <= d:
result += math.comb(d, i) * math.comb(n, k) * sign
i += 1
n -= f
k -= f
sign *= -1
return result
print(dice(30, 30, 500) % int((math.pow(10, 9) + 7)))
A number is gapful if it is at least 3 digits long and is divisible by the number formed by stringing the first and last numbers together.
The smallest number that fits this description is 100. First digit is
1, last digit is 0, forming 10, which is a factor of 100. Therefore,
100 is gapful.
Create a function that takes a number n and returns the closest gapful
number (including itself). If there are 2 gapful numbers that are
equidistant to n, return the lower one.
Examples gapful(25) ➞ 100
gapful(100) ➞ 100
gapful(103) ➞ 105
so to solve this i wrote the code that loops from the given number to greater than that and find out if it is or not by
function getFrequency(array){
var i=array
while(i>=array){
let a=i.toString().split('')
let b=a[0]+a[a.length-1]
b= +b
if(i%b==0) return i
i++
}
}
console.log(getFrequency(103))
Thats fine but what if the gapful number is less than the number passed in the function ?
like if i pass 4780 the answer is 4773 so in my logic how do i check simultaneoulsy smaller and greater than the number passed ?
I am only looping for the numbers greater than the number provided in function
You can alternate between subtracting and adding. Start at 0, then check -1, then check +1, then check -2, then check +2, etc:
const gapful = (input) => {
let diff = 0; // difference from input; starts at 0, 1, 1, 2, 2, ...
let mult = 1; // always 1 or -1
while (true) {
const thisNum = input + (diff * mult);
const thisStr = String(thisNum);
const possibleFactor = thisStr[0] + thisStr[thisStr.length - 1];
if (thisNum % possibleFactor === 0) {
return thisNum;
}
mult *= -1;
if (mult === 1) {
diff++;
}
}
};
console.log(
gapful(100),
gapful(101),
gapful(102),
gapful(103),
gapful(104),
gapful(105),
gapful(4780),
);
You could take separate functions, one for a check if a number is a gapful number and another to get left and right values and for selecting the closest number.
function isGapful(n) {
if (n < 100) return false;
var temp = Array.from(n.toString());
return n % (temp[0] + temp[temp.length - 1]) === 0;
}
function getClosestGapful(n) {
var left = n,
right = n;
while (!isGapful(right)) right++;
if (n < 100) return right;
while (!isGapful(left)) left--;
return n - left <= right - n
? left
: right;
}
console.log(getClosestGapful(25)); // 100
console.log(getClosestGapful(100)); // 100
console.log(getClosestGapful(103)); // 105
console.log(getClosestGapful(4780)); // 4773
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));
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;
}