Late Weekly challenge 67 #1 only

I wrote some library to make combination in 2013.
I was overwhelming when I found this challenge but I found that it is buggy !!!

I think that finding combination isn't necessarily written using recursive calling.
so this is my first "working" solution.

it is possible to use some list of words (ex) "a", "b", "c" ) instead of number.


#!/usr/bin/env perl
# -*- Mode: cperl; cperl-indent-level:4; tab-width: 8; indent-tabs-mode: nil -*-
# Copyright (c) 2013,2020 JEON Myoungjin

use strict; use warnings;



package List::Selection::Combination;

#use Carp qw(croak);

#use version 0.77 our $VERSION = version->declare( '0.1.3' );



sub make_combination_no_recursive ( $$ ) {

   
my $N   = $_[0];            # number of selection

   
my @elements = @{ $_[1] };  # reference !!!

   
my $M = scalar @elements;



    my @result;



    # minimum sanity check

   
if ( $M < $N ) {

        warn
"unable to choose $N from given selection of $M";

       
return ();

   
}



    my ( @room,         # number of spaces(rooms) each finger can move

         
@pos,          # current position of finger

         $next_id      
# of finger to move

       
);



    # set initial values ...

   
{

       
# each finger can move to right number of ( M-N ) space(s).

       
@room = ( $M-$N ) x $N;

       
@pos  = 0..($N - 1);

        $next_id
= $N - 1; # id starts from zero

       
# initial record; note: use not index number but real value

        push
@result, [ map { $elements[$_] }  @pos ];

       
#print "@pos\n";

   
}



    {

       
if ( $room[$next_id] > 0 ) {

           
# can move to right so .. do it.

           
--$room[$next_id];

           
++$pos[$next_id];

           
# and make a record

            push
@result, [ map { $elements[$_] } @pos ];

           
#print "@pos\n";

           
redo;

       
}

       
else {

           
# no more room to move

           
# so find the next finger to move.

           
my $found = 0;

           
for ( my $i = $next_id; $i > 0; --$i ) {

               
if ( $room[ $i-1 ] > 0 ) {

                    $next_id
= $i-1;

                    $found
= 1;

                   
last;

               
}

           
}



            if ( $found ) {

               
#move all the fingers which are starts from $next_id to last one

               
@pos[ $next_id..($N-1) ]

                 
= ( $pos[$next_id]+1 ) ..( $pos[$next_id] + ($N-$next_id) );

               
@room[ $next_id..($N-1) ]  # note: all finger has same room

                 
= ( $room[ $next_id ] - 1 ) x ( $N - $next_id );

               
# and make a record

                push
@result, [ map { $elements[$_] } @pos ];

               
#print "@pos\n";



                # next finger to move will be ($N-1)

               
# or will find next loop;

                $next_id
= ($N-1);



                redo; # if we can move next finger

           
}

       
}

   
}

   
return @result;

}



package main;



my $m = shift // 5;

my $n = shift // 2;



my @result = List::Selection::Combination::make_combination_no_recursive

   
( $n,  [ 1..$m ] );



for my $r (@result) {

   
print "@r\n";

}

below code is almost same as above but assume use only digits.
and in format the challenge wants me to do.


# -*- Mode: cperl; cperl-indent-level:4; tab-width: 8; indent-tabs-mode: nil -*-
# Copyright (c) 2013,2020 JEON Myoungjin

use strict; use warnings;
#use feature qw /say/;

package List::Selection::Combination;
#use Carp qw(croak);
#use version 0.77 our $VERSION = version->declare( '0.1.3' );

sub print_combination_no_recursive ( $$ ) {
my $N = $_[0]; # number of selection
my @elements = @{ $_[1] }; # reference !!!
my $M = scalar @elements;

local $" = ",";

if ( $M < $N ) {
warn "unable to choose $N from given selection of $M";
return ();
}

print "[ ";
my ( @room, # number of spaces(rooms) each finger can move
@pos, # current position of finger
$next_id # of finger to move
);

{
@room = ( $M-$N ) x $N;
@pos = 1..$N;
$next_id = $N - 1; # id starts from zero
print "[@pos]";
}

{
if ( $room[$next_id] > 0 ) {
--$room[$next_id];
++$pos[$next_id];
print ", [@pos]";
redo;
}
else {
my $found = 0;
for ( my $i = $next_id; $i > 0; --$i ) {
if ( $room[ $i-1 ] > 0 ) {
$next_id = $i-1;
$found = 1;
last;
}
}

if ( $found ) {
@pos[ $next_id..($N-1) ]
= ( $pos[$next_id]+1 ) ..( $pos[$next_id] + ($N-$next_id) );
@room[ $next_id..($N-1) ]
= ( $room[ $next_id ] - 1 ) x ( $N - $next_id );
print ", [@pos]";

$next_id = ($N-1);

redo;
}
}
}
print " ]";
}

package main;

my $m = shift // 5;
my $n = shift // 2;

List::Selection::Combination::print_combination_no_recursive
( $n, [ 1..$m ] );


and performance is quite excellent.


time perl
Task1.combination.pl 100 5 | wc -l
75287520

________________________________________________________

Executed in   34.37 secs   fish           external

   usr time  
34.98 secs  906.00 micros   34.98 secs

   sys time    
2.19 secs  105.00 micros    2.19 secs


I'm a chef and haven't got enough time to code these days.
but this kind of challenge makes me feel alive.

2 Comments

Leave a comment

Sign in to comment.

About Myoungjin Jeon

user-pic JG's blog for perl (-; I'm a chef and enjoying coding for my life.