File Coverage

blib/lib/Math/Random/Discrete.pm
Criterion Covered Total %
statement 39 39 100.0
branch 7 8 87.5
condition 2 3 66.6
subroutine 4 4 100.0
pod 2 2 100.0
total 54 56 96.4


line stmt bran cond sub pod time code
1             package Math::Random::Discrete;
2             {
3             $Math::Random::Discrete::VERSION = '1.01';
4             }
5 1     1   21748 use strict;
  1         3  
  1         37  
6 1     1   5 use warnings;
  1         2  
  1         425  
7              
8             # ABSTRACT: Discrete random variables with general distributions
9              
10             # This is an implementation of Walker's alias method.
11              
12             sub new {
13 1     1 1 64 my ($class, $_weights, $values) = @_;
14              
15 1         2 my @weights = @$_weights;
16              
17             # compute average weight
18              
19 1         2 my $N = @weights;
20 1         1 my $sum = 0;
21              
22 1         2 for my $weight (@weights) {
23 10         10 $sum += $weight;
24             }
25              
26 1         3 my $avg = $sum / $N;
27              
28             # split weights into two groups: smaller and larger than average
29              
30 1         2 my (@small, @large);
31              
32 1         4 for (my $i = 0; $i < $N; ++$i) {
33 10 100       13 if ($weights[$i] <= $avg) {
34 5         9 push(@small, $i);
35             }
36             else {
37 5         12 push(@large, $i);
38             }
39             }
40              
41             # generate F and A arrays
42              
43 1         2 my (@F, @A);
44              
45 1   66     7 while (@small and @large) {
46 9         9 my $i = pop(@small);
47 9         7 my $j = $large[-1];
48 9         10 $A[$i] = $j;
49 9         13 $F[$i] = $weights[$i] / $avg;
50              
51 9         10 $weights[$j] -= $avg - $weights[$i];
52              
53 9 100       40 push(@small, pop(@large))
54             if $weights[$j] <= $avg;
55             }
56              
57 1         3 for my $i (@small, @large) {
58 1         1 $A[$i] = $i;
59 1         3 $F[$i] = 1.0;
60             }
61              
62             # create blessed ref
63              
64 1         4 my $self = {
65             values => $values,
66             A => \@A,
67             F => \@F,
68             };
69              
70 1         5 return bless($self, $class);
71             }
72              
73             sub rand {
74 55000     55000 1 355345 my $self = shift;
75              
76 55000         76408 my $F = $self->{F};
77 55000         71236 my $r = CORE::rand(@$F);
78 55000         62053 my $ri = int($r); # integer part
79 55000         62527 my $rf = $r - $ri; # fractional part
80              
81 55000 100       103059 my $i = $rf < $F->[$ri] ? $ri : $self->{A}[$ri];
82              
83 55000         76897 my $values = $self->{values};
84              
85 55000 50       158109 return $values ? $values->[$i] : $i;
86             }
87              
88             1;
89              
90             __END__