在實際項目中,遇到需要正態分布算法去計算一個數值在整體的分布區間,例如:
100,90,80,70,60,50,40,30,20,10共10個數,按從高到低的順序排序,總數的10%分布區域為極高頻,總數的30%分布區域為高頻,總數的40%分布區域為中頻,總數的20%分布區域為低頻,比如我新增一個數字88,我如何快速得到新增數字位於那個頻段?以及其他數字有那些數字頻段發生了變化???
代碼實現(計算數值在整體分布的累計概率):
1 <?php 2 /** 3 * php 實現excel的normdist函數 4 * 5 * 使用方法: 6 $list = array(1.09,1.50,1.31,1.44); 7 $normdist = new normdist($list); 8 echo $normdist->getCdf($list[0]); 9 */ 10 class normdist { 11 12 public $list = array(); 13 public $mu; 14 public $sigma; 15 16 public function __construct($list) 17 { 18 $this->list = $list; 19 $this->mu = $this->getMu($list); // 獲取平均值 20 $this->sigma = $this->getSigma($list); // 獲取標准偏差 21 } 22 23 /** 24 * @name 正態分布的累積概率函數 25 * @param string|integer $value 26 * @return number 27 */ 28 public function getCdf($value) 29 { 30 $mu = $this->mu; 31 $sigma = $this->sigma; 32 $t = $value - $mu; 33 $y = 0.5 * $this->erfcc(-$t / ($sigma * sqrt(2.0))); 34 if ($y > 1.0) $y = 1.0; 35 36 return $y; 37 } 38 39 private function erfcc($x) 40 { 41 $z = abs($x); 42 $t = 1. / (1. + 0.5 * $z); 43 $r = 44 $t * exp(-$z*$z-1.26551223+ 45 $t*(1.00002368+ 46 $t*(.37409196+ 47 $t*(.09678418+ 48 $t*(-.18628806+ 49 $t*(.27886807+ 50 $t*(-1.13520398+ 51 $t*(1.48851587+ 52 $t*(-.82215223+ 53 $t*.17087277))))))))); 54 if ($x >= 0.) 55 return $r; 56 else 57 return 2 - $r; 58 } 59 60 /** 61 * @name 獲取平均值 62 * @param array $list 63 * @return number 64 */ 65 private function getMu($list) 66 { 67 return array_sum($list) / count($list); 68 } 69 70 /** 71 * @name 獲取標准差 72 * @param array $list 73 * @return number 74 * @beizhu 標准差 = 方差的平方根 75 */ 76 private function getSigma($list) 77 { 78 $total_var = 0; 79 foreach ($list as $v) { 80 $total_var += pow( ($v - $this->getMu($list)), 2); 81 } 82 83 return sqrt( $total_var / (count($list) - 1 )); // 這里為什么數組元素個數要減去1 84 } 85 }