aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--challenge-114/james-smith/README.md374
-rw-r--r--challenge-114/james-smith/blog.txt1
-rw-r--r--challenge-114/james-smith/perl/ch-1.pl69
-rw-r--r--challenge-114/james-smith/perl/ch-2.pl70
4 files changed, 261 insertions, 253 deletions
diff --git a/challenge-114/james-smith/README.md b/challenge-114/james-smith/README.md
index 3d284e315c..9bf832ce51 100644
--- a/challenge-114/james-smith/README.md
+++ b/challenge-114/james-smith/README.md
@@ -1,4 +1,6 @@
-# Perl Weekly Challenge #113
+# Perl Weekly Challenge #114
+
+# What no regexs or loops....
You can find more information about this weeks, and previous weeks challenges at:
@@ -10,311 +12,177 @@ submit solutions in whichever language you feel comfortable with.
You can find the solutions here on github at:
-https://github.com/drbaggy/perlweeklychallenge-club/tree/master/challenge-113/james-smith/perl
-
-# Challenge 1 - Represent Integer
-
-**You are given a positive integer `$N` and a digit `$D`. Write a script to
-check if $N can be represented as a sum of positive integers having `$D` at
-least once. If check passes print `1` otherwise `0`.**
-
-**Assumption** although not clear in the question, we make the
-assumption that the numbers in the sum are all unique.
+https://github.com/drbaggy/perlweeklychallenge-club/tree/master/challenge-114/james-smith/perl
-## Solution...
+# Challenge 1 - Next highest palindrome
-There are two classes of solution to this problem:
+***You are given a positive integer `$N`. Write a script to find out
+the next Palindrome Number higher than the given integer `$N`.***
- 1. Solutions where `$D` only appears in the right hand column.
- 1. Solutions where `$D` appears in more than one column.
+## The solution - naive
-### Type 2 solutions...
-If `$D` is 0. Then we can find solutions for all numbers `>=100`. For
-the numbers `100` -> `109` the number itself surfices. For numbers
-`>=110` we can always find a solution of the form (or similar):
+We will see this again for the next challenge we just increment `$N`
+until we find another palindrome.
-```
- x x x x 0 x
-+ x 0
+```perl
+sub next_palindrome_naive {
+ my ($n) = @_;
+ 1 until ++$n eq reverse $n;
+ return $n
+}
```
-If `$D` in not 0. Then we can find similar solutions for all numbers
-`>= 10*$D`. Again for `10*$D` -> `10*$D + 9` the number itself surfices.
-For numbers `>=11*$D` we have similar solutions to above where we can
-write the nubmer as:
+## The solution - optimized
-```
- x x x x D x
-+ x D
-```
-
-So we check for that first, then we can look at the type (1) solutions
+First we note that it is easier to compute the palindrome greater than
+equal to itself {so we just incremement the passed parameter}.
-### Type 1 solutions...
+We should then be able to do away with the loop entirely as the
+palindromic number will either have the same first half as itself OR
+will have this value incrememented by 1 as the first half.... No loop
+requried..
-We need a sequence of numbers `$D`, `10+$D`, `20+$D`, `30+$D` *etc*
-that is less than or equal to `$N` and has the same last digit as `$N`.
-Now we note `$D + 10+$D + 20+$D + 30+$D + 40+$D` is `100 + 5$D` and so
-we know this is possible to represent for all values of `$D` so we
-can ignore this case.
+### The cases..
-## Perl code
+There are two cases we need to consider:
-The first pass at this uses a for loop to generate the values of the
-numbers in the sum. After first checking the criteria for type 2
-solutions...
+ * There are an even number of digits
+ * There are an odd number of digits..
-```perl
-sub represent {
- my( $t, $n, $d ) = ( 0, @_ );
- ## Type 2 solutions...
- return 1 if $n >= 10 *A ( $d || 10 );
- ## Type 1 solutions...
- $n >= ( $t += $_ * 10 + $d ) &&
- ( $n % 10 == $t % 10 ) && return 1 for 0..3;
- 0;
-}
-```
+The first case is slightly easiers as we just check to see if the
+palindrome created by reversing the first digits and putting them
+at the end is greater than or equal to the number, and if not
+increment and try again.
-We can further improve performance by removing the need for the `$t`
-variable and the `for` loop by "unrolling" the loop as below...
+The second case is slightly more interesting as we have the middle
+digit to consider. In the 2nd half above we can increment the middle
+digit if (less than 9) OR incremennt the first digits..
```perl
-sub represent_unrolled {
- my( $n, $d ) = @_;
- ## Type 2 solutions...
- $n >= 10 * ( $d || 10 ) ||
- ## Type 1 solutions...
- $n >= $d && $n%10 == $d ||
- $n >= 2*$d+10 && !( ($n-2*$d)%10 ) ||
- $n >= 3*$d+30 && !( ($n-3*$d)%10 ) ||
- $n >= 4*$d+60 && !( ($n-4*$d)%10 ) ? 1 : 0;
+sub next_palindrome {
+ my $p = 1 + shift;
+ my $x = substr $p, 0, (length $p)>>1;
+ if( 1 & length $p ) {
+ my $y = substr $p, (length$p)>>1, 1;
+ return $x.$y.reverse $x if $p <= $x.$y.reverse $x;
+ return $x.($y+1).reverse $x if $y<9;
+ $x++;
+ return $x.'0'.reverse $x;
+ } else {
+ $x++ if $p > $x.reverse $x;
+ return $x.reverse $x;
+ }
}
```
-This appears to be 50% faster than the loop solution...
+## Notes and Summary
-# Challenge 2 - Recreate Binary Tree
+You will note I've used the "Yoda" form of some of the expressions
+inequalities. It is much easier for instance to realise that:
+`1 & length $p` is "and"ing `1` with the length of `$p` rather than
+"and"ing `1` with `$p` and then taking the length (which will be 1) if
+you were to write `length $p & 1`...
-**You are given a Binary Tree. Write a script to replace each
-node of the tree with the sum of all the remaining nodes.**
+There were some cases where I thought assigning the result of
+`reverse$x` and `length$p` would speed things up - but it seemed to
+slow things down by 10% or so - So I'm assuming there is some neat
+code in the interpreter/compiler does this cacheing for you.
-We already have an embryonic `Tree` object from the Linked list/tree
-challenge in week 94.
+For small numbers of `$N` there is little difference in the performance
+15% - but as soon as numbers are up to 3/4 digits then the optimised
+version is 6 times faster, for 5/6 digits 80 times faster, for 7/8
+approximately 1000 times faster...
-So we will extend this (and to write a true `BinaryTree`) representation.
+# Challenge 2 - Higher Integer Set Bits
-## Walk the tree
+***You are given a positive integer `$N`. Write a script to find
+the next higher integer having the same number of 1 bits in binary
+representation as `$N`.***
-For each of the problems cloning, summing, updating a tree we need to
-walk the tree. The solution I propose here will define a "walking"
-function on the tree, which has it's parameters:
- * a function to handle each node
- * a "global" storage object
- * a "local" storage object
- * whether the node is a left child / right child.
+## The solution - naive
-## Perl `BinaryTree` object.
-
-Our binary tree is represented by an array of length 3. The value of
-the node {which can be any object} and the left and right children.
-
-We then have two methods - `add_left_child` and `add_right_child` to
-add them to the tree.
-
-We also have accessors:
-
- * `left` - left child
- * `right` - right child
- * `value` - value of node
-
-We have to functions to check for existance of a child:
-
- * `has_left` - left child
- * `has_right` - right child
-
-and finally a method to update a node `update`
+There is a simple solution we can try - and that is to take the number,
+count the number of 1-bits, and then just increment repeatedly until we
+get a number with the same amount of 1-bits.
```perl
-package BinaryTree;
-
-sub new {
- my $class = shift;
- my $value = shift;
- my $self = [ $value, undef, undef ];
- bless $self, $class;
-}
-
-sub update {
- my( $self, $val ) = @_;
- $self->[0] = $val;
- return $self;
-}
-
-sub value {
- my $self = shift;
- return $self->[0];
+sub next_bin {
+ my $n = shift;
+ my $c = (sprintf '%b', $n) =~ tr/1/1/;
+ while(++$n) {
+ return $n if $c == ( (sprintf '%b', $n) =~ tr/1/1/ );
+ }
}
+```
-sub add_child_left {
- my( $self,$child ) = @_;
- $self->[1] = $child;
- return $self;
-}
+ * We convert the number to binary using sprintf with the format `'%b'`;
+ * We count the number of "1"s in the string using `tr`. `tr/1/1/` leaves the string unchanged but returns the number of "1"s that were replaced.
-sub add_child_right {
- my( $self,$child ) = @_;
- $self->[2] = $child;
- return $self;
-}
+## The solution - optimized
-sub left {
- my $self = shift;
- return $self->[1];
-}
+We can easily find a solution to this problem.
-sub right {
- my $self = shift;
- return $self->[2];
-}
+If the number contains a pair of digits "01" then we can find a number
+that is larger but has the same number of digits by swapping the "01" to "10".
+(Note we can force the binary representation to always have a "01" by prefixing
+the binary representation with "0")
-sub has_left {
- my $self = shift;
- return defined $self->[1];
-}
+So e.g. `174 = 1010 1110` - you can replace either of the `01`s to give either:
-sub has_right {
- my $self = shift;
- return defined $self->[2];
-}
+ * `1100 1110 = 206`
+ * `1011 0110 = 182`
-```
-
-## Walking the tree...
+We note that to minimize the number we start by replacing the last `01` by `10`
-Our tree walking function takes up to 4 parameters:
+So we have: `182 = 1011 0110 > 174 = 1010 1100`
- 1. `$fn` a callback function which does whatever is needed,
- e.g. collect summary statistics, update the node etc;
- 1. `$global` a reference to a variable which is used as
- "global" storage for the walk
- 1. `$local` a variable which is used as "local" storage for the walk,
- it is updated as the value from `$fn` before being passed to the
- children,
- 1. `$dir` the direction of the walk whether it be "left" or "right"
+The digits after the last `01` will be of the form `1...10..0`, so we can again
+reduce the value by flipping this string around to be `0...01...1`;
-`$self`, `$global`, `$local` and `$dir` are all passed to the callback
-function....
+So now we have: `179 = 1011 0011 > 174 = 1010 110`
-The code itself is quite simple to look at...
+The code then becomes either:
```perl
-sub walk {
- my( $self, $fn, $global, $local, $dir ) = @_;
- $local = $fn->( $self, $global, $local, $dir||'' );
- $self->left->walk( $fn, $global, $local, 'left' ) if $self->has_left;
- $self->right->walk( $fn, $global, $local, 'right' ) if $self->has_right;
- return;
+sub next_bin_rex {
+ return oct '0b'.sprintf('0%b',shift) =~ s{01(1*)(0*)$}{10$2$1}r;
}
```
-
-## Cloning - with walk
+or
```perl
-sub clone {
- my( $self, $clone_fn ) = @_;
- $clone_fn ||= sub { $_[0] };
- my $clone = {};
- $self->walk( sub { my( $node, $global, $local, $dir ) = @_;
- if(exists $global->{'tree'} ) {
- my $child = BinaryTree->new( $clone_fn->( $node->value ) );
- $dir eq 'left' ? $local->add_child_left( $child ) : $local->add_child_right( $child );
- return $child;
- }
- $global->{'tree'} = BinaryTree->new( $clone_fn->( $node->value ) );
- return $global->{'tree'};
- }, $clone );
- return $clone->{'tree'};
+sub next_bin_rrev {
+ my $t = rindex my $s = sprintf('0%b',shift),'01';
+ return oct '0b'.substr($s,0,$t).'10'.reverse substr $s,$t+2;
}
```
-We can use this `walk` method to clone our tree.
-Global is a hashref with one entry - 'tree' which will contain our
-cloned tree.
-The first time through the loop it initialises the tree and adds the
-root node to the tree and returns itself. `$local` is then this node.
+depending on whether or not you use a regular expression to find
+the last "`01`" in the binary representaiton.
-For subsequent calls the tree exists and so a new BinaryTree objects is
-produced and attached to it's parent (which is held in `$local`) either
-as a left or right child.
+## Summary
-`clone` can take an additional `callback` which is applied to each
-node when being copied - which defaults to just a straight copy.
+Both the performance of `next_bin_regex` and `next_bin_rrev` appear
+to slow down only slightly as `$N` increases - comparabale with
+"linear" scans for the last "`01`". Whereas the `next_bin` naive
+method has no such property.
-There are two more "walk methods" in the object, which dump the data:
-either as a single line of values (flatten) or as "ASCII-art" to show
-the nodes and their relationships.
+Running this a large number of times - we have the following
+approximate rates for the calculations....
-```perl
-sub flatten {
- my( $self,$dump_fn ) = @_;
- $dump_fn ||= sub { $_[0] };
- my $arrayref = [];
- $self->walk( sub {
- my($node,$global) = @_;
- push @{$global}, $dump_fn->( $node->value );
- }, $arrayref );
- return @{$arrayref};
-}
+| Size of number | Rate rrev | Rate regex | Rate naive |
+| -------------- | ---------: | ---------: | ---------: |
+| 1-500 | 1,600,000 | 500,000 | 600,000 |
+| Approx 1000 | 1,550,000 | 440,000 | 400,000 |
+| Approx 1x10^6 | 1,500,000 | 390,000 | 4,000 |
+| Approx 1x10^9 | 1,450,000 | 330,000 | 1 |
-sub dump {
- my( $self, $dump_fn ) = @_;
- $dump_fn ||= sub { $_[0] };
- $self->walk( sub {
- my( $node, $global, $local, $dir ) = @_;
- say join '',
- $local||'',
- $dir eq 'left' ? '<' : $dir eq 'right' ? '>' : ' ',
- ' ', $dump_fn->($node->value);
- return $local .= ' ';
- }, {}, '', '' );
- return;
-}
-```
-
-Like clone they take a simple call back if you want to include a
-function of the object's values rather than the value itself....
-*a callback* within *a callback*.... You could even say its
-***Turtles all the way down***...
-
-## The solution
-
-### Walking the tree to get the sum
-
-We create a "*global*` variable which contains the total, walk through
-all nodes and add the node value to this total.... We can then
-retrieve the value by inspecting `$glob`..
-
-```perl
-my $glob = { 'total' => 0 };
-$y->walk( sub {
- my( $node, $global ) = @_;
- $global->{'total'} += $node->value;
-}, $glob );
-```
-
-### Walking the tree to update the nodes...
+The calls do include the hardest example `2^n-1` for which the next
+number is `2^(n-1)` more - so much of the time in the naive loop is
+taken up by that example - in the 1x10^9 example this would require
+500_000_000 iterations of the increment/check loop.
-We pass the variable $glob back in, and use the total there
-to update the value. For the value to be the sum of all the
-other nodes, we can get this by adding all the nodes together
-than subtracting away the node value....
-
-So the 2nd half becomes.
-```perl
-$y->walk( sub {
- my( $node, $global ) = @_;
- $node->update( $global->{'total'} - $node->value );
-}, $glob );
-```
+We see as we did a few weeks ago that if you don't actually need to
+use regexs then you can get an appreciable speed boost. Obviously
+remembering there is trade off between coding and running time.
diff --git a/challenge-114/james-smith/blog.txt b/challenge-114/james-smith/blog.txt
new file mode 100644
index 0000000000..83cc017bd8
--- /dev/null
+++ b/challenge-114/james-smith/blog.txt
@@ -0,0 +1 @@
+https://github.com/drbaggy/perlweeklychallenge-club/tree/master/challenge-114/james-smith
diff --git a/challenge-114/james-smith/perl/ch-1.pl b/challenge-114/james-smith/perl/ch-1.pl
new file mode 100644
index 0000000000..8303864dbd
--- /dev/null
+++ b/challenge-114/james-smith/perl/ch-1.pl
@@ -0,0 +1,69 @@
+#!/usr/local/bin/perl
+
+use strict;
+
+use warnings;
+use feature qw(say);
+use Test::More;
+
+my @tests = ([1,2],[9,11],[99,101],[989,999],[999,1001],[10,11],[11,22],[12,22],
+ [1990,1991],[1992,2002],[99011,99099],[97979,98089],[99099,99199],[99999,100001],
+ [111222,112211]);
+#@tests = map { chomp; [split] } <>;
+
+is( next_palindrome( $_->[0] ), $_->[1] ) foreach @tests;
+is( next_palindrome_naive( $_->[0] ), $_->[1] ) foreach @tests;
+
+done_testing();
+
+use Benchmark qw{ cmpthese };
+my @ranges = (
+ [ 100_000, 1, 101 ],
+ [ 50_000, 950, 1050 ],
+ [ 20_000, 9950, 10050 ],
+ [ 10_000, 99950, 100050 ],
+ [ 5_000, 999950, 1000050 ],
+ [ 1_000, 9999950, 10000050 ],
+ [ 1_000, 99999950, 100000050 ],
+);
+
+foreach my $r (@ranges) {
+ cmpthese($r->[0], {
+ slow => sub { next_palindrome_naive($_) for $r->[1] .. $r->[2] },
+ fast => sub { next_palindrome($_) for $r->[1] .. $r->[2] },
+ });
+}
+
+sub next_palindrome_naive {
+ my ($n) = @_;
+ 1 until ++$n eq reverse $n;
+ return $n
+}
+
+sub next_palindrome {
+ my $p = 1 + shift;
+ my $x = substr $p, 0, (length $p)>>1;
+ if( 1 & length $p ) {
+ ## Odd length so we have three options...
+ ## new number by reversing the first half of the number > $n so OK!
+ ## middle digit is < 9 so just increment that
+ ## o/w we will need to increment the first half and reverse it...
+ ## if $x is 999 then the only time we could do the ++ to get
+ ## a longer string is when $p is of the form 999.9.999, but
+ ## then 999.9.999 > 999.9.998 and so we don't get this... so we
+ ## are safe with this approach...
+ my $y = substr $p, (length$p)>>1, 1;
+ return $x.$y.reverse $x if $p <= $x.$y.reverse $x;
+ return $x.($y+1).reverse $x if $y<9;
+ return ++$x.'0'.reverse $x;
+ }
+ ## Even no of digits..
+ ## If $n >= $x.reverse $x we incrememnt $x;
+ ## The only time that this could lead to a longer number is when
+ ## $x is 999 and is OK as $x.reverse $x will always be larger than
+ ## $n....
+ return $x.reverse $x if $p <= $x.reverse $x;
+ return ++$x.reverse $x;
+}
+
+
diff --git a/challenge-114/james-smith/perl/ch-2.pl b/challenge-114/james-smith/perl/ch-2.pl
new file mode 100644
index 0000000000..773f79b0cb
--- /dev/null
+++ b/challenge-114/james-smith/perl/ch-2.pl
@@ -0,0 +1,70 @@
+#!/usr/local/bin/perl
+
+use strict;
+
+use warnings;
+use feature qw(say);
+use Test::More;
+use Benchmark qw(cmpthese);
+
+my @sols = (
+[7,11],[11,13],[13,14],[14,19],[19,21],[21,22],[22,25],[25,26],[26,28],[28,35],[35,37],[37,38],[38,41],[41,42],[42,44],[44,49],[49,50],[50,52],[52,56],[56,67],[67,69],[69,70],[70,73],[73,74],[74,76],[76,81],[81,82],[82,84],[84,88],[88,97],[97,98],[98,100],[100,104],[104,112],[112,131],[131,133],[133,134],[134,137],[137,138],[138,140],[140,145],[145,146],[146,148],[148,152],[152,161],[161,162],[162,164],[164,168],[168,176],[176,193],[193,194],[194,196],[196,200],[200,208],[208,224],[224,259],[259,261],[261,262],[262,265],[265,266],[266,268],[268,273],[273,274],[274,276],[276,280],[280,289],[289,290],[290,292],[292,296],[296,304],[304,321],[321,322],[322,324],[324,328],[328,336],[336,352],[352,385],[385,386],[386,388],[388,392],[392,400],[400,416],[416,448],[255,383],[383,447],[447,479],[479,495],[495,503],[503,507],[507,509],[509,510],[3,5],[5,6],[6,9],[9,10],[10,12],[12,17],[17,18],[18,20],[20,24],[24,33],[33,34],[34,36],[36,40],[40,48],[48,65],[65,66],[66,68],[68,72],[72,80],[80,96],[96,129],[129,130],[130,132],[132,136],[136,144],[144,160],[160,192],[192,257],[257,258],[258,260],[260,264],[264,272],[272,288],[288,320],[320,384],[15,23],[23,27],[27,29],[29,30],[30,39],[39,43],[43,45],[45,46],[46,51],[51,53],[53,54],[54,57],[57,58],[58,60],[60,71],[71,75],[75,77],[77,78],[78,83],[83,85],[85,86],[86,89],[89,90],[90,92],[92,99],[99,101],[101,102],[102,105],[105,106],[106,108],[108,113],[113,114],[114,116],[116,120],[120,135],[135,139],[139,141],[141,142],[142,147],[147,149],[149,150],[150,153],[153,154],[154,156],[156,163],[163,165],[165,166],[166,169],[169,170],[170,172],[172,177],[177,178],[178,180],[180,184],[184,195],[195,197],[197,198],[198,201],[201,202],[202,204],[204,209],[209,210],[210,212],[212,216],[216,225],[225,226],[226,228],[228,232],[232,240],[240,263],[263,267],[267,269],[269,270],[270,275],[275,277],[277,278],[278,281],[281,282],[282,284],[284,291],[291,293],[293,294],[294,297],[297,298],[298,300],[300,305],[305,306],[306,308],[308,312],[312,323],[323,325],[325,326],[326,329],[329,330],[330,332],[332,337],[337,338],[338,340],[340,344],[344,353],[353,354],[354,356],[356,360],[360,368],[368,387],[387,389],[389,390],[390,393],[393,394],[394,396],[396,401],[401,402],[402,404],[404,408],[408,417],[417,418],[418,420],[420,424],[424,432],[432,449],[449,450],[450,452],[452,456],[456,464],[464,480],[127,191],[191,223],[223,239],[239,247],[247,251],[251,253],[253,254],[254,319],[319,351],[351,367],[367,375],[375,379],[379,381],[381,382],[382,415],[415,431],[431,439],[439,443],[443,445],[445,446],[446,463],[463,471],[471,475],[475,477],[477,478],[478,487],[487,491],[491,493],[493,494],[494,499],[499,501],[501,502],[502,505],[505,506],[506,508],[31,47],[47,55],[55,59],[59,61],[61,62],[62,79],[79,87],[87,91],[91,93],[93,94],[94,103],[103,107],[107,109],[109,110],[110,115],[115,117],[117,118],[118,121],[121,122],[122,124],[124,143],[143,151],[151,155],[155,157],[157,158],[158,167],[167,171],[171,173],[173,174],[174,179],[179,181],[181,182],[182,185],[185,186],[186,188],[188,199],[199,203],[203,205],[205,206],[206,211],[211,213],[213,214],[214,217],[217,218],[218,220],[220,227],[227,229],[229,230],[230,233],[233,234],[234,236],[236,241],[241,242],[242,244],[244,248],[248,271],[271,279],[279,283],[283,285],[285,286],[286,295],[295,299],[299,301],[301,302],[302,307],[307,309],[309,310],[310,313],[313,314],[314,316],[316,327],[327,331],[331,333],[333,334],[334,339],[339,341],[341,342],[342,345],[345,346],[346,348],[348,355],[355,357],[357,358],[358,361],[361,362],[362,364],[364,369],[369,370],[370,372],[372,376],[376,391],[391,395],[395,397],[397,398],[398,403],[403,405],[405,406],[406,409],[409,410],[410,412],[412,419],[419,421],[421,422],[422,425],[425,426],[426,428],[428,433],[433,434],[434,436],[436,440],[440,451],[451,453],[453,454],[454,457],[457,458],[458,460],[460,465],[465,466],[466,468],[468,472],[472,481],[481,482],[482,484],[484,488],[488,496],[1,2],[2,4],[4,8],[8,16],[16,32],[32,64],[64,128],[128,256],[63,95],[95,111],[111,119],[119,123],[123,125],[125,126],[126,159],[159,175],[175,183],[183,187],[187,189],[189,190],[190,207],[207,215],[215,219],[219,221],[221,222],[222,231],[231,235],[235,237],[237,238],[238,243],[243,245],[245,246],[246,249],[249,250],[250,252],[252,287],[287,303],[303,311],[311,315],[315,317],[317,318],[318,335],[335,343],[343,347],[347,349],[349,350],[350,359],[359,363],[363,365],[365,366],[366,371],[371,373],[373,374],[374,377],[377,378],[378,380],[380,399],[399,407],[407,411],[411,413],[413,414],[414,423],[423,427],[427,429],[429,430],[430,435],[435,437],[437,438],[438,441],[441,442],[442,444],[444,455],[455,459],[459,461],[461,462],[462,467],[467,469],[469,470],[470,473],[473,474],[474,476],[476,483],[483,485],[485,486],[486,489],[489,490],[490,492],[492,497],[497,498],[498,500],[500,504]);
+
+#is( next_bin_rex( $_->[0]), $_->[1] ) foreach @sols;exit;
+#is( next_bin_rrev($_->[0]), $_->[1] ) foreach @sols;exit;
+#is( next_bin( $_->[0]), $_->[1] ) foreach @sols;exit;
+#done_testing();
+
+my @ranges = (
+ [ 1000, 1, 500 ],
+ [ 500, 500, 2500 ],
+ [ 100, 1_047_576, 1_049_576 ],
+ [ 50, 1_073_740_824, 1_073_742_824 ],
+);
+
+foreach my $r (@ranges) {
+ cmpthese( 10*$r->[0], {
+ 'rind' => sub { next_bin_rrev( $_ ) foreach $r->[1] .. $r->[2] },
+ 'rex' => sub { next_bin_rex( $_ ) foreach $r->[1] .. $r->[2] },
+# 'simp' => sub { next_bin( $_ ) foreach $r->[1] .. $r->[2] },
+ });
+}
+sub next_bin {
+ my $n = shift;
+ my $c = (sprintf '%b', $n) =~ tr/1/1/;
+ while(++$n) {
+ return $n if $c == ( (sprintf '%b', $n) =~ tr/1/1/ );
+ }
+}
+
+## All numbers can be written in the binary form as
+## ^[01]*(01)1*0*$
+## This we can match with the regexp..
+## /01(1*)(0*)$/
+## The next highest number with the same number of bits
+## flips the 01 to 10 and switches the 1s with the 0s
+## The regex replace is then:
+## /01(1*)(0*)$/10$2$1/
+
+sub next_bin_rex {
+ return oct '0b'.sprintf('0%b',shift) =~ s{01(1*)(0*)$}{10$2$1}r;
+}
+
+## We further note we can find the "01" with rindex
+## rather than having to use a regex {regex's are expensive}
+##
+## We also note that to flip 1111000 to 0001111 we don't need to
+## know how many 1s there are or 0s we just reverse the string.
+##
+## This gives us the following similar function which DOES NOT
+## use regexs
+##
+## Usually avoiding regexs leads to more performant code (unless the
+## replacement for the regex is particularly complex - which in this
+## case it isn't!)
+
+sub next_bin_rrev {
+ my $t = rindex my $s = sprintf('0%b',shift),'01';
+ return oct '0b'.substr($s,0,$t).'10'.reverse substr $s,$t+2;
+}
+