wesley.kueppo's blog

Pernicious.pl

I opt for a very simple approach, from the given definition we can deduce the following properties:

a) 2^n + 1 is always pernicious for all positive integer n and has 2 ones.

b) 2^n - 1 is pernicious if and only if n is prime and has n ones.

With these two properties, we can avoid the computation of the number of ones.

Prerequisite

Default base in perl is e but we need to compute in base 2.

  1. Calculate logarithm in base 2.
sub log_base2 {
	my $n = shift;
	log($n) / log(2)
}
  1. Check if $number is prime.

If there is no divisor of $number between 2 and sqrt($number), then $number is prime.

sub is_prime {
	my ($number, $prime) = (shift, 1);

	$prime = 0 if ($number == 1);
	if ($number > 3) {
		foreach my $i (2..sqrt $number) {
			if ($number % $i == 0) {
				$prime = 0;
				last;
			}
		}
	}
	return $prime;
}

Pernicious.pl

#!/usr/bin/env perl

use strict;
use warnings;

### HELPER
# Find log of $n base 2.
sub log_base2 {
	my $n = shift;
	log($n) / log(2)
}

### HELPER
# Check if $number is prime (1: success, 0: failure).
sub is_prime {
	my ($number, $prime) = (shift, 1);

	$prime = 0 if ($number == 1);
	if ($number > 3) {
		foreach my $i (2..sqrt $number) {
			if ($number % $i == 0) {
				$prime = 0;
				last;
			}
		}
	}
	return $prime;
}

### MAIN
# List the first $limit penicious numbers.
sub penicious {
	my ($v, $limit) = (3, shift);
	my @found = ();

	while (@found != $limit) {
		my ($fpower, $spower) = (log_base2($v + 1), log_base2($v - 1));
		if ($spower =~ /^\d+$/) {
			push @found, $v;
		} elsif ($fpower =~ /^\d+$/ and is_prime $fpower) {
			push @found, $v;
		} else {
			my ($ones, $val) = (0, sprintf '%b', $v);
			foreach (split '', $val) {
				$ones++ if ($_ eq '1');
			}
			push @found, $v if (is_prime $ones);
		}
		$v++;
	}
	print join ', ', @found, "\n";
}

penicious 10;

#mathematics #perl #pernicious-numbers #theweeklychallenge