I am trying to write a function which return s-tuples with non-negative integers and a given sum $n$ (the sum of each tuple is $n$). In the program, I need to use s nested loops:
for i1 from 0 to n do
for i2 from 1 to n do
...
for is from 1 to n do
end for;
end for;
end for;
How could I use only a few loops instead of s loops? Thank you very much.
I suggest the combinat:-composition() command. On its own, the command won't include zero terms, but you can instead partition n+s and remove 1 from each term at the end:
restart;
partitions := proc( n :: posint, s :: posint, { allowzero :: truefalse := false } )
local P, u:
if allowzero = false then
P := convert( combinat:-composition( n, s ), 'list' ):
return select( u -> numelems(u) = s, P ):
else
P := procname( n + s, s, ':-allowzero'=false ):
return map( u -> u -~ 1, P ):
end if:
end proc:
partitions( 5, 2, ':-allowzero'=false ); # [ [1,4], [2,3], [3,2], [4,1] ]
partitions( 5, 2, ':-allowzero'=true ); # [ [0,5], [1,4], [2,3], [3,2], [4,1], [5,0] ]
I know this question has been asked before here (compare multiple hashes for common keys merge values). As far as I can tell, it went unanswered. If you answer, please include an example that uses the List::Compare->new() constructor.
List::Compare has the ability to accept multiple arrays as input. However, there are no examples that explain how to do so if you do not know in advance how many there will be passed to the constructor.
Example from the man page:
$lcm = List::Compare->new(\#Al, \#Bob, \#Carmen, \#Don, \#Ed);
or...
You may use the 'single hashref' constructor format to build a
List::Compare object to process three or more lists at once:
$lcm = List::Compare->new( { lists => [\#Al, \#Bob, \#Carmen, \#Don, #Ed], } );
or
$lcm = List::Compare->new( {
lists => [\#Al, \#Bob, \#Carmen, \#Don, \#Ed],
unsorted => 1, } );
I need to use this 'single hashref' constructor above, because I don't know how many lists (arrays) will be passed to the constructor. The closest I come is this:
my %l;
my #a = ("fred", "barney", "pebbles", "bambam", "dino");
my #b = ("george", "jane", "elroy", "judy");
my #c = ("homer", "bart", "marge", "maggie");
my #d = ("fred", "barney", "pebbles", "bambam", "dino");
my #e = ("fred", "george", "jane", "elroy", "judy", "pebbles");
$l{'lists'}{'a'} = [ #a ];
$l{'lists'}{'b'} = [ #b ];
$l{'lists'}{'c'} = [ #c ];
$l{'lists'}{'d'} = [ #d ];
$l{'lists'}{'e'} = [ #e ];
my $lc = List::Compare->new(\%l);
my #intersection = $lc->get_intersection;
print #intersection . "\n";
I am getting:
Need to define 'lists' key properly: at /usr/local/share/perl5/List/Compare.pm line 21.
The Compare.pm code (line 21) is:
die "Need to define 'lists' key properly: $!"
unless ( ${$argref}{'lists'}
and (ref(${$argref}{'lists'}) eq 'ARRAY') );
Can someone tell me how to construct and name this hash from simple arrays? I need to be able to process a wide and varied number of them continuously. There could possibly be hundreds of arrays involved.
Update
#Borodin's answer was exactly what I needed. I apologize for the bad data, was trying to come up with something concise. Here is what derived from that code
my #sets_to_process = qw( DOW SP500 has_trend_lines_day );
my #sets;
my $num_sets = $#sets_to_process;
for my $i (0 .. $num_sets) {
my #set = get_ids_in_list( $dbh, $sets_to_process[$i] );
push #sets, \#set;
}
my $lc = List::Compare->new(#sets);
my #intersection = $lc->get_intersection;
print "Sets:\n";
printf " %s\n", join ', ', #$_ for #sets;
print "\n";
print "Intersection:\n";
printf " %s\n", #intersection? join(', ', #intersection) : 'None';
print "\n";
The problem with your parameter construction is that you are defining %l (a dreadful identifier, by the way) as a hash containing a hash of arrays, like this
(
lists => {
a => ["fred", "barney", "pebbles", "bambam", "dino"],
b => ["george", "jane", "elroy", "judy"],
c => ["homer", "bart", "marge", "maggie"],
d => ["fred", "barney", "pebbles", "bambam", "dino"],
e => ["fred", "george", "jane", "elroy", "judy", "pebbles"],
},
)
but the documentation is clear that it should be a simple hash containing an array of arrays
(
lists => [
["fred", "barney", "pebbles", "bambam", "dino"],
["george", "jane", "elroy", "judy"],
["homer", "bart", "marge", "maggie"],
["fred", "barney", "pebbles", "bambam", "dino"],
["fred", "george", "jane", "elroy", "judy", "pebbles"],
],
)
Furthermore, it doesn't help your problem that “[You] don't know how many lists (arrays) will be passed to the constructor” because all that you are doing is pushing the problem inside a data structure instead of keeping it at the parameter level
It is difficult to help you with the data you have given because the intersection is the empty set, so here's a sample program you can experiment with that generates between five and ten sets of sixteen random alphabet letters. It creates different data to work with each time it is run, and passes the list of array references directly as parameters to the new constructor instead of using a reference to a hash with a single lists element
use strict;
use warnings;
use List::Util 'shuffle';
use List::Compare;
my #sets;
my $num_sets = 5 + rand(6);
for (1 .. $num_sets) {
my #set = (shuffle 'A' .. 'Z')[0..16];
push #sets, [ sort #set ];
}
my $lc = List::Compare->new(#sets);
my #overlap = $lc->get_intersection;
print "Sets:\n";
printf " %s\n", join ', ', #$_ for #sets;
print "\n";
print "Intersection:\n";
printf " %s\n", #overlap ? join(', ', #overlap) : 'None';
print "\n";
sample outputs
Sets:
B, C, D, E, F, G, K, L, M, O, P, Q, S, U, V, W, X
B, C, D, F, G, I, J, L, M, P, R, T, U, V, W, X, Y
A, B, C, D, F, G, H, K, L, M, O, R, T, U, V, W, Y
A, B, D, G, H, I, K, L, M, O, R, T, U, V, W, Y, Z
A, B, C, D, E, F, H, J, K, L, M, P, Q, S, U, V, Z
Intersection:
B, D, L, M, U, V
Sets:
A, B, C, D, F, J, K, L, M, N, Q, R, U, V, W, X, Y
A, E, F, G, H, I, J, L, O, P, Q, R, S, T, V, X, Z
B, E, G, H, J, K, L, M, N, P, S, T, U, V, W, Y, Z
B, C, D, E, F, G, H, I, J, N, O, Q, R, T, V, W, Z
A, B, C, E, F, G, H, I, L, N, O, Q, T, U, W, X, Y
Intersection:
None
Update
With regard to the updated code in your question, your identifier $num_sets is wrongly-named as it is the index of the final element in #sets, or one less than the actual number of sets
If you want to use a variable then you should say
my $num_sets = #sets_to_process;
and then loop like this
for my $i ( 0 .. $num_sets-1 ) { ... }
But in this case you don't need the indices at all, and it's probably best to forget about $num_sets and write just this
for my $set ( #sets_to_process ) {
my #set = get_ids_in_list($dbh, $set);
push #sets, \#set;
}
or even just use map like this
my #sets = map [ get_ids_in_list($dbh, $_) ], #sets_to_process;
A hash of (0 or more levels of hash refs of) array refs of hash refs. Note that the level above the leaf nodes will always be array refs, even if they only have one element.
I need to fetch the aggregate sum of VALUE (in an array of array ref) by preserving the order of the hash refs (In the order of insertion).
Examples :
1)
(
A => {
A1 => [
{ VALUE => 10 },
{ VALUE => 20 }
],
B1 => [
{ VALUE => 30 }
],
},
B => {
A1 => [
{ VALUE => 10 }
],
B1 => [
{ VALUE => 5 }
],
},
C => {
A1 => [
{ VALUE => 100 }
],
},
)
The required output of the above structure will be -
(
[A, A1, 30],
[A, B1, 30],
[B, A1, 10],
[B, B1, 5],
.
.
.
.
)
2)
(
A => [
{ VALUE => 10 },
{ VALUE => 20 }
],
B => [
{ VALUE => 30 }
],
)
The required output of the above structure will be -
(
[A, 30],
[B, 30]
)
You need to write a function that will walk your hash structure and compute the necessary sums. For each key in the hash, it needs to make this decision:
If the value of this key is a list ref, then sum up the VALUE elements in the hashes in this list and return [key, sum]
If the value of this hash is a hash ref, then recurse into that hash. If we got a list back from it, append it to our current output and continue.
At the top level (depth 0), print out each list that's returned.
There are a number of details that still need to be resolved, but that ought to get you started on the right track.