I have an lcm function that is failing for very large numbers, but not smaller numbers. For example, on an input of (6, 8), it returns 24 as the lcm. But if I do something such as (2000000000,1999999999), my while loop will infinitely loop, and not return anything.
I am using a big integer library to handle big numbers, but it doesn't seem to be working. Here is the code:
function lcm(n1, n2) {
n1 = bigInt(n1).value; //bigInt comes from the big-integer library
n2 = bigInt(n2).value;
let smaller = bigInt.min(n1, n2);
let bigger = bigInt.max(n1, n2);
let s1 = smaller;
if (smaller === 1) {
return bigger;
} else if (bigger === 1) {
return smaller;
}
while (s1 % bigger !== 0) { //once s1 % bigger is 0, we found the lcm in s1's variable
s1 = bigInt(s1).add(smaller);
console.log(s1);
}
return s1;
}
Any ideas?
Your algorithm is too slow. With n1=2000000000 and n2=1999999999 it is doing 2 billion add calls. You can use this formula:
lcm(a, b) = a * b / gcd(a, b)
To calculate gcd(a, b) (greatest common divisor) you need to implement division-based version of Euclidian algorithm. Recursive implementation:
function gcd(a, b) {
return b == 0 ? a : gcd(b, a % b);
}
Other implementations (if you want to avoid recursion) can be found here: JS how to find the greatest common divisor
If you are using this package, then you are in luck - it has a built in lcm method for you: link to the docs
If you want to see the implementation of it, check out the source over at the Github repository.
Implementation of the method inside the above library: Source
Hope this helps you out :)
Related
Problem statement: I'm trying to get string > binary without using the inbuilt method in javascript.
This is a piece of program where a string input (like "ABC") is accepted, then it is translated to an array of equivalent code value ([65,66,67]).
Function binary() will change a number to binary. But I'm unable to join them together to loop through all the contents. Please help. (I'm a noob, please forgive my bad code and bad explanation)
var temp3 = [65,66,67];
var temp2 = [];
var r;
for(i=0;i<temp3.length;i++) {
var r = temp3[i];
temp2.push(binary(r));
}
function binary(r) {
if (r === 0) return;
temp2.unshift(r % 2);
binary(Math.floor(r / 2));
return temp2;
}
console.log(temp2);
I think this is a cleaner version of this function. It should work for any non-negative integers, and would be easy enough to extend to the negatives. If we have a single binary digit (0 or 1) and hence are less than 2, we just return the number converted to a string. Otherwise we call recursively on the floor of half the number (as yours does) and append the final digit.
const binary = (n) =>
n < 2
? String (n)
: binary (Math.floor (n / 2)) + (n % 2)
console.log (binary(22)) //=> '10110'
console.log ([65, 66, 67] .map (binary)) //=> ['1000001', '1000010', '1000011']
In your function you have this code
var r = temp3[i];
I don't see any temp3 variable anywhere in your code above so I'd imagine that could be causing some issues.
I am developing a JavaScript application and I needed a recursive algorithm for the longest common subsequence, so I went here and tried this one out.
It goes like this:
function lcs(a, b) {
var aSub = a.substr(0, a.length - 1);
var bSub = b.substr(0, b.length - 1);
if (a.length === 0 || b.length === 0) {
return '';
} else if (a.charAt(a.length - 1) === b.charAt(b.length - 1)) {
return lcs(aSub, bSub) + a.charAt(a.length - 1);
} else {
var x = lcs(a, bSub);
var y = lcs(aSub, b);
return (x.length > y.length) ? x : y;
}
}
It worked fine with the few test cases i tried until now, but I found that it loops on the following test case:
a: This entity works ok
b: This didn't work ok but should after
It also loops with:
a: This entity works ok
b: This didn't work as well
which at some point should get in the middle branch.
I have noticed that it is a translation of a Java version (here) of the same algorithm. It goes like this:
public static String lcs(String a, String b){
int aLen = a.length();
int bLen = b.length();
if(aLen == 0 || bLen == 0){
return "";
}else if(a.charAt(aLen-1) == b.charAt(bLen-1)){
return lcs(a.substring(0,aLen-1),b.substring(0,bLen-1))
+ a.charAt(aLen-1);
}else{
String x = lcs(a, b.substring(0,bLen-1));
String y = lcs(a.substring(0,aLen-1), b);
return (x.length() > y.length()) ? x : y;
}
}
I supposed that the JavaScript translation was wrong assuming that String.substr() and String.substring() were the same (which they aren't).
To be sure that it wasn't the case, I tried the Java one on the same test case here.
Guess what? Also the java version does not end.
I am struggling to debug it, as it is recursive.
Anyone has any idea on what is going wrong with it?
As others have pointed out in the comments, the program itself is correct. The issue you are experiencing is due that, in this implementation, the code has an exponential time complexity, and therefore takes A LONG time to run with your example input. If you let it run for a LONG time, it will return the correct result.
As others have also pointed out in the comments, LCS between two Strings is solvable with a lower time complexity using dynamic programming, which will solve it much quicker. Refer to the internet for more help (wikipedia ) or, better, try to solve it yourself thinking about the fact that there are, for each String of length n, exactly N^2 substrings. You can trivially solve it in N^2*M^2 (n m are the lengths of the two strings) by just checking if any substring of a is present in b. Ask yourself if you can do better for exercise? If yes how, if no, why.
When I try to do 8067 % 80.67 I get 80.66999999999983, instead of 0 beacuse of known floating point javascript behaviour.
So I went and made a function for this, to avoid floating point javascript errors.
function math(a, b) {
var left = Math.abs(a),
times = 1,
abs = a >= 0 ? 1 : -1;
while (Math.abs(a) >= b * times) {
left -= b;
times++;
}
return (a - (b * (times - 1))) * abs;
}
http://jsfiddle.net/s5w3C/
So my question is: is this usefull, ie a good tool to use instead of %? is there cases where this will also give falsy results like the modulus % oprator.
I am looking for a tools to calculate % consistently.
I didn't really inspect the algorithm for correctness, but if you care about efficiency, this is a bad idea. Basically, the larger the input, the slower your code will execute.
I think any fix will only work to a certain level of accuracy and for certain sized numbers. Perhaps something like the following will be sufficient:
function nearlyMod(a, b) {
var precision = ('' + b).split('.').length;
var estimate = (a % b).toFixed(precision);
return estimate == b ? 0 : +estimate;
}
console.log(nearlyMod(8067, 80.66)); // 1
console.log(nearlyMod(8067, 80.67)); // 0
console.log(nearlyMod(8067, 80.68)); // 79.68
It tests if the result is an even divisor within the precision of the original number. If so, it returns 0, otherwise it returns a number to the same precision (which may or may not be what you want).
The result is always a number (the value returned from toFixed is a string, hence +estimate).
A better name might be "roundedMod" or similar.
I want to know whether it is possible?
Let Suppose:
var a = 2592;
var b = 2584;
if(a nearly equal to b) {
// do something
}
Like so.
var diff = Math.abs( a - b );
if( diff > 50 ) {
console.log('diff greater than 50');
}
That would compare if the absolute difference is greater than 50 using Math.abs and simple comparison.
Here's the old school way to do it...
approxeq = function(v1, v2, epsilon) {
if (epsilon == null) {
epsilon = 0.001;
}
return Math.abs(v1 - v2) < epsilon;
};
so,
approxeq(5,5.000001)
is true, while
approxeq(5,5.1)
is false.
You can adjust pass in epsilons explicitly to suit your needs. One part in a thousand usually covers my javascript roundoff issues.
var ratio = 0;
if ( a > b) {
ratio = b / a;
}
else {
ratio = a / b;
}
if (ratio > 0.90) {
//do something
}
One line Es6 way version of The Software Barbarian:
const approxeq = (v1, v2, epsilon = 0.001) => Math.abs(v1 - v2) <= epsilon;
console.log(approxeq(3.33333, 3.33322)); // true
console.log(approxeq(2.3, 2.33322)); // false
console.log(approxeq(3, 4, 1)); // true
I changed it to include the number in the margin. So with an epsilon margin of 1 approxeq between 1 and 2 is true
Floating point comparison gets complicated in a hurry. It's not as simple as diff less than epsilon in a lot of cases.
Here's an article about the subject, though not javascript specific.
https://floating-point-gui.de/errors/comparison/
TLDR:
When one of the numbers being compared is very close to zero, subtracting the smaller from the larger can lose digits of precision, making the diff appear smaller than it is (or zero).
Very small numbers with different signs work in a weird way.
Dividing by zero will cause problems.
In the article is a function (java) which solves better for these cases:
public static boolean nearlyEqual(float a, float b, float epsilon) {
final float absA = Math.abs(a);
final float absB = Math.abs(b);
final float diff = Math.abs(a - b);
if (a == b) { // shortcut, handles infinities
return true;
} else if (a == 0 || b == 0 || (absA + absB < Float.MIN_NORMAL)) {
// a or b is zero or both are extremely close to it
// relative error is less meaningful here
return diff < (epsilon * Float.MIN_NORMAL);
} else { // use relative error
return diff / Math.min((absA + absB), Float.MAX_VALUE) < epsilon;
}
}
Before you complain: Yes, that's Java, so you'd have to rewrite it in Javascript. It's just to illustrate the algorithm and it's just copied from the article.
I'm still looking for a thorough solution to this problem, ideally with an NPM package so I don't have to figure this out again every time I need it.
Edit: I've found a package which implements the solution from the article linked above (which has the same link in their readme).
https://www.npmjs.com/package/#intocode-io/nearly-equal
This will be a less error-prone solution than others shown in other answers. There are several npm packages which implement the naive solutions which have error cases near zero as described above. Make sure you look at the source before you use them.
I'm trying to resolve a small programming challenge, which is calculating the nth number of the Golomb's sequence (see this for more help). I've written a simple solution, but it may have any problem, because the number at 2500000 position is 10813 but my program gives me 10814.
var golomb = (function(){
var cache = [null, 1];
const o = 0.5 * (1 + Math.sqrt(5)); // Golden ratio
return function(n){
return cache[n] || (function(){
return Math.round(Math.pow(o, 2-o) * Math.pow(n, o-1));
})();
};
})();
var num = golomb(process.argv[2]);
console.log(num);
Maybe, the golden ratio needs more lenght than JavaScript gives. Someone can help? Thanks.
For what it's worth, here is a function based on the recurrence relation, with a cache, that gives the correct answer pretty quickly
var golomb = (function() {
var cache = [null, 1];
return function(n) {
var i;
for (i=cache.length;i<n;i++) cache[i]=golomb(i);
return cache[n] || (cache[n]=1+golomb(n-golomb(golomb(n-1))));
}
})();
check it up on jsFiddle
Sgmonda, the formula you got from Wolfram Alpha isn't an exact solution. I've actually complained to them, since I like the Golomb sequence. The recurrence relation is exact but slow, even if you cache it. Heh, that's why the programming challenge is a challenge.
From the wikipedia article:
Colin Mallows has given an explicit recurrence relation:
a(1) = 1;
a(n + 1) = 1 + a(n + 1 − a(a(n)))
You need to implement your solution in this iterative method that uses integers.
A quick attempt at trying to implement that gives:
function golomb(n) {
if(n == 1) return 1;
else return 1 + golomb(n − golomb(golomb(n-1)));
}