More efficiently handle Cartesian AoA products in Perl

I am working on calculating the probability value of two elements in a group with the same value (similar situation with the birthday problem, http://en.wikipedia.org/wiki/Birthday_problem ).

For this, I have 24 sets of three values. Each element in the group will have one value out of 3 from each of 24 sets.

The calculation I need to do is get the sum of the square for all possible iterations of these values.

Such an iteration is obviously very intense, given the necessarily iterative nature.

When entering from SE, I already have:

#!perl;
use List::Util qw(reduce);
use Set::CrossProduct;

my @array = ( ## AoA containing values for caluculation, cut-down to allow benchmarking
#   [0.33, 0.33, 0.33],  x11 more in full set
    [0.33, 0.33, 0.33],
    [0.33, 0.33, 0.33],
    [0.33, 0.33, 0.33],
    [0.33, 0.33, 0.33],
    [0.33, 0.33, 0.33],
    [0.33, 0.33, 0.33],
    [0.33, 0.33, 0.33],
    [0.33, 0.33, 0.33],
    [0.33, 0.33, 0.33],
    [0.33, 0.33, 0.33],
    [0.33, 0.33, 0.33],
    [0.33, 0.33, 0.33],
    [0.33, 0.33, 0.33]
);

$val = 0;
my $iterator = Set::CrossProduct->new(\@array);
while (my $tuple = $iterator->get) {
    $freq = reduce { $a * $b } @$tuple;
    $val += ($freq*$freq);
}

$toprint=sprintf("%.50e", $val);
print $toprint;

13 , , , 45 , 24 . - , . , , ...

Perl, , .

.

EDIT: R, , , , .

+4
1

- . :


- , . 3 ^ 24 = 282+ ., , . , , ( ):


( ) , . .

, :

my ( @baskets, $iter );
push @{ $baskets[ $iter++ % 4 ] }, $_ for $iterator->combinations;

:

sub segment {

  my $num_segments = shift;
  my ( @baskets, $iter );

  push @{ $baskets[ $iter++ % $num_segments ] }, $_ for @_;
  return @baskets;
}

my @jobs = segment( 4, $iterator->combinations );

, (. perldoc perlthrtut , Perl):

use threads;                                            # imports threads module

sub work {                                              # What each thread will run

  my @tuples = @_;

  my $sum;
  for my $tuple ( @tuples ) {

    my $freq = 1;
    $freq *= $_ for @$tuple;
    $sum += $freq * $freq;
  }

  return $sum;
}

my @threads = map threads->new( \&work, @$_ ), @jobs;  # Create and launch threads
                                                       # with different tuple sets

my $grand_total;
$grand_total += $_->join for @threads;                 # Accumulate sub-totals

n 1 ( n)

: . , .

, 2 d.p., 100 ( , ​​). , 24 , , , , ( ). , 3:

[ 0.33, 0.45, 0.22 ], # Tuple A
.
.
.
[ 0.45, 0.22, 0.33 ], # Tuple B

A B $freq. , $freq, $freq "" ( ).

:

my %seen;
for my $tuple ( $iterator->combinations ) {

    my @sorted = sort @$tuple;
    my $tuple_as_string = "@sorted";

    $seen{$tuple_as_string}{count}++;

    next unless exists $seen{$tuple_as_string}{freq};

    my $freq = 1;
    $freq *= $_ for @$tuple;

    $seen{$tuple_as_string}{freq} = $freq;
}


my $grand_total;
for my $unique ( keys %seen ) {

    my $count = $seen{$unique}{count};
    my $freq = $seen{$unique}{freq};
    $grand_total += $count * $freq * $freq;
}

, "" , .

+3

Source: https://habr.com/ru/post/1533949/