One-to-One and Nybbles: (Perl) Weekly Challenge #119
I saw but didn’t notice at first that now, instead of Perl Weekly Challenge, it’s The Weekly Challenge, and to respect that change, I coded in both Perl and Javascript today.
TASK #1 › Swap Nibbles
Submitted by: Mohammad S Anwar
You are given a positive integer$N
.Write a script to swap the two nibbles of the binary representation of the given number and print the decimal number of the new binary representation.
A nibble is a four-bit aggregation, or half an octet.
To keep the task simple, we only allow integer less than or equal to 255.
I always knew of it as a nybble, but sure.
We’re given the example of 101
, and convert it to binary (01100101
), split it up into nibbles ([0110], [0101]
), reverse them ([0101], [0110]
), join them into one binary number (01010110
), and turn that into a decimal number (86
).
I used substr
to do that, but @nibbles = $n =~ m{\d{4}}g
is the way do it with regular expressions in Perl. I’m sure there’s a similar way in Javascript. There’s differing ways to go from decimal to binary — Perl => sprintf('%08b'),$n
, JS => n.toString(2)
— and back — Perl => oct('0b'.$b)
, JS => parseInt(b,2)
— but these are very similar and very short solutions.
Show Me The (Perl) Code
#!/usr/bin/env perl
use feature qw{say state signatures};
use strict;
use warnings;
use utf8;
no warnings qw{ experimental };
for my $n ( 0 .. 20 ) {
say join "\t", '', $n, flopped($n);
}
say join "\t", '', 86, flopped(86);
say join "\t", '', 101, flopped(101);
say join "\t", '', 18, flopped(18);
say join "\t", '', 33, flopped(33);
sub flopped ($n) {
my $b = sprintf '%08b', $n;
my $c = join '', substr( $b, 4, 4 ), substr( $b, 0, 4 );
my $r = oct( '0b' . $c );
return $r;
}
0 0
1 16
2 32
3 48
4 64
5 80
6 96
7 112
8 128
9 144
10 160
11 176
12 192
13 208
14 224
15 240
16 1
17 17
18 33
19 49
20 65
86 101
101 86
18 33
33 18
Show Me The (JavaScript) Code
"use strict";
for (let i in Array(21).fill("")) {
let v = flopped(i);
console.log(["", i, v].join("\t"));
}
console.log(["", 86, flopped(86)].join("\t"));
console.log(["", 101, flopped(101)].join("\t"));
console.log(["", 18, flopped(18)].join("\t"));
console.log(["", 33, flopped(33)].join("\t"));
function flopped(n) {
let b = parseInt(n).toString(2);
while (b.length < 8) {
b = "0" + b;
}
let front = b.substring(0, 4);
let back = b.substring(4);
let r = back + front;
let x = parseInt(r, 2);
return x;
}
0 0
1 16
2 32
3 48
4 64
5 80
6 96
7 112
8 128
9 144
10 160
11 176
12 192
13 208
14 224
15 240
16 1
17 17
18 33
19 49
20 65
86 101
101 86
18 33
33 18
TASK #2 › Sequence without 1-on-1
Submitted by: Cheok-Yin Fung
Write a script to generate sequence starting at 1. Consider the increasing sequence of integers which contain only 1’s, 2’s and 3’s, and do not have any doublets of 1’s like below. Please accept a positive integer $N and print the $Nth term in the generated sequence.1, 2, 3, 12, 13, 21, 22, 23, 31, 32, 33, 121, 122, 123, 131, …
It took me a little bit to think through this, but my steps are as follows. We’re primarily thinking of numbers that only contain 1, 2 and 3, so, after the binary (base 2) move in the previous challenge, I first thought about converting to base 3 and adding, but no, it’s better to go to base 4 and filter.
Because we’re filtering, if you start with an array of size n
and remove all the /11/
and /0/
entries, you end up with an array that’s smaller than n
, so I start with an array of 2 * n
and go larger as needed.
I think the biggest conceptual difference is that get_sequence
in Perl takes 0..n
, while my JS get_sequence
takes n
and builds the array itself. I mean, beyond Memoize, which I don’t think is necessary but I’m sure speeds it up a little.
Show Me The (Perl) Code
#!/usr/bin/env perl
use strict;
use warnings;
use feature qw{ postderef say signatures state };
no warnings qw{ experimental };
use Memoize;
memoize('first_pass');
my @list = map { int } @ARGV;
@list = ( 2, 5, 10, 60, 200 ) unless scalar @list;
for my $n (@list) {
say join "\t", '', $n, solve_sequence($n);
}
# here we get an array such that index $n is in the array
# using increasingly aggressive methods, then
sub solve_sequence( $n ) {
my $j = $n * 2;
my @sequence = get_sequence( 1 .. $j );
while ( !$sequence[$n] ) {
$j = $j * 2;
@sequence = get_sequence( 1 .. $j );
}
return $sequence[$n];
}
# the next things we want to do are to remove the blocked numbers
# which contain either 0 or 11, and then add another entry to the
# start of the array so that 1 aligns with 1.
sub get_sequence( @arr ) {
my @seq =
grep { !/11/ }
grep { !/0/ }
map { first_pass($_) } @arr;
unshift @seq, '';
return @seq;
}
# the numbers will contain only the digits 1, 2 and 3, so to limit
# the amount of numbers we have to come up with, I first make everything
# base 4. This function is memoizable and so I memoized it.
sub first_pass ( $n ) {
return $n if $n == 0;
my @output;
while ($n) {
my $i = $n % 4;
$n = int $n / 4;
push @output, $i;
}
return join '', reverse @output;
}
2 2
5 13
10 32
60 2223
200 31221
Show Me The (JavaScript) Code
"use strict";
let list = [2, 5, 10, 60, 200];
for (let i in list) {
let n = list[i];
let s = solve_sequence(n);
console.log(["", n, s].join("\t"));
}
function solve_sequence(n) {
let j = n * 2;
let s = get_sequence(1 + j);
while (s[n] == undefined) {
j = j * 2;
s = get_sequence(j);
}
return s[n];
}
function get_sequence(n) {
n = parseInt(n);
let sequence = Array(n)
.fill("")
.map((x, i) => first_pass(i + 1))
.filter((x) => !x.toString().match(/0/))
.filter((x) => !x.toString().match(/11/));
sequence.unshift(0);
return sequence;
}
function first_pass(n) {
if (n == 0) {
return 0;
}
let output = [];
while (n) {
let i = n % 4;
n = parseInt(n / 4);
output.push(i);
}
return output.reverse().join("");
}
2 2
5 13
10 32
60 2223
200 31221