Search code examples
perlmodulecdf

Cumulative Density Function of a normal distribution calculation without installing a Math module


I would like to calculate the Cumulative Density Function of a normal distribution in Perl. I am using the Math::Gauss module from CPAN which calculates a CDF without any problem.

ttt.pl

#!/usr/bin/perl 

use strict;
use warnings;

use Math::Gauss ':all';

my $x    = 0.1;
my $mean = 0;
my $std  = 0.1;

my $output = cdf($x, $mean, $std);

print $output;

However, I got a module installation problem when I ran the code in a different server as below:

Can't locate Math/Gauss.pm in @INC (@INC contains:
/etc/perl
/usr/local/lib/perl/5.14.2
/usr/local/share/perl/5.14.2
/usr/lib/perl5
/usr/share/perl5
/usr/lib/perl/5.14
/usr/share/perl/5.14
/usr/local/lib/site_perl
. ) at ./ttt.pl line 5.
BEGIN failed--compilation aborted at ./ttt.pl line 5.

I don't have a root authority so I need to install it locally, but I think CDF calculation is not a big calculation. (The CDF equation is simple.)

So it would be great if I knew the way to calculate a CDF in Perl without any installation. Or is there a way to include Math/Gauss.pm in my code so I can use it without installation?


Solution

  • Below is a module based on Math::Gauss.

    # Copyright (C) 2011 by Philipp K. Janert
    # No rights reserved by Eric L. Brine
    #
    # March 13th, 2017 - Eric Brine - Trimmed into light version of the module.
    # 
    # This library is free software; you can redistribute it and/or modify
    # it under the same terms as Perl itself, either Perl version 5.10.1 or,
    # at your option, any later version of Perl 5 you may have available.
    
    package Math::GaussLite;
    
    use strict;
    use warnings;
    
    use Carp;
    use Exporter qw( import );
    
    our @EXPORT_OK = qw( cdf );
    
    my $SQRT2PI = 2.506628274631;
    
    sub pdf {
      my ( $x, $m, $s ) = ( 0, 0, 1 );
      $x = shift if @_;
      $m = shift if @_;
      $s = shift if @_;
    
      if( $s <= 0 ) {
        croak( "Can't evaluate Math::Gauss:pdf for \$s=$s not strictly positive" );
      }
    
      my $z = ($x-$m)/$s;
    
      return exp(-0.5*$z*$z)/($SQRT2PI*$s);
    }
    
    sub cdf {
      my ( $x, $m, $s ) = ( 0, 0, 1 );
      $x = shift if @_;
      $m = shift if @_;
      $s = shift if @_;
    
      # Abramowitz & Stegun, 26.2.17
      # absolute error less than 7.5e-8 for all x
    
      if( $s <= 0 ) {
        croak( "Can't evaluate Math::Gauss:cdf for \$s=$s not strictly positive" );
      }
    
      my $z = ($x-$m)/$s;
    
      my $t = 1.0/(1.0 + 0.2316419*abs($z));
      my $y = $t*(0.319381530
              + $t*(-0.356563782
                + $t*(1.781477937
                  + $t*(-1.821255978
                    + $t*1.330274429 ))));
      if( $z > 0 ) {
        return 1.0 - pdf( $z )*$y;
      } else {
        return pdf( $z )*$y;
      }
    }
    
    1;
    

    (Honestly, since this is really just a formula, the Copyright doesn't provide much protection.)