4

I'm trying to learn algorithms and coding stuff by scratch. I wrote a function that will find square roots of square numbers only, but I need to know how to improve its performance and possibly return square roots of non square numbers

function squareroot(number) {
    var number;
    for (var i = number; i >= 1; i--) {
        if (i * i === number) {
            number = i;
            break;
       }
   }
   return number;
}

 alert(squareroot(64))

Will return 8

Most importantly I need to know how to improve this performance. I don't really care about its limited functionality yet

user5680735
  • 693
  • 1
  • 7
  • 21
  • What happens for `squareroot(65)`? Hint: nothing good. – Bergi Mar 07 '16 at 22:57
  • ` I need to know how to improve its performance` what makes you think it doesn't have "good" performance? What are your benchmarks? People talk in general terms of performance but that's all it is general terms. What are you trying to improve? – scrappedcola Mar 07 '16 at 22:57
  • 4
    `var squareroot = Math.sqrt;` there I did it for you – Matti Virkkunen Mar 07 '16 at 22:58
  • It seems I'm iterating over every possible number and that might be unnecessary – user5680735 Mar 07 '16 at 22:58
  • 1
    There are [pretty many quite sophisticated algorithms to do this](https://en.wikipedia.org/wiki/Methods_of_computing_square_roots). You won't benefit from most of these as a learner, though. – Bergi Mar 07 '16 at 22:59
  • https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sqrt – baao Mar 07 '16 at 22:59
  • Do you really understand how to take a square root? Have you looked into current implementations? There are really good ways to look for how to do this rather than just blindly jumping into the problem. – scrappedcola Mar 07 '16 at 22:59
  • to learn about pow/sqrt/... see [Power by squaring for negative exponents](http://stackoverflow.com/a/30962495/2521214) and for realy nasty sqrt implementation see mine [integer sqrt by bin-search without multplication](http://stackoverflow.com/a/34657972/2521214) for floating point there are many magic algos out there like fron Quake etc .... exploiting the floating point number format in binary – Spektre Mar 08 '16 at 09:17

8 Answers8

6

Here is a small improvement I can suggest. First - start iterating from 0. Second - exit loop when the square of root candidate exceeds the number.

function squareroot(number) {
    for (var i = 0; i * i <= number; i++) {
        if (i * i === number)
            return i;
   }
   return number; // don't know if you should have this line in case nothing found
}

This algo will work in O(√number) time comparing to initial O(n) which is indeed performance improvement that you asked.

Edit #1

Just even more efficient solution would be to binary search the answer as @Spektre suggested. It is known that x2 is increasing function.

function squareroot(number) {
    var lo = 0, hi = number;
    while(lo <= hi) {
         var mid = Math.floor((lo + hi) / 2);
         if(mid * mid > number) hi = mid - 1;
         else lo = mid + 1;
    }
    return hi;
}

This algo has O(log(number)) running time complexity.

Ivan Gritsenko
  • 3,910
  • 2
  • 17
  • 30
  • @user5680735 this runs in `O(n)` if you change the for a little to scan the bits from MSB to LSB you will get binary search which runs in `O(log(n))` instead. the code will change just slightly see my linked QA's I commented your Question with – Spektre Mar 08 '16 at 09:24
  • @Spektre, where did you found a `sqrt` function usage? I've changed O(sqrt(number)) notation not to confuse you. – Ivan Gritsenko Mar 08 '16 at 09:34
  • Sorry my bad... You gave it right. I still did not have my tea in the morning ... I saw it in `O(sqrt(number))` miss the `O` :) – Spektre Mar 08 '16 at 10:24
  • @LeoDutra you are right, 2 is not a square root of 5. But OP was interested in finding square roots of square numbers. 5 is not a square number. – Ivan Gritsenko Oct 21 '19 at 14:13
3

The stuff that you try to do is called numerical methods. The most rudimentary/easy numerical method for equation solving (yes, you solve an equation x^2 = a here) is a Newtons method.

All you do is iterate this equation:

enter image description here

In your case f(x) = x^2 - a and therefore f'(x) = 2x.

This will allow you to find a square root of any number with any precision. It is not hard to add a step which approximate the solution to an integer and verifies whether sol^2 == a

Salvador Dali
  • 182,715
  • 129
  • 638
  • 708
  • Thanks for your answer. But what is `a`? I found in preudocode on Wikipedia Newton's method page that `f = @(x) x^2 - 2 %The function whose root we are trying to find` and can't understand why `... - 2` here – Mikhail May 13 '18 at 12:38
  • @Mikhail `a` is the number which square root you are trying to find. So if you want to find a sqrt(15), then `a=15` – Salvador Dali May 13 '18 at 20:27
2
function squareRoot(n){
    var avg=(a,b)=>(a+b)/2,c=5,b;
    for(let i=0;i<20;i++){
        b=n/c;
        c=avg(b,c);
    }
    return c;
}

This will return the square root by repeatedly finding the average.

var result1 = squareRoot(25) //5
var result2 = squareRoot(100) //10
var result3 = squareRoot(15) //3.872983346207417

JSFiddle: https://jsfiddle.net/L5bytmoz/12/

1

Here is the solution using newton's iterative method -

/**
 * @param {number} x
 * @return {number}
 */
// newstons method
var mySqrt = function(x) {
    if(x==0 || x == 1) return x;

    let ans, absX = Math.abs(x);
    let tolerance = 0.00001;
    while(true){
        ans = (x+absX/x)/2;
        if(Math.abs(x-ans) < tolerance) break;
        x = ans;
    }
    return ans;
};
Musa
  • 3,721
  • 4
  • 17
  • 24
0

Separates Newton's method from the function to approximate. Can be used to find other roots.

function newton(f, fPrime, tolerance) {
  var x, first;

  return function iterate(n) {
    if (!first) { x = n; first = 1; }

    var fn = f(x);

    var deltaX = fn(n) / fPrime(n);
    if (deltaX > tolerance) {
      return iterate(n - deltaX)
    }

    first = 0;
    return n;
  }
}


function f(n) { 
  return  function(x) { 
    if(n < 0) throw n + ' is outside the domain of sqrt()';
    return x*x - n;
  };
}

function fPrime(x) {
  return 2*x;
}


var sqrt = newton(f, fPrime, .00000001)
console.log(sqrt(2))
console.log(sqrt(9))
console.log(sqrt(64))
0

Binary search will work best.

let number = 29;
let res = 0;

console.log((square_root_binary(number)));
function square_root_binary(number){

    if (number == 0 || number == 1)  
       return number;

    let start = 0;
    let end = number;


    while(start <= end){

        let mid = ( start + end ) / 2;

        mid = Math.floor(mid);

        if(mid * mid == number){
            return mid;
        }

        if(mid * mid < number){
            start = mid + 1;
            res = mid;
        }
        else{
            end = mid - 1;
        }
    }

    return res;
}
Divyanshu Rawat
  • 2,632
  • 22
  • 39
0

I see this solution on Github which is the much better and easiest approach to take a square root of a number without using any external library

function TakingPerfectSquare(Num) {
   for (var i = 0; i <= Num; i++) {
      var element = i;
      if ((element == element) && (element*element == Num)) {
         return true;
      }
   }
   return false;
}
console.log(TakingPerfectSquare(25));
Jason Smith
  • 443
  • 3
  • 10
0

If you analyze all natural numbers with their squares you might spot a pattern...

Numbers   Squares   Additives
   1         1          3
   2         4          5
   3         9          7
   4        16          9
   5        25         11
   6        36         13
   7        49         15

Look at the first row in the squares column (i.e 1) and add it with the first row in the additives column (ie. 3). You will get four which is in the second row of the squares column.

If you keep repeating this you'll see that this applies to all squares of natural numbers. Now if you look at the additives column, all the numbers below are actually odd.

To find the square root of a perfect square you should keep on subtracting it with consecutive odd numbers (starting from one) until it is zero. The number of times it could be subtracted is the square root of that number.

This is my solution in typescript...

function findSquareRoot(number: number): number {
  for (let i = 1, count = 0; true; number -= i, i += 2, count++) {
    if (number <= 0) {
      return number === 0 ? count : -1; // -1 if number is not a perfect square
    }
  }
}

Hopefully this has better time complexity :)