Do you have a question? Post it now! No Registration Necessary. Now with pictures!
 Subject
 Posted on
 Rainer Weikusat
November 11, 2013, 11:21 pm
list of all pairs which can be formed from the sequence 0, 1, 2, ... 31.
I figured that it should be possible to write a recursive function
taking a 'ring size' (32 in this case) and a 'depth' (2) argument which
would return a generator cycling through all pairs, with the
function generating the generator being capable of returning a working
result for any combination of size and depth. This turned out to be
amazingly more complicated than I originally thought. The result I came
up with is

sub ring
{
my $size = $_[0];
my $cur;
$cur = 1;
return sub {
return $cur = ($cur + 1) % $size;
}
}
sub rings
{
my ($size, $depth) = @_;
my ($cur, $ring_next, $tail, $last_t0);
$ring_next = ring($size);
return $ring_next if $depth == 1;
$last_t0 = $cur = 1;
$tail = rings($size, $depth  1);
return sub {
my @tail;
@tail = $tail>();
$cur = $ring_next>() if !$tail[0] && $last_t0;
$last_t0 = $tail[0];
return ($cur, @tail);
};
}
my $r = rings(32, 2);
print(join(',', $r>()), "\n") for 0 .. 1023;

Are there other possibilities?
Re: 'depth n' combinations of a sequence of numbers
Did you considered using Algorithm::Combinatorics?
It's written in C, with no recursion and based in the literature.
It claims to be efficient, and as far as I tested it is i.e. it
takes the same time to generate all the permutations that the circular
permutations, proportionally.
Best regards
Re: 'depth n' combinations of a sequence of numbers
[...]
Again, I wanted a recursive solution. But this is nevertheless a neat
idea. In somewhat demystified from, it looks like this:
perl E 'say(join("\n", glob(sprintf(",", (join(",", 0 .. 31)) x 2))))'
which is based on using socalled 'brace expansion' to create a
cartesian product (is this the right term?) of n sets. The drawback
would be that this is a rather memory intense process which means that
perl E 'say(join("\n", glob(sprintf(",,,,", (join(",", 0 .. 31)) x 5))))'
pushes the computer where I ran this (512M) into thrashing while the
equivalent, arraybased routine I posted in the other reply produces the
result set with constant memory usage in about 5s (as do the other two,
albeit they take somewhat longer).
Re: 'depth n' combinations of a sequence of numbers
Since Perl has arrays, iteration is simpler than recursion for this problem.
sub rings
{
my($size,@values);
$size=int(shift(@_));
@values=(0) x shift(@_);
sub
{
my(@result);
@result=@values;
$_=($_+1)%$size and last for reverse @values;
@result;
};
}
Or, as stated elsewhere in this thread, use Algorithm::Combinatorics.
/Bo Lindbergh
Re: 'depth n' combinations of a sequence of numbers
Sorry but this was not a "Help me! I'm lost!" question. I was
specifically looking for a recursive solution.
There are some things about this code I really dislike, eg, the $size =
int(shift(@_)) instead of the equivalent $size = $_[0]. Also, you're not
treating the initial state specially despite it is special which means
the code has to do an intermediate copy of the result array. But the
forloop is decidedly a good idea. Combining some parts of both leads to

sub rings
{
my ($size, @tuple);
$size = $_[0];
@tuple = (1) x $_[1];
return sub {
$_ = ($_ + 1) % $size and last for reverse(@tuple);
return @tuple;
}
}

Which is what I will likely actually use.
I understand that "download shit from the internet" (and stitch that
together by applied ingenuity aka 'bizarre workarounds nobody
understands') is some people's natural first reaction to any programming
problem and I won't begrudge them the (commercial) successes they
achieve in this way with "our free software who runs your company",
however, I don't care about that because I know that I will have to
maintain this 'shit' at some point in time and fixing bugs in
unfamiliar code takes longer than writing code in many cases (I don't do
"fire, bill got paid, anything else not my problem, forget" jobs).
Since the same people usually can't stop showing off their impressive
downloading skills, I've instructed my newsreader to stop them for me
:).
Re: 'depth n' combinations of a sequence of numbers
In the given context,
,
 sub rings
 {
 my($size,@values);

 $size=int(shift(@_));
 @values=(0) x shift(@_);
 sub
 {
 my(@result);

 @result=@values;
 $_=($_+1)%$size and last for reverse @values;
 @result;
 };
 }
`
they were 'mostly' equivalent: Technically, the first shift is needed,
because otherwise, the second shift will return the wrong argument. But
since @_ isn't accessed anymore after the second shift, the first can be
replaced with $_[0] and the second with $_[1] which is (IMHO) clearer
because there's no point in shifting @_. This would turn the
$size = int(shift(@_))
into
$size = int($_[0])
If the value of $size is <= UV_MAX, $x % $size == $x % int($size),
example of the other case (on a 32bit system):
[rw@sable]~#perl e '$a = 2**33; $b = 2**32 + 1.3; print($a % $b, " ", $a % int($b), "\n")'
4294967294.7 4294967295
but practically, that's outside of the domain of 'sensible' $size
values.
Nevertheless, I didn't know that about the latter.
Re: 'depth n' combinations of a sequence of numbers
Using "glob" as suggested by DeRykus, for list [$p..$q] and depth $d,
use Data::Dump qw(dump);
($p, $q, $d) = @ARGV;
$x = join ",", $p..$q;
dump run($x,$d);
sub run {
my ($T, $d) = @_;
if ($d == 1) {
glob "}";
} else {
$T = join ",", run($T, $d);
glob "}_}";
}
}
James
use Data::Dump qw(dump);
($p, $q, $d) = @ARGV;
$x = join ",", $p..$q;
dump run($x,$d);
sub run {
my ($T, $d) = @_;
if ($d == 1) {
glob "}";
} else {
$T = join ",", run($T, $d);
glob "}_}";
}
}
James
Re: 'depth n' combinations of a sequence of numbers
El 15/11/13 02:06, James escribió:
I'm afraid this has to be parsed and that delays the final result.
Here is a comparison of methods:
Brute force method takes 0.000116 s.
Intelligent method takes 0.001029 s.
Reiner #a gen() takes 0.000337 s. (requires finish)
Reiner #b rings() takes 0.000573 s. (requires finish)
James glob method takes 0.00066 s. and bad return, to be parsed
Then, and in counterintuitive results, the best method to do
variations_with_repetition of order 2 in a short list is:
@data = 0..31;
for $i (@data){
for $j (@data){
push @results, ($i,$j);
}
}
which I call it the "brute force" approach. It's simpler, more
agile, in a task that it's not very sensitive a priori by the
upper bound of only 1 milisecond.
Best regards
I'm afraid this has to be parsed and that delays the final result.
Here is a comparison of methods:
Brute force method takes 0.000116 s.
Intelligent method takes 0.001029 s.
Reiner #a gen() takes 0.000337 s. (requires finish)
Reiner #b rings() takes 0.000573 s. (requires finish)
James glob method takes 0.00066 s. and bad return, to be parsed
Then, and in counterintuitive results, the best method to do
variations_with_repetition of order 2 in a short list is:
@data = 0..31;
for $i (@data){
for $j (@data){
push @results, ($i,$j);
}
}
which I call it the "brute force" approach. It's simpler, more
agile, in a task that it's not very sensitive a priori by the
upper bound of only 1 milisecond.
Best regards
Re: 'depth n' combinations of a sequence of numbers
El 18/11/13 21:12, James escribió:
No, but I tried now. Let's say the order of the variations is 4, higher
enough to not to try to loop over @data. Then, the number of variations
with repetition is 32**4. I stored in an array all the variations from
the literature method (module in C) and the Rainer's method. It takes
a decent size to not to try with 5 at home. Timing is 1.157 sec. for the
classical method and 0.819 for the Rainer's method, which suggest
that is a good method. But at end of the Rainer's array (stopped at
++$count == 32**4) I saw an anomaly: the quad 31 31 31 31 is repeated
in 32**41 and 32**4. The output in the classic method is correct:
31 31 31 30 and 31 31 31 31. So, I don't know what to say. I need to
check later.
Best regards
No, but I tried now. Let's say the order of the variations is 4, higher
enough to not to try to loop over @data. Then, the number of variations
with repetition is 32**4. I stored in an array all the variations from
the literature method (module in C) and the Rainer's method. It takes
a decent size to not to try with 5 at home. Timing is 1.157 sec. for the
classical method and 0.819 for the Rainer's method, which suggest
that is a good method. But at end of the Rainer's array (stopped at
++$count == 32**4) I saw an anomaly: the quad 31 31 31 31 is repeated
in 32**41 and 32**4. The output in the classic method is correct:
31 31 31 30 and 31 31 31 31. So, I don't know what to say. I need to
check later.
Best regards
Re: 'depth n' combinations of a sequence of numbers
[...]
This is not a generator, it is certainly not a generator constructed by
a recursive function and it doesn't even create pairs, just a flat list
of numbers. Considering this, something which is going to be a hell lot
faster and also doesn't work would be
perl e ';'
Re: 'depth n' combinations of a sequence of numbers
El 18/11/13 21:30, Rainer Weikusat escribió:
Well in fact, running perl to do nothing could take 2 miliseconds, where
generating the list of variations takes 1.
Jokes and "negation mode" apart, if you have the list of pairs of
variations you could cycle over it as free as you want, one by one,
by steps, take random pairs, etc. You know perfectly this, and I
know that you know.
Best regards
Well in fact, running perl to do nothing could take 2 miliseconds, where
generating the list of variations takes 1.
Jokes and "negation mode" apart, if you have the list of pairs of
variations you could cycle over it as free as you want, one by one,
by steps, take random pairs, etc. You know perfectly this, and I
know that you know.
Best regards
Re: 'depth n' combinations of a sequence of numbers
# I hope this is fancy enough for you !
my $iterator = CodeGen();
print $iterator>();
print $iterator>();
while (my $res = $iterator>()) {print $res}
sub CodeGen
{
my ($i,$j)=(0,0);
sub
{
$i++;
if ($i>31)
if ($j>31){return undef}
return "$i,$j\n"
}
}
my $iterator = CodeGen();
print $iterator>();
print $iterator>();
while (my $res = $iterator>()) {print $res}
sub CodeGen
{
my ($i,$j)=(0,0);
sub
{
$i++;
if ($i>31)
if ($j>31){return undef}
return "$i,$j\n"
}
}
Re: 'depth n' combinations of a sequence of numbers
[...]
This starts with 1, 0 and not with 0, 0. It doesn't return a list. And
it doesn't cycle. Something simple which works (as intended) just for pairs from 0
.. 31 could be
sub gen
{
use integer;
my $x = 1;
return sub {
++$x;
return ($x / 32, $x % 32);
};
}
or
sub gen
{
use integer;
my $x = 1;
sub { (++$x / 32, $x % 32) }
}
[Compared with the overhead of the function call, the time needed for
the divisions is probably a negligible quantity]
Site Timeline
 » ImageMagick to rotate an image
 — Next thread in » PERL Discussions
 » Defining a data structure  is there a standard/best practice way?
 — Previous thread in » PERL Discussions
 » s suffix question
 — Newest thread in » PERL Discussions
 » SSD partition alignment revisited
 — The site's Newest Thread. Posted in » Computer Hardware