File Coverage

blib/lib/Math/Random/Zipf.pm
Criterion Covered Total %
statement 46 46 100.0
branch 6 8 75.0
condition 7 18 38.8
subroutine 10 10 100.0
pod 4 7 57.1
total 73 89 82.0


line stmt bran cond sub pod time code
1             package Math::Random::Zipf;
2              
3 2     2   118512 use warnings;
  2         5  
  2         55  
4 2     2   10 use strict;
  2         3  
  2         58  
5 2     2   1692 use POSIX;
  2         23044  
  2         16  
6              
7             our $VERSION = '0.11';
8              
9             sub new {
10 2     2 1 832 my ($class, $N, $exp) = @_;
11              
12 2   33     29 my $self = bless {
13             N => $N,
14             alpha => $exp,
15             cdf => [],
16             pmf => [],
17             }, ref($class) || $class;
18              
19 2         5 my $sum = 0;
20 2         8 for my $k (1 .. $N) {
21 20         91 $sum += ($self->{pmf}[$k-1] = 1/($k ** $exp));
22 20         25 push(@{$self->{cdf}}, $sum);
  20         251  
23             }
24 2         8 my $mult = 1/$sum;
25 2         25 $self->{cdf}->[$_] *= $mult, $self->{pmf}->[$_] *= $mult for (0 .. $N - 1);
26              
27 2         14 return $self;
28             }
29              
30             sub rand {
31 62022     62022 1 286410 my $self = shift;
32 62022   66     269828 my $x = shift || rand();
33              
34 62022         116422 my $tab = $self->{cdf};
35 62022         121442 my $lower = -1;
36 62022         115261 my $upper = @$tab - 1;
37 62022         111400 my $try = -1;
38 62022         114856 my $last_try = -1;
39             # binary search
40 62022         73879 while (1) {
41 299367         890291 my $try = POSIX::floor(($lower + $upper + 1) / 2);
42 299367 100       859510 last if $last_try == $try;
43              
44 237345 100       643560 if ($tab->[$try] >= $x) {
45 174308         316281 $upper = $try;
46             }
47             else {
48 63037         137750 $lower = $try-1;
49             }
50 237345         508571 $last_try = $try;
51             }
52 62022         274231 return $upper + 1;
53             }
54              
55             sub inv_cdf {
56 2     2 0 2835 my ($self, $P) = @_;
57 2         7 return $self->rand($P);
58             }
59              
60             sub pmf {
61 2     2 1 1165 my $self = shift;
62 2         3 my $x = shift;
63              
64 2 50 33     56 return 0 if $x < 1 || $x > $self->{N} || floor($x) != $x;
      33        
65 2         10 return $self->{pmf}->[$x - 1];
66             }
67              
68             sub cdf {
69 2     2 0 3 my $self = shift;
70 2         4 my $x = shift;
71              
72 2 50 33     28 return 0 if $x < 1 || $x > $self->{N} || floor($x) != $x;
      33        
73 2         9 return $self->{cdf}->[$x - 1];
74             }
75            
76             sub pmf_ref {
77 4     4 1 1776 return shift->{pmf};
78             }
79              
80             sub cdf_ref {
81 4     4 0 70 return shift->{cdf};
82             }
83              
84              
85             __END__