DEV Community

Bob Lied
Bob Lied

Posted on

PWC 254 Task 1, Three Power

The tune you should be humming while working on this task is Blues Power

Task 1: Three Power

You are given a positive integer, $n.
Write a script to return true if the given integer
is a power of three otherwise return false.
Enter fullscreen mode Exit fullscreen mode
  • Example 1: Input: $n = 27, Output: true ( 27 = 3 ^ 3 )
  • Example 2: Input: $n = 0, Output: true ( 0 = 0 ^ 3)
  • Example 3: Input: $n = 6, Output: false

Thoughts

Quite a few early solutions seem to have interpreted this to mean "check if $n is a cube", and solve it by checking if the cube root of $n is 3. But that is not what "power of three" means. The powers of three are 30, 31, 32, 33, etc.

Example 2 seems to have led them astray. I'm going to assume that Example 2 is a mistake, both because it doesn't match the definition of powers of three and because the input is specified as a positive integer and therefore should exclude zero.

Digression on cube roots

On Facebook, this led to a discussion of finding the cube root of -8, which should be -2? The typical way of finding a cube root is to raise a number to the 1/3 power -- (-8)**(1/3). However, the ** operator (and the underlying C pow() function on which it's based) explicitly don't work for a negative base with a fractional exponent. Why? Because in general, the roots of negative numbers are complex numbers, which can't be represented with a single floating point return value.

Well. Perl has Math::Complex, which overloads the ** operator to work with complex numbers. So if we cleverly plop in use Math::Complex;, set up -8 as a complex number and let 'er rip, we will get -2, right?

$ perl -MMath::Complex -wE 'say cplx(-8,0) ** (1/3)'
1+1.73205080756888i
Enter fullscreen mode Exit fullscreen mode

Urk. What happened? There are three cube roots in the complex plane, and the one that we got is 1+sqrt(3)i. To get all the roots, we have the root function, but it has some worrisome floating point rounding error.

$ perl -MMath::Complex -wE 'my @r = root(cplx(-8,0), 3); say "@r"'
1+1.73205080756888i -2+2.44929359829471e-16i 0.999999999999999-1.73205080756888
Enter fullscreen mode Exit fullscreen mode

I'm going to take all this (complex arithmetic, numerical accuracy) as further evidence that this "easy" problem really isn't about cube roots.

On with it, then

So, the answer is to divide $n by three, and keep doing it until there's either a remainder or we reach 1. That's a one-liner if we're willing to be terse. Here it is checking for $n=9:

$ perl -wE 'my $n=shift; $n /= 3 while ( $n > 0 && $n % 3 == 0 ); say $n == 1' -- 9
Enter fullscreen mode Exit fullscreen mode

Just to make something interesting of it, let's over-engineer it by using the recently-introduced class capability, and let's generalize to numbers other than 3. We want to end up with something that can be used like:

my $n = shift; # From command line arguments
if ( Power->new(base => 3)->isPowerOf($n) ) {
    say "true";
} else {
    say "false";
}
Enter fullscreen mode Exit fullscreen mode

We'll create a class constructor that takes the base as an argument, and has a method to check a number for being a power of that base.

use v5.38;
use feature 'class'; no warnings "experimental::class";

class Power {
    field $base :param = 10;
    method isPowerOf($n) { ... }
}
Enter fullscreen mode Exit fullscreen mode

The field keyword declares member variables, and tagging it with the :param trait means that it should be initialized via the constructor. We'll give it a default value of 10 if the constructor doesn't explicitly set it. The convention for a class constructor is to provide the fields as named parameters, using the variable name. So, to create a Power object that uses 3 as the base instead of 10:

my $p3 = Power->new(base => 3);
if ( $p3->isPowerOf(27) ) { ... }
Enter fullscreen mode Exit fullscreen mode

The implementation of the isPowerOf method will be able to use $base as an in-scope variable, and it will refer to the member variable of the object. In traditional OO Perl, we would be using the more awkward $self->{base}.

    method isPowerOf($n) {
        if ( $n > 0  )    {
            $n /= $base  while ( $n % $base == 0 );
            return $n == 1;
        }
        elsif ( $n == 0 ) { return true }
        else              { return false; }
    }
Enter fullscreen mode Exit fullscreen mode

Since we're across the over-engineering boundary, I added a bit of error checking, and I made Example 2 pass, just in case we are in an alternate universe where 0 is a power of 3.

And to complete the example, the testing code for using this class looks like:

sub runTest
{
    use Test2::V0;
    use builtin qw/true false/; no warnings "experimental::builtin";

    my $p3 = Power->new(base => 3);
    is( $p3->isPowerOf(27), true,  "Example 1 with class");
    is( $p3->isPowerOf( 0), true,  "Example 2 with class");
    is( $p3->isPowerOf( 6), false, "Example 3 with class");

    done_testing;
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)