Peter’s blog ✴ Week 133 ✴ 4 October 2021

THE WEEKLY CHALLENGE
Root and Smith

The Perl Camel

Task 1

Integer square root

You are given a positive integer $N. Write a script to calculate the integer square root of the given number. Please avoid using built-in functions. Find out more about it here.

Examples


Input: $N = 10
Output: 3

Input: $N = 27
Output: 5

Input: $N = 85
Output: 9

Input: $N = 101
Output: 10

Analysis

The obvious answer would be int(sqrt($N)) were we not banned from using built-in functions.

So my solution does a binary chop. I start with the range 1 .. $N and find the midpoint, rounded to an integer, $try.

If $try ** 2 is greater than $N then I modify my range to be 1 .. $try, or else modify it to be $try .. $N, and repeat that process until the lower limit is just 1 less than the upper, at which point the lower limit is the answer.

This completes in milliseconds even for 10-digit $N.

I am sorry that the 'Try it' feature is currently working very slowly or not at all owing to some issue with my web hosting provider.

Try it 

Try running the script with any input:



example: 25122021

Script


#!/usr/bin/perl

# Peter Campbell Smith - 2021-10-05
# PWC 133 task 1

use v5.20;
use strict;
use warnings;

for (123, 3000, 44444, 556677, 87654321, 909090909) {
    integer_square_root($_);
}

sub integer_square_root {
    
    my ($low, $high, $odd, $try, $n);
    $n = shift;

    $low = 1;    # answer can't be less than this
    $high = $n;  # or more than this

    while (1) {
        
        # get a trial answer which is the average of $low and $high (rounded up)
        $odd = ($low + $high) & 1 ? 1 : 0;   # because we aren't allowed to use int()
        $try = ($low + $high + $odd) / 2;
        
        # if the trial's square is larger than n, set the new $high to the trial value
        if ($try ** 2 > $n) {
            $high = $try;
            
        # or if it is less than or equal, set the new $low to the trial value
        } else {
            $low = $try;
        }
        
        # and finish when $low is just 1 less than $high
        last if $high == $low + 1;
    }

    say qq[\nInput:  \$N = $n];
    say qq[Output: $low];
    printf(qq[   %s ** 2 = %d, %s ** 2 = %d\n], $low, $low ** 2, $low + 1, ($low + 1) ** 2);
}

16 lines of code

Output from script


Input:  $N = 123
Output: 11
   11 ** 2 = 121, 12 ** 2 = 144

Input:  $N = 3000
Output: 54
   54 ** 2 = 2916, 55 ** 2 = 3025

Input:  $N = 44444
Output: 210
   210 ** 2 = 44100, 211 ** 2 = 44521

Input:  $N = 556677
Output: 746
   746 ** 2 = 556516, 747 ** 2 = 558009

Input:  $N = 87654321
Output: 9362
   9362 ** 2 = 87647044, 9363 ** 2 = 87665769

Input:  $N = 909090909
Output: 30151
   30151 ** 2 = 909082801, 30152 ** 2 = 909143104

 

Any content of this website which has been created by Peter Campbell Smith is in the public domain