RabbitFarm
2022-03-20
Persnickety Pernicious and Weird
The examples used here are from The Weekly Challenge problem statement and demonstrate the working solution.
Part 1
Write a script to generate the first 10 Pernicious Numbers.
Solution
use strict;
use warnings;
use Math::Primality qw/is_prime/;
sub count_bits{
my($n) = @_;
my $total_count_set_bit = 0;
while($n){
my $b = $n & 1;
$total_count_set_bit++ if $b;
$n = $n >> 1;
}
return $total_count_set_bit;
}
sub first_n_pernicious{
my($n) = @_;
my @pernicious;
my $x = 1;
do{
my $set_bits = count_bits($x);
push @pernicious, $x if is_prime($set_bits);
$x++;
}while(@pernicious < $n);
return @pernicious;
}
MAIN:{
print join(", ", first_n_pernicious(10)) . "\n";
}
Sample Run
$ perl perl/ch-1.pl
3, 5, 6, 7, 9, 10, 11, 12, 13, 14
Notes
Number Theory was one of my favorite classes as an undergraduate. This sort of challenge is fun, especially if you dive into the background of these sequences and try to learn more about them. Computing them is fairly straightforward, especially here where the two functions are largely drawn from past TWCs.
Part 2
You are given number, $n > 0. Write a script to find out if the given number is a Weird Number.
Solution
use strict;
use warnings;
use boolean;
use Data::PowerSet q/powerset/;
sub factor{
my($n) = @_;
my @factors = (1);
foreach my $j (2 .. sqrt($n)){
push @factors, $j if $n % $j == 0;
push @factors, ($n / $j) if $n % $j == 0 && $j ** 2 != $n;
}
return @factors;
}
sub is_weird{
my($x) = @_;
my @factors = factor($x);
my $sum = unpack("%32I*", pack("I*", @factors));
for my $subset (@{powerset(@factors)}){
return false if unpack("%32I*", pack("I*", @{$subset})) == $x;
}
return boolean($sum > $x);
}
MAIN:{
print is_weird(12) . "\n";
print is_weird(70) . "\n";
}
Sample Run
$ perl perl/ch-2.pl
0
1
Notes
This task kind of bothered me, not because of the complexity of the task itself; the code was overall not extremely demanding. Rather anytime when I want to make use of Data::PowerSet I get a bit anxious that there may be a far more elegant way of proceeding! After coming up blank on alternatives I just went with this, but I'll probably still have this in the back of my mind for a few more days.
References
posted at: 18:29 by: Adam Russell | path: /perl | permanent link to this entry
The Weekly Challenge 156 (Prolog Solutions)
The examples used here are from the weekly challenge problem statement and demonstrate the working solution.
Part 1
Write a script to generate the first 10 Pernicious Numbers.
Solution
pernicious(_) --> [].
pernicious(Seen) --> [X], x(Seen, X), {set_bits(X, Bits), fd_prime(Bits)}, pernicious([X|Seen]).
x(Seen, X) --> {between(1, 100, X), \+ member(X, Seen)}.
set_bits(N, X):-
set_bits(N, 0, X).
set_bits(0, X, X).
set_bits(N, X_Acc, X):-
B is N /\ 1,
X0 is X_Acc + B,
N0 is N >> 1,
set_bits(N0, X0, X), !.
Sample Run
$ gprolog --consult-file prolog/ch-1.p
| ?- length(Pernicious, 10), phrase(pernicious([]), Pernicious).
Pernicious = [3,5,6,7,9,10,11,12,13,14] ?
(115 ms) yes
| ?- phrase(pernicious([]), [3, 5, 6]).
true ?
(95 ms) yes
Notes
DCGs are great aren't they? The ability to have two modes, one to test and the other to create is a joy! The logic here is pretty straightforward and more or less follows straight fromt he definition.
Part 2
Write a script to compute the first 10 distinct Padovan Primes.
Solution
weird(_) --> [].
weird(Seen) --> [X], x(Seen, X), {
findall(F, factor(X, F), Factors), flatten([1, Factors], FlatFactors),
sum_list(FlatFactors, FactorSum),
FactorSum > X,
powerset(FlatFactors, FactorSets),
maplist(sum_list, FactorSets, FactorSetSums),
\+ member(X, FactorSetSums)
},
weird([X|Seen]).
x(Seen, X) --> {between(1, 1000, X), \+ member(X, Seen)}.
powerset(X,Y):- bagof(S, subseq(S,X), Y).
subseq([], []).
subseq([], [_|_]).
subseq([X|Xs], [X|Ys] ):- subseq(Xs, Ys).
subseq([X|Xs], [_|Ys] ):- append(_, [X|Zs], Ys), subseq(Xs, Zs).
factor(N, Factors):-
S is round(sqrt(N)),
fd_domain(X, 2, S),
R #= N rem X,
R #= 0,
Q #= N // X,
Q #\= X,
fd_labeling([Q, X]),
Factors = [Q, X].
factor(N, Factors):-
S is round(sqrt(N)),
fd_domain(X, 2, S),
R #= N rem X,
R #= 0,
Q #= N // X,
Q #= X,
fd_labeling([Q]),
Factors = [Q].
Sample Run
$ gprolog --consult-file prolog/ch-2.p
| ?- phrase(weird([]), [70]).
true ?
yes
| ?- length(Weird, 1), phrase(weird([]), Weird).
Weird = [70] ?
(4 ms) yes
Notes
This solution follows the same generate and test approach I used in the Perl Solution, as far as the testing of the powerset of divisors is concerned anyway. (I'll admit I was too lazy to write my own powerset code so I grabbed someone else's. See the references for a link to the source.)
In my ongoing attempts to improve my DCG skills I implemented this as a DCG which is a bit of overkill for this problem, but it is always nice to be able to generate the sequence as well as validate.
References
posted at: 18:26 by: Adam Russell | path: /prolog | permanent link to this entry