package Algorism; use Exporter; @ISA = qw(Exporter); #公開したいサブルーチン @EXPORT = qw(); use strict; use Math::Matrix; use Math::MatrixReal; use Sci qw($pi tanh cosh sinh); my $pi = 3.1415926535; #================================================ # 静的メンバー関数 #================================================ sub InterpolateValuesInCube { my ($nx, $ny, $nz, $pValues, $x, $y, $z) = @_; $x = Utils::Reduce01($x); $y = Utils::Reduce01($y); $z = Utils::Reduce01($z); my $xx = $x * $nx; my $yy = $y * $ny; my $zz = $z * $nz; my $ix = int( $xx + 0.0 ); my $iy = int( $yy + 0.0 ); my $iz = int( $zz + 0.0 ); my ($ix1, $iy1, $iz1) = ($ix+1, $iy+1, $iz+1); $ix1 -= $nx if($ix1 >= $nx); $iy1 -= $ny if($iy1 >= $ny); $iz1 -= $nz if($iz1 >= $nz); my $a0 = $pValues->[$ix][$iy][$iz]; my $ax = $pValues->[$ix1][$iy][$iz] - $a0; my $ay = $pValues->[$ix][$iy1][$iz] - $a0; my $az = $pValues->[$ix][$iy][$iz1] - $a0; my $axy = $pValues->[$ix1][$iy1][$iz] - $a0 - $ax - $ay; my $ayz = $pValues->[$ix][$iy1][$iz1] - $a0 - $ay - $az; my $azx = $pValues->[$ix1][$iy][$iz1] - $a0 - $az - $ax; my $axyz = $pValues->[$ix1][$iy1][$iz1] - $a0 - $ax - $ay - $az - $axy - $ayz - $azx; return $a0 + $ax * ($xx-$ix) + $ay * ($yy-$iy) + $az * ($zz-$iz) + $axy * ($xx-$ix)*($yy-$iy) + $ayz * ($yy-$iy)*($zz-$iz) + $azx * ($zz-$iz)*($xx-$ix) + $axyz * ($xx-$ix)*($yy-$iy)*($zz-$iz); # return $pValues->[$ix][$iy][$iz]; } sub Integrate3DSimple { my ($nx, $ny, $nz, $pValues) = @_; my $S = 0.0; for(my $ix = 0 ; $ix < $nx ; $ix++) { for(my $iy = 0 ; $iy < $ny ; $iy++) { for(my $iz = 0 ; $iz < $nz ; $iz++) { $S += $pValues->[$ix][$iy][$iz]; } } } return $S; } #周期境界条件を考えるときは、単純和に一致する sub Integrate3D { my ($nx, $ny, $nz, $pValues) = @_; my $S = 0.0; # for(my $ix = 0 ; $ix < $nx ; $ix++) { for(my $ix = 0 ; $ix < $nx-1 ; $ix++) { # for(my $iy = 0 ; $iy < $ny ; $iy++) { for(my $iy = 0 ; $iy < $ny-1 ; $iy++) { # for(my $iz = 0 ; $iz < $nz ; $iz++) { for(my $iz = 0 ; $iz < $nz-1 ; $iz++) { my ($ix1, $iy1, $iz1) = ($ix+1, $iy+1, $iz+1); # $ix1 = 0 if($ix1 == $nx); # $iy1 = 0 if($iy1 == $ny); # $iz1 = 0 if($iz1 == $nz); my $a0 = $pValues->[$ix][$iy][$iz]; my $ax = $pValues->[$ix1][$iy][$iz] - $a0; my $ay = $pValues->[$ix][$iy1][$iz] - $a0; my $az = $pValues->[$ix][$iy][$iz1] - $a0; my $axy = $pValues->[$ix1][$iy1][$iz] - $a0 - $ax - $ay; my $ayz = $pValues->[$ix][$iy1][$iz1] - $a0 - $ay - $az; my $azx = $pValues->[$ix1][$iy][$iz1] - $a0 - $az - $ax; my $axyz = $pValues->[$ix1][$iy1][$iz1] - $a0 - $ax - $ay - $az - $axy - $ayz - $azx; #print "a=($a0, $ax, $ay, $az)\n"; $S += $a0 + 0.5 * ($ax + $ay + $az) + 0.25 * ($axy + $ayz + $azx) + 0.125 * $axyz; #print "S=$S\n"; #exit; } } } return $S; } sub ConvolutionByFunc { my ($pX, $pY, $pfunc) = @_; my $nData = @$pX; my $x0 = $pX->[0]; my $x1 = $pX->[$nData - 1]; my $dx = $pX->[1] - $pX->[0]; my @y; #print "dx0,f\n"; for(my $i = 0 ; $i < $nData ; $i++) { my $x0 = $pX->[$i]; my $y0 = $pY->[$i]; for(my $j = 0 ; $j < $nData ; $j++) { my $dx0 = ($i-$j) * $dx; my $f = &$pfunc($dx0); #print "$dx0,$f\n"; $y[$j] += $y0 * $f * $dx; } #exit; } return \@y; } sub Convolution { my ($pX, $pY, $Width, $Function, $IgnoreZeroValue, $Normalize) = @_; $Function = "Gaussian" if(!defined $Function); $IgnoreZeroValue = 0 if(!defined $IgnoreZeroValue); # my $pi = Sci::pi(); my $nData = @$pX; my $x0 = $pX->[0]; my $x1 = $pX->[$nData - 1]; my $dx = $pX->[1] - $pX->[0]; my $di = int(($Width * 5.0) / $dx + 1.1); my $coeff = 1/sqrt($pi)/($Width/$dx); my @y; for(my $j = 0 ; $j < $nData ; $j++) { my $x0 = $pX->[$j]; my $y0 = $pY->[$j]; for(my $k = -$di ; $k <= $di ; $k++) { next if($j+$k < 0 or $j+$k >= $nData); next if($IgnoreZeroValue and $pY->[$j+$k] == 0.0); my $dvx = $dx*$k; $y[$j+$k] += $coeff * $y0 * exp(-$dvx*$dvx/$Width/$Width); } } if($Normalize) { my @S; for(my $j = 0 ; $j < $nData ; $j++) { my $x0 = $pX->[$j]; my $y0 = $pY->[$j]; $S[$j] = 0.0; for(my $k = -$di ; $k <= $di ; $k++) { next if($j+$k < 0 or $j+$k >= $nData); next if($IgnoreZeroValue and $pY->[$j+$k] == 0.0); my $dvx = $dx*$k; $S[$j] += $coeff * exp(-$dvx*$dvx/$Width/$Width); } } for(my $j = 0 ; $j < $nData ; $j++) { next if($S[$j] == 0.0); $y[$j] /= $S[$j]; } } return \@y; } sub minver { my ($A) = @_; # my $A = new Math::MatrixReal(2, 2); # $A->assign(1, 1, $this->{"a11"}); # $A->assign(1, 2, $this->{"a21"}); # $A->assign(2, 1, $this->{"a12"}); # $A->assign(2, 2, $this->{"a22"}); # print "A:
\n";print $A;print "
\n"; return $A->inverse(); # print "RA:
\n";print $RA;print "
\n"; # my $X = new Math::MatrixReal(2,1); # $X->assign(1, 1, $xc); # $X->assign(2, 1, $yc); # print "X:
\n";print $X;print "
\n"; # my $XX = $RA * $X; # print "XX:
\n";print $XX;print "
\n"; } #入力 # $pX->[$i], $pY->[$i], $n : データ # $m : 近似多項式の次数 #出力 # \@c : 多項式のi次の係数へのリファレンス sub lstsq { my ($pX, $pY, $n, $m, $c) = @_; return 999 if($m >= $n or $m < 1); my $nMtx = $m + 1; my $W = new Math::MatrixReal($nMtx, $nMtx); my $V = new Math::MatrixReal($nMtx, 1); for(my $j = 0 ; $j < $m+1 ; $j++) { for(my $k = $j ; $k < $m+1 ; $k++) { my $w = 0.0; for(my $i = 0 ; $i < $n ; $i++) { my $x = $pX->[$i]; my $y = $pY->[$i]; $w += $x ** ($j+$k); } $W->assign($j+1, $k+1, $w); $W->assign($k+1, $j+1, $w); } my $v = 0.0; for(my $i = 0 ; $i < $n ; $i++) { my $x = $pX->[$i]; my $y = $pY->[$i]; $v += $y * $x ** $j; } $V->assign($j+1, 1, $v); } #print "W:\n";print $W;print "\n"; #print "V:\n";print $V;print "\n"; my $RW = $W->inverse(); #print "RW:\n";print $RW;print "\n"; my $XX = $RW * $V; #print "XX:\n";print $XX;print "\n"; for(my $i = 0 ; $i < $m+1 ; $i++) { $c->[$i] = $XX->element($i+1, 1); } return 0; } sub SolveByNewtonRapson { my ($pFunc, $pDiffFunc, $iniX, $dX, $ramda, $Xth, $fth, $nMaxIter, $IsPrint) = @_; my $x = $iniX; my ($Xdiff, $fdiff, $nIter, $mess); my $delta; for(my $i = 0 ; $i < $nMaxIter ; $i++) { my $f1; if($pDiffFunc) { $f1 = &$pDiffFunc($x); #print "f1=$f1\n"; } else { my $f = &$pFunc($x-$dX); my $f2 = &$pFunc($x+$dX); #print "dX=$dX: $f2 - $f = $df\n"; if(defined $f and defined $f2) { my $df = $f2 - $f; $f1 = $df / 2.0 / $dX; } } if(!defined $f1) { return (undef, undef, undef, undef, "function did not return a value"); } my $f = &$pFunc($x); if($f1 < 0.0) { $f1 -= $ramda; } else { $f1 += $ramda; } $delta = -$f / $f1; print "$i: x = $x => " if($IsPrint); $x += $delta; print "$x ($delta)\n" if($IsPrint); if($Xth > 0 and abs($delta) < $Xth) { $nIter = $i+1; $fdiff = &$pFunc($x); $mess = "converged for X"; return ($x, $delta, $fdiff, $nIter, $mess); } if($fth > 0 and abs($f) < $fth) { $nIter = $i+1; $fdiff = &$pFunc($x); $mess = "converged for f"; return ($x, $delta, $fdiff, $nIter, $mess); } } $mess = "Error: not converged"; return (undef, $delta, undef, $nMaxIter, $mess); } sub SolveBySecant { my ($pFunc, $iniX, $initialdX, $ramda, $Xth, $fth, $nMaxIter, $IsPrint) = @_; my $x = $iniX; my ($Xdiff, $fdiff, $nIter, $mess); my $fprev = &$pFunc($x); my $dX = $initialdX; $x += $dX; for(my $i = 0 ; $i < $nMaxIter ; $i++) { my $f2 = &$pFunc($x); #print "dX=$dX: $f2 - $f = $df\n"; if(!defined $f2) { return (undef, undef, undef, undef, "function did not return a value"); } my $df = $f2 - $fprev; my $f1 = $df / $dX; if($f1 < 0.0) { $f1 -= $ramda; } else { $f1 += $ramda; } $dX = -$f2 / $f1; print "$i: x = $x => " if($IsPrint); $x += $dX; print "$x ($dX)\n" if($IsPrint); if($Xth > 0 and abs($dX) < $Xth) { $nIter = $i+1; $fdiff = &$pFunc($x); $mess = "converged for X"; return ($x, $dX, $fdiff, $nIter, $mess); } if($fth > 0 and abs($f2) < $fth) { $nIter = $i+1; $fdiff = &$pFunc($x); $mess = "converged for f"; return ($x, $dX, $fdiff, $nIter, $mess); } $fprev = $f2; } $mess = "Error: not converged"; return (undef, $dX, undef, $nMaxIter, $mess); } sub SolveByBisection { my ($pFunc, $x0, $x1, $Xth, $fth, $nMaxIter, $IsPrint) = @_; my $mess = 'converged'; my $fmin = &$pFunc($x0); my $fmax = &$pFunc($x1); if($fmin * $fmax > 0.0) { #print "f(x0)=$fmin and f(x1)=$fmax have same sign.\n" if($IsPrint); $mess = "Error: f(x0) and f(x1) have the same sign\n"; return (undef, undef, undef, undef, $mess); } for(my $i = 0 ; $i < $nMaxIter ; $i++) { my $dx = abs($x1 - $x0); #print "$i: $dx\n" if($IsPrint); return (($x0+$x1)/2.0, $dx, ($fmin+$fmax)/2.0, $i, $mess) if(abs($dx) < $Xth); my $xhalf = ($x0 + $x1) / 2.0; my $fhalf = &$pFunc($xhalf); #print "$i: E: $x0 - $xhalf - $x1: $fmin - $fhalf - $fmax\n" if($IsPrint); print "$i: $x0 - $xhalf - $x1: f=$fhalf\n" if($IsPrint); return (($x0+$x1)/2.0, $dx, $fhalf, $i, $mess) if(abs($fhalf) < $fth); if($fmin * $fhalf < 0.0) { $x1 = $xhalf; $fmax = $fhalf; } else { $x0 = $xhalf; $fmin = $fhalf; } } $mess = "Error: not converged"; return (undef, undef, undef, $nMaxIter, $mess); } sub SolveByNewton { my ($func, $iniX, $dX, $Xth, $fth, $nMaxIter) = @_; my $x = $iniX; for(my $i = 0 ; $i < $nMaxIter ; $i++) { my $curx = $x; my $f = eval($func); #print "$i: x=$x: y=$f\n"; $x += $dX; my $f2 = eval($func); my $df = $f2-$f; my $f1 = $df / $dX; my $delta = -$f / $f1; #print " $f-$f2=$f1: dX=$delta\n"; #print " x:$curx d:$delta\n"; $x = $curx + $delta; #print " x:$x (new)\n"; #print " dx:$delta ($Xth) df:$df ($fth)\n"; last if($Xth > 0 and abs($delta) < $Xth); last if($fth > 0 and abs($df) < $fth); } return $x; } sub IsFactor { my ($I, $idiv) = @_; return 0 if($idiv <= 0); my $res = $I - int($I / $idiv) * $idiv; return 1 if(abs($res) < 1.0e-10); return 0; } sub Factorize { my ($I) = @_; my $iMax = $I / 2; my @check; for(my $i = 0 ; $i <= $iMax ; $i++) { $check[$i] = 0; } my @e; my $ic = 2; while(1) { last if($ic > $iMax); if($check[$ic]) { $ic++; next; } if(IsFactor($I, $ic)) { push(@e, $ic); $I = $I / $ic; next; } for(my $j = 1 ; ; $j++) { last if($ic*$j > $I/2); $check[$ic*$j] = 1; } $ic++; } push(@e, $I) if(@e == 0); return @e; } sub SmoothingBySimpleAverage { my ($pY, $n) = @_; my $n2 = int($n / 2); my $nData = @$pY; my @y; for(my $i = 0 ; $i < $nData ; $i++) { my $c = 0; $y[$i] = 0.0; for(my $k = $i - $n2 ; $k <= $i + $n2 ; $k++) { next if($k < 0 or $k >= $nData); $y[$i] += $pY->[$k]; $c++; } if($c > 0) { $y[$i] /= $c; } else { $y[$i] = $pY->[$i]; } } return \@y; } sub AdaptiveSmoothing { my ($pY, $n) = @_; my $m = int($n / 2); my $W23 = (4.0 * $m * $m - 1.0) * (2.0 * $m + 3.0) / 3.0; my @w23j; for(my $j = -$m ; $j <= $m ; $j++) { $w23j[$j + $m] = (3.0 * $m * ($m+1.0) - 1.0 - 5.0 * $j * $j) / $W23; } my $nData = @$pY; my @y; for(my $i = 0 ; $i < $nData ; $i++) { my $c = 0.0; $y[$i] = 0.0; for(my $j = -$m ; $j <= $m ; $j++) { my $k = $i + $j; next if($k < 0 or $k >= $nData); $y[$i] += $w23j[$j+$m] * $pY->[$k]; $c += $w23j[$j+$m]; } if($c > 0) { $y[$i] /= $c; } else { $y[$i] = $pY->[$i]; } } return \@y; } sub SmoothingBymLSQ { my ($pX, $pY, $nSmooth, $nOrder, $iPrintLevel) = @_; my (@wx, @wy, @x, @y); my $nData = @$pX; my $nhalf = int($nSmooth/2); my $c = 0; for(my $i = $nhalf ; $i < $nData-$nhalf ; $i++) { my $Opt = new Optimize; @wx = (); @wy = (); for(my $j = 0 ; $j < $nSmooth ; $j++) { $wx[$j] = $pX->[$i-$nhalf + $j]; $wy[$j] = $pY->[$i-$nhalf + $j]; } my ($pCi, $S2) = $Opt->Optimize("mlsq", \@wx, \@wy, $nOrder, $iPrintLevel); #print "Optimized at (", join(',',@{$pCi}), ") with S2=$S2\n"; $x[$c] = $pX->[$i]; $y[$c] = $Opt->YCal($x[$c]); $c++; #print "$x\t$y[$i]\t$ycal\n"; } return (\@x, \@y); } sub SmoothingByFFT { my ($pY, $imin, $imax, $IsPrint) = @_; my $fft = new MyFFT; my ($n, $pX, $pData) = $fft->SetData(undef, $pY, undef, "forward:real", "truncate"); my ($pFreq, $pReal, $pImag) = $fft->FFT(); #print "n(forward)=$n\n"; my (@real, @image); for(my $i = 0 ; $i < @$pFreq ; $i++) { my $x = $pFreq->[$i]; if($i < $imin or $imax < $i) { # if($x < $k0 or $k1 < $x) { $real[$i] = 0.0; $image[$i] = 0.0; } else { $real[$i] = $pReal->[$i]; $image[$i] = $pImag->[$i]; } } ($n, $pX, $pData) = $fft->SetData(undef, \@real, \@image, "inverse:real", "truncate"); my ($pTime, $pOrgReal) = $fft->FFT(); #print "n(inverse)=$n\n"; return ($pFreq, $pReal, $pImag, $pTime, $pOrgReal); } sub IntegrateByRectangle { my ($pX, $pY) = @_; my $nData = @$pX; return undef if($nData < 2); my @y; $y[0] = 0.0; #0.5 * ($pX->[1] - $pX->[0]) * $pY->[0]; for(my $i = 1 ; $i < $nData ; $i++) { $y[$i] = $y[$i-1] + ($pX->[$i] - $pX->[$i-1]) * $pY->[$i-1]; } $y[$nData-1] -= 0.5 * ($pX->[$nData-1] - $pX->[$nData-2]) * $pY->[$nData-1]; return \@y; } sub IntegrateByRectangleWithFunction { my ($pFunc, $x0, $x1, $nBlock) = @_; return undef if($nBlock < 1); my $h = ($x1 - $x0) / $nBlock; my $S = 0.0; for(my $i = 0 ; $i <= $nBlock-1 ; $i++) { my $x = $x0 + $i * $h; my $y = &$pFunc($x); $S += $y; } return $S * $h; } sub IntegrateByTrapezoid { my ($pX, $pY, $idx0, $idx1) = @_; my $nData = @$pX; my $nYData = @$pY; return undef if($nData < 2); $idx0 = 0 if(!defined $idx0); $idx1 = $nData if(!defined $idx1); my @y; for(my $i = 0 ; $i < $idx0 ; $i++) { $y[$i] = 0.0; } $y[$idx0] = 0.0; #0.5 * ($pX->[$idx0+1] - $pX->[$idx0]) * $pY->[$idx0]; $y[$idx0+1] = 0.5 * ($pX->[$idx0+1] - $pX->[$idx0]) * ($pY->[$idx0+1] + $pY->[$idx0]); for(my $i = $idx0+2 ; $i < $idx1 ; $i++) { #print "i=$idx0, $idx1, $i (n=$nData, $nYData, p=$pX, $pY)\n"; #print "eq: [y[i] = $y[$i-1] + 0.5 * ($pX->[$i] - $pX->[$i-1]) * ($pY->[$i] + $pY->[$i-1]);]\n"; $y[$i] = $y[$i-1] + 0.5 * ($pX->[$i] - $pX->[$i-1]) * ($pY->[$i] + $pY->[$i-1]); #exit if(!defined $y[$i]); } my $last = $y[$idx1-1]; for(my $i = $idx1 ; $i < $nData ; $i++) { $y[$i] = $last; } return \@y; } sub IntegrateByTrapezoidWithFunction_NotUsed { my ($pFunc, $x0, $x1, $nBlock) = @_; return undef if($nBlock < 1); my $h = ($x1 - $x0) / $nBlock; my $S = 0.0; for(my $i = 0 ; $i <= $nBlock-1 ; $i++) { my $xi = $x0 + $i * $h; my $xi1 = $xi + $h; $S += &$pFunc($xi) + &$pFunc($xi1); } return $S / 2.0 * $h; } sub IntegrateByTrapezoidWithFunction { my ($pFunc, $x0, $x1, $nBlock) = @_; return undef if($nBlock < 1); my $h = ($x1 - $x0) / $nBlock; my $S = 0.5 * (&$pFunc($x0) + &$pFunc($x1)); for(my $i = 1 ; $i <= $nBlock-1 ; $i++) { my $x = $x0 + $i * $h; my $y = &$pFunc($x); $S += $y; } return $S * $h; } sub IntegrateBySimpsonWithFunction_NotUsed { my ($pFunc, $x0, $x1, $nBlock) = @_; return undef if($nBlock < 2 or $nBlock %2 != 0); my $h = ($x1 - $x0) / $nBlock; my $S = 0.0; for(my $i = 0 ; $i <= $nBlock-2 ; $i += 2) { my $xi = $x0 + $i * $h; my $xi1 = $xi + $h; my $xi2 = $xi1 + $h; $S += &$pFunc($xi) + 4.0 * &$pFunc($xi1) + &$pFunc($xi2);; } return $S / 3.0 * $h; } sub IntegrateBySimpsonWithFunction { my ($pFunc, $x0, $x1, $nBlock) = @_; return undef if($nBlock < 2 or $nBlock %2 != 0); my $h = ($x1 - $x0) / $nBlock; my $S = &$pFunc($x0) + &$pFunc($x1); my $S4 = 0.0; for(my $i = 1 ; $i <= $nBlock-1 ; $i += 2) { my $x = $x0 + $i * $h; my $y = &$pFunc($x); $S4 += $y; } my $S2 = 0.0; for(my $i = 2 ; $i <= $nBlock-2 ; $i += 2) { my $x = $x0 + $i * $h; my $y = &$pFunc($x); $S2 += $y; } return ($S + 4.0 * $S4 + 2.0 * $S2) / 3.0 * $h; } sub SinglePointIntegrateByConstantStepSimpson { my ($nData, $h, $pY) = @_; if($nData < 3 or $nData % 2 == 0 or $h <= 0.0) { return undef; } my $w = 0.0; for(my $i = 1 ; $i < $nData-1 ; $i += 2) { $w += $pY->[$i-1] + 4.0 * $pY->[$i] + $pY->[$i+1]; } my $S = $w * $h / 3.0; return $S; } sub SinglePointIntegrateBySimpson { my ($pX, $pY) = @_; my $nData = @$pX; if($nData < 3 or $nData % 2 == 0) { return undef; } my $w = 0.0; for(my $i = 1 ; $i < $nData-1 ; $i += 2) { my $h = 0.5 * ($pX->[$i+1] - $pX->[$i-1]); my $hpd = 1.0 / ($pX->[$i] - $pX->[$i-1]); my $hmd = 1.0 / ($pX->[$i+1] - $pX->[$i]); my $d = $pX->[$i] - $pX->[$i-1] - $h; $w += ( (1.0 + 2.0*$d*$hpd) * $pY->[$i-1] + 2.0*$h*($hpd+$hmd) * $pY->[$i] + (1.0 - 2.0*$d*$hmd) * $pY->[$i+1] ) * $h; } my $S = $w / 3.0; return $S; } sub IntegrateByConstantStepSimpson { my ($nData, $h, $pY) = @_; return undef if($nData < 2); my @y; $y[0] = 0.0; $y[1] = $h * $pY->[0]; for(my $i = 1 ; $i < $nData-1 ; $i++) { my @yy = ($pY->[$i-1], $pY->[$i], $pY->[$i+1]); my $S = SinglePointIntegrateByConstantStepSimpson(3, $h, \@yy); $y[$i+1] = $y[$i-1] + $S; } return \@y; } sub IntegrateBySimpson { my ($pX, $pY) = @_; my $nData = @$pX; return undef if($nData < 2); my @y; $y[0] = 0.0; $y[1] = ($pX->[1] - $pX->[0]) * $pY->[0]; for(my $i = 1 ; $i < $nData-1 ; $i++) { my @xx = ($pX->[$i-1], $pX->[$i], $pX->[$i+1]); my @yy = ($pY->[$i-1], $pY->[$i], $pY->[$i+1]); my $S = SinglePointIntegrateBySimpson(\@xx, \@yy); $y[$i+1] = $y[$i-1] + $S; } return \@y; } sub IntegrateBySimpson38WithFunction { my ($pFunc, $x0, $x1, $nBlock) = @_; return undef if($nBlock < 3 or $nBlock % 3 != 0); my $h = ($x1 - $x0) / $nBlock; my $S = 0.0; for(my $i = 0 ; $i <= $nBlock-3 ; $i += 3) { my $xi = $x0 + $i * $h; my $xi1 = $xi + $h; my $xi2 = $xi1 + $h; my $xi3 = $xi2 + $h; $S += 3.0 * &$pFunc($xi) + 9.0 * &$pFunc($xi1) + 9.0 * &$pFunc($xi2) + 3.0 * &$pFunc($xi3); # $S += 3.0 * &$pFunc($xi) + 9.0 * &$pFunc($xi) + 9.0 * &$pFunc($xi) + 3.0 * &$pFunc($xi); # $S += 24.0 * &$pFunc($xi);# + 9.0 * &$pFunc($xi) + 9.0 * &$pFunc($xi) + 3.0 * &$pFunc($xi); } return $S / 8.0 * $h; } sub IntegrateByBodeWithFunction { my ($pFunc, $x0, $x1, $nBlock) = @_; return undef if($nBlock < 4 or $nBlock % 4 != 0); my $h = ($x1 - $x0) / $nBlock; my $S = 0.0; for(my $i = 0 ; $i <= $nBlock-4 ; $i += 4) { my $xi = $x0 + $i * $h; my $xi1 = $xi + $h; my $xi2 = $xi1 + $h; my $xi3 = $xi2 + $h; my $xi4 = $xi3 + $h; $S += 14.0 * &$pFunc($xi) + 64.0 * &$pFunc($xi1) + 24.0 * &$pFunc($xi2) + 64.0 * &$pFunc($xi3) + 14.0 * &$pFunc($xi4); } return $S / 45.0 * $h; } sub IntegrateByConstantStepBode { my ($nData, $h, $pY) = @_; return undef if($nData < 2); my $py03 = IntegrateBySimpson([0.0, $h, 2.0*$h, 3.0*$h], [@$pY[0..4]]); my @y = @$py03; $h /= 45.0; for(my $i = 0 ; $i < $nData-4 ; $i++) { my $sum = 14.0 * $pY->[$i] + 64.0 * $pY->[$i+1] + 24.0 * $pY->[$i+2] + 64.0 * $pY->[$i+3] + 14.0 * $pY->[$i+4]; $y[$i+4] = $y[$i] + $sum * $h; } return \@y; } sub IntegrateByRombergFunc { my ($x) = @_; return exp(-($x**2)); # return sqrt(1.0 - $x*$x); } sub IntegrateByRomberg { my ($pFunc, $x0, $x1, $nMax, $EPS, $IsPrint) = @_; my $y = &$pFunc($x0); print "y($x0)=$y\n" if($IsPrint); my @S; $S[0] = []; my $n = 1; my $h = $x1 - $x0; my $y0 = &$pFunc($x0); my $y1 = &$pFunc($x1); $S[0][0] = ($y0 + $y1) / 2.0 * $h; print "S00 = $S[0][0]\n" if($IsPrint); my $kfin; for(my $k = 1 ; $k < $nMax ; $k++) { $n *= 2; print "k=$k (n=$n)\n" if($IsPrint); $h = ($x1 - $x0) / $n; # 台形則の計算 $S[$k][0] = 0.0; for(my $i = 0 ; $i < $n ; $i++) { my $xi = $x0 + $i * $h; #print "i=$i, xi=$xi\n"; my $xi1 = $xi + $h; my $yi = &$pFunc($xi); my $yi1 = &$pFunc($xi1); $S[$k][0] += ($yi + $yi1) / 2.0 * $h; } print " S[$k][0] = $S[$k][0]\n" if($IsPrint); my $d4 = 4.0; for(my $d = 1 ; $d <= $k ; $d++) { #print " d=$d\n"; $S[$k][$d] = ($d4 * $S[$k][$d-1] - $S[$k-1][$d-1]) / ($d4 - 1.0); print " S[$k][$d] = $S[$k][$d]\n" if($IsPrint); $d4 *= 4.0; } my $delta = abs($S[$k][$k] - $S[$k-1][$k-1]); if($delta < $EPS) { $kfin = $k; last; } } my $mess; if(!defined $kfin) { $mess = "Not converged"; $kfin = $nMax-1; } my $Sfin = $S[$kfin][$kfin]; my $diff = ($kfin >= 1)? $Sfin - $S[$kfin-1][$kfin-1] : undef; return ($Sfin, $n, $kfin, $diff, $mess); } sub IntegrateByIMTWithFunction { my ($pFunc, $x0, $x1, $nBlock, $nGL, $nGLOrder) = @_; $nGL = 2 if(!defined $nGL); $nGLOrder = 5 if(!defined $nGLOrder); return undef if($nBlock < 2); # x = ($x1 - $x0) * x' + $x0)] # [x = $x0, $x1] => [x' = 0, 1] # S= Int(x, $x0, $x1) = ($x1 - $x0) * Int(x', 0, 1) my $Q = 0.00702985840661; # = int(exp(-1/t - 1/(1-t)) my $h = 1.0 / $nBlock; my $S = 0.0; for(my $i = 1 ; $i <= $nBlock-1 ; $i++) { # my $ui = $i * $h; # my $xi = ($x1 - $x0) * $ui + $x0; # my $yi = &$pFunc($xi); ## phi = exp(-1.0 / $ui - 1.0 / (1.0 - $ui)) # my $phi = exp(-1.0 / $ui - 1.0 / (1.0 - $ui)); ## phi' = [1/$ui/$ui - 1/(1-$ui)^2] * phi # my $ui1m = 1.0 - $ui; # my $phi1 = (1.0/$ui/$ui - 1.0/$ui1m/$ui1m) * $phi; # # $S += $yi * $phi1; my $ui = $i * $h; my $phiui = Algorism::IntegrateByGaussLegendreWithFunction( sub { my ($x) = @_; return exp(-1.0 / $x - 1.0 / (1.0-$x)) / $Q; }, 0.0, $ui, $nGL, $nGLOrder); my $xi = ($x1 - $x0) * $phiui + $x0; my $yi = &$pFunc($xi); #print "i=$i: ui=$ui => phi(u)=$phiui => xi=$xi\n"; $S += $yi * exp(-1.0 / $ui - 1.0 / (1.0 - $ui)); } return $S / $Q * $h * ($x1 - $x0); } sub IntegrateByDoubleIntegralWithFunction { my ($pFunc, $x0, $x1, $nBlock, $u0, $u1) = @_; $u0 = -4.0 if(!defined $u0); $u1 = 4.0 if(!defined $u1); return undef if($nBlock < 2); # x = [($x1 - $x0) * x'+ ($x0 + $x1)] / 2.0 # [x = $x0, $x1] => [x' = -1, 1] # S= Int(x, $x0, $x1) = ($x1 - $x0) / 2.0 * Int(x', -1, 1) my $h = ($u1 - $u0) / $nBlock; my $S = 0.0; for(my $i = 0 ; $i <= $nBlock ; $i++) { my $ui = $u0 + $i * $h; my $phiui = tanh($pi / 2.0 * sinh($ui)); my $xi = (($x1 - $x0) * $phiui + $x0 + $x1) / 2.0; #print "i=$i: ui=$ui => phi(u)=$phiui => xi=$xi\n"; my $cosh = cosh($ui); my $sinh = sinh($ui); my $coshsinh = cosh($pi / 2.0 * sinh($ui)); $S += &$pFunc($xi) * $cosh / ($coshsinh * $coshsinh); } return ($pi / 2.0) * $S * $h * ($x1 - $x0) / 2.0; } sub IntegrateByCubicSplineWithFunction { my ($pFunc, $x0, $x1, $nBlock) = @_; return undef if($nBlock < 4); my (@x, @y); my $h = ($x1 - $x0) / $nBlock; for(my $i = 0 ; $i <= $nBlock ; $i++) { $x[$i] = $x0 + $i * $h; $y[$i] = &$pFunc($x[$i]); } return Algorism::IntegrateByCubicSpline(\@x, \@y); } sub IntegrateByCubicSpline { my ($pX, $pY) = @_; my $nData = @$pX; if($nData < 4) { return undef; } for(my $i = 0 ; $i < $nData-1 ; $i++) { if($pX->[$i+1] <= $pX->[$i]) { return undef; } } my ($ph, $psp) = subspl($pX, $pY); my $w = 0.0; for(my $i = 1 ; $i < $nData ; $i++) { my $hi = $ph->[$i]; my $hi2 = $hi * $hi; $w += $hi * ( $pY->[$i] + $pY->[$i-1] - $hi2 * ($psp->[$i-1] + $psp->[$i]) * 8.3333333333333e-2); } return $w * 0.5; } my %ZPGaussLegendreIntegration = ( '2' => [-1.0 / sqrt(3), + 1.0 / sqrt(3)], '3' => [-sqrt(3.0/5.0), 0.0, +sqrt(3.0/5.0)], '4' => [-0.861136311594052, -0.339981043584856, +0.339981043584856, +0.861136311594052], # '5' => [-0.906179845938663, -0.538469310105683, 0.0, +0.538469310105683, +0.906179845938663], '5' => [-0.906179845938663992797626878299, -0.538469310105683091036314420700, 0.0, +0.538469310105683091036314420700, +0.906179845938663992797626878299], ); my %WGaussLegendreIntegration = ( '2' => [1.0, 1.0], '3' => [5.0/9.0, 8.0/9.0, 5.0/9.0], '4' => [0.347854845137453, 0.652145154862546, 0.652145154862546, 0.347854845137453], # '5' => [0.236226885056189, 0.478628670499366, 0.568888888888889, 0.478628670499366, 0.236226885056189], '5' => [0.236926885056189087514264040719, 0.478628670499366468041291514835, 0.568888888888888888888888888888, 0.478628670499366468041291514835, 0.236926885056189087514264040719] ); #4 点公式 #?0.861136311594052575223946488892 0.347854845137453857373063949221 #?0.339981043584856264802665759103 0.652145154862546142626936050778 #+0.339981043584856264802665759103 0.652145154862546142626936050778 #+0.861136311594052575223946488892 0.347854845137453857373063949221 #5 点公式 #?0.906179845938663992797626878299 0.236926885056189087514264040719 #?0.538469310105683091036314420700 0.478628670499366468041291514835 #0 0.568888888888888888888888888888 #+0.538469310105683091036314420700 0.478628670499366468041291514835 #+0.906179845938663992797626878299 0.236926885056189087514264040719 #6 点公式 #?0.932469514203152027812301554493 0.171324492379170345040296142172 #?0.661209386466264513661399595019 0.360761573048438607569833513837 #?0.238619186093196908630501721680 0.467913934572691047389870343989 #+0.238619186093196908630501721680 0.467913934572691047389870343989 #+0.661209386466264513661399595019 0.360761573048438607569833513837 #+0.932469514203152027812301554493 0.171324492379170345040296142172 #7 点公式 #?0.949107912342758524526189684047 0.129484966168869693270611432679 #?0.741531185599394439863864773280 0.279705391489276667901467771423 #?0.405845151377397166906606412076 0.381830050505118944950369775488 #0 0.417959183673469387755102040816 #+0.405845151377397166906606412076 0.381830050505118944950369775488 #+0.741531185599394439863864773280 0.279705391489276667901467771423 #+0.949107912342758524526189684047 0.129484966168869693270611432679 sub IntegrateByGaussLegendreWithFunction { my ($pFunc, $x0, $x1, $nBlock, $nOrder, $pZP, $pW) = @_; return undef if($nBlock < 1); if(!defined $pZP) { $pZP = $ZPGaussLegendreIntegration{"$nOrder"}; $pW = $WGaussLegendreIntegration{"$nOrder"}; } if(!defined $pZP or !defined $pW) { print "Error in Algorism::IntegrateByGaussLegendreWithFunction: Can not find zero points and weights for order [$nOrder].\n"; return undef; } # if($nOrder == 5) { # print "\n\nWarning: Algorism::IntegrateByGaussLegendreWithFunction: G-L integration with nOrder=5 is not guaranteed.\n"; # print " The acuracy of the zero points and/or weight must be re-examined.\n\n\n"; # # } #for(my $i = 0 ; $i < $nOrder ; $i++) { # print "$i: Zero at $pZP->[$i]\tw=$pW->[$i]\n"; #} my $h = ($x1 - $x0) / $nBlock; my $h2 = $h / 2.0; my $S = 0.0; for(my $i = 0 ; $i <= $nBlock-1 ; $i++) { my $xc = $x0 + ($i + 0.5) * $h; for(my $j = 0 ; $j < $nOrder ; $j++) { my $xj = $xc + $pZP->[$j] * $h2; #print "i=$i: j=$j: xc=$xc: xj=$xj (ZP=$pZP->[$j])\n"; $S += $pW->[$j] * &$pFunc($xj); } } #print "S=$S\n"; #exit; return $S * $h2; } sub Differentiate2 { my ($pFunc, $x, $h) = @_; return (&$pFunc($x + $h) - &$pFunc($x)) / $h; } sub Differentiate3 { my ($pFunc, $x, $h) = @_; return (&$pFunc($x + $h) - &$pFunc($x - $h)) / 2.0 / $h; } sub Differentiate5 { my ($pFunc, $x, $h) = @_; return (-&$pFunc($x + 2*$h) + 8*&$pFunc($x + $h) - 8*&$pFunc($x - $h) + &$pFunc($x - 2*$h)) / 12.0 / $h; } sub Differentiate7 { my ($pFunc, $x, $h) = @_; return (&$pFunc($x + 3*$h) - 9*&$pFunc($x + 2*$h) + 45*&$pFunc($x + $h) - 45*&$pFunc($x - $h) + 9*&$pFunc($x - 2*$h) - &$pFunc($x - 3*$h)) / 60.0 / $h; } sub Differentiate7WithIndex { my ($pY, $i0, $h) = @_; return ($pY->[$i0 + 3] - 9*$pY->[$i0 + 2] + 45*$pY->[$i0 + 1] - 45*$pY->[$i0 - 1] + 9*$pY->[$i0 - 2] - $pY->[$i0 - 3]) / 60.0 / $h; } sub DifferentiateByLinearAverage { my ($pX, $pY) = @_; my $nData = @$pX; return undef if($nData < 2); my @y; $y[0] = ($pY->[1] - $pY->[0]) / ($pX->[1] - $pX->[0]); $y[$nData-1] = ($pY->[$nData-1] - $pY->[$nData-2]) / ($pX->[$nData-1] - $pX->[$nData-2]); for(my $i = 1 ; $i < $nData-1 ; $i++) { my $d1 = ($pY->[$i] - $pY->[$i-1]) / ($pX->[$i] - $pX->[$i-1]); my $d2 = ($pY->[$i+1] - $pY->[$i] ) / ($pX->[$i+1] - $pX->[$i] ); $y[$i] = ($d1 + $d2) * 0.5; } return \@y; } sub DifferentiateByLagrange { my ($pX, $pY, $x) = @_; my $nData = @$pX; if($nData < 2 or $x < $pX->[0] or $x >= $pX->[$nData-1]) { return undef; } for(my $i = 0 ; $i < $nData-1 ; $i++) { if($pX->[$i+1] <= $pX->[$i]) { return undef; } } my $w = 0.0; for(my $i = 0 ; $i < $nData ; $i++) { my $sm0 = 0.0; my $sm2 = 1.0; my $xi = $pX->[$i]; for(my $j = 0 ; $j < $nData ; $j++) { if($i != $j) { $sm2 *= ($xi - $pX->[$j]); my $sm1 = 1.0; for(my $k = 0 ; $k < $nData ; $k++) { if($k != $i and $k != $j) { $sm1 *= ($x - $pX->[$k]); } } $sm0 += $sm1; } } $w += $pY->[$i] * $sm0 / $sm2; } return $w; } sub DifferentiateByCubicSpline { my ($pX, $pY, $x) = @_; my $nData = @$pX; if($nData < 2 or $x < $pX->[0] or $x >= $pX->[$nData-1]) { return undef; } for(my $i = 0 ; $i < $nData-1 ; $i++) { if($pX->[$i+1] <= $pX->[$i]) { return undef; } } my ($ph, $psp) = subspl($pX, $pY); for(my $i = 1 ; $i < $nData ; $i++) { if($pX->[$i-1] <= $x and $x <= $pX->[$i]) { my $hi = $ph->[$i]; my $hi2 = $hi * $hi; my $dxp2 = ($pX->[$i] - $x) * ($pX->[$i] - $x); my $dxm2 = ($x - $pX->[$i-1]) * ($x - $pX->[$i-1]); my $diff = ($psp->[$i-1] * ($hi2 - 3.0 * $dxp2) + $psp->[$i] * (3.0 * $dxm2 - $hi2) + 6.0 * ($pY->[$i] - $pY->[$i-1]) ) / (6.0 * $hi); return $diff; } } return undef; } sub DifferentiateByRichardsonExtrapolation { my ($pFunc, $x, $h0, $nMax, $EPS, $IsPrint) = @_; my $h = $h0; my @D; my $delta; my $mfin; $D[0][0] = (&$pFunc($x + $h) - &$pFunc($x - $h)) / 2.0 / $h; print "D00: $D[0][0]\n" if($IsPrint); for(my $m = 1 ; $m <= $nMax ; $m++) { $h *= 0.5; $D[0][$m] = (&$pFunc($x + $h) - &$pFunc($x - $h)) / 2.0 / $h; print "D0$m: $D[0][$m]\n" if($IsPrint); for(my $k = $m-1 ; $k >= 0 ; $k--) { my $m1 = $m - $k; $D[$m1][$k] = (4**$m1 * $D[$m1-1][$k+1] - $D[$m1-1][$k]) / (4**$m1 - 1); print "D$m1$k: $D[$m1][$k]\n" if($IsPrint); } $delta = abs($D[$m][0] - $D[$m-1][0]); if($delta < $EPS) { $mfin = $m; last; } } if(!$mfin) { $mfin = $nMax; } return ($D[$nMax][0], $mfin, $h0 * (0.5**$mfin), $delta, \@D); } sub DifferentiateByRichardsonExtrapolation_old { my ($pFunc, $x, $h0, $nMax, $EPS, $IsPrint) = @_; my $h = $h0; my @D; my $delta; for(my $k = 0 ; $k <= $nMax ; $k++) { $D[0][$k] = (&$pFunc($x + $h) - &$pFunc($x - $h)) / 2.0 / $h; print "D0$k: $D[0][$k]\n" if($IsPrint); $h /= 2.0; } my $mfin; print "D00: $D[0][0]\n" if($IsPrint); for(my $m = 1 ; $m <= $nMax ; $m++) { for(my $k = 0 ; $k <= $nMax-$m ; $k++) { $D[$m][$k] = (4**$m * $D[$m-1][$k+1] - $D[$m-1][$k]) / (4**$m - 1); print "D$m$k: $D[$m][$k]\n" if($IsPrint); } $delta = abs($D[$m][0] - $D[$m-1][0]); if($delta < $EPS) { $mfin = $m; last; } } if(!$mfin) { $mfin = $nMax; } return ($D[$nMax][0], $mfin, $h0 * (0.5**$mfin), $delta, \@D); } sub FindIndexByBinarySearch { my ($pX, $x) = @_; return 0 if($x <= $pX->[0]); my $nData = @$pX; return $nData - 1 if($pX->[$nData-1] <= $x); my $idx0 = 0; my $idx1 = $nData - 1; my $x0 = $pX->[$idx0]; my $x1 = $pX->[$idx1]; while(1) { my $idxh = int( ($idx0 + $idx1) /2 ); my $xh = $pX->[$idxh]; #print "x: $x ($x0,$xh,$x1) => "; # return $idxh if(($x1 - $x) * ($xh - $x) == 0.0); if(($x1 - $x) * ($xh - $x) <= 0.0) { $idx0 = $idxh; $x0 = $pX->[$idx0]; } else { # elsif(($xh - $x) * ($x0 - $x) < 0.0) { $idx1 = $idxh; $x1 = $pX->[$idx1]; } # else { # return undef; # } #print "($x0 [$idx0],$x1 [$idx1])\n"; last if($idx1 - $idx0 <= 1); } #exit if($x > 1140); return $idx0; } sub Interpolate { my ($pX, $pY, $x) = @_; return $pY->[0] if($x <= $pX->[0]); my $nData = @$pX; return $pY->[$nData-1] if($x >= $pX->[$nData-1]); my $idx = FindIndexByBinarySearch($pX, $x); my $idx1 = $idx+1; my $k = ($pY->[$idx1] - $pY->[$idx]) / ($pX->[$idx1] - $pX->[$idx]); return $pY->[$idx] + $k * ($x - $pX->[$idx]); } sub InterpolateForConstantStepX { my ($nData, $x0, $dx, $pY, $x) = @_; my $idx = int(($x - $x0) / $dx); return $pY->[0] if($idx < 0); return $pY->[$nData-1] if($idx >= $nData); #return $pY->[$idx]; $idx-- if($idx == $nData-1); my $idx1 = $idx+1; #print "idx: $idx, $idx1 [$nData]\n"; my $xa = $x0 + $idx*$dx; my $xb = $x0 + $idx1*$dx; #print "x: $xa < $x < $xb\n"; #print " y[$idx1]=$pY->[$idx1], $pY->[$idx]\n"; my $k = ($pY->[$idx1] - $pY->[$idx]) / ($xb - $xa); #print "k=$k\n"; return $pY->[$idx] + $k * ($x - $xa); } sub InterpolateByLagrange { my ($pX, $pY, $x) = @_; my $nData = @$pX; if($nData < 3) { # if($nData < 3 or $x < $pX->[0] or $x >= $pX->[$nData-1]) { return undef; } my $w = 0.0; for(my $i = 0 ; $i < $nData ; $i++) { my $w1 = 1.0; my $w2 = $pX->[$i]; for(my $j = 0 ; $j < $nData ; $j++) { my $w3 = $pX->[$j]; if($i != $j) { $w1 *= ($x - $w3) / ($w2 - $w3); } } $w += $w1 * $pY->[$i]; } #print ("y=$w for $x [$pX->[0],$pX->[1],$pX->[2]] [$pY->[0],$pY->[1],$pY->[2]]\n"); #last if($x > 1150); return $w; } sub InterpolateBySimpson { my ($pX, $pY, $x) = @_; return $pY->[0] if($x <= $pX->[0]); my $nData = @$pX; return $pY->[$nData-1] if($x >= $pX->[$nData-1]); my $idx = FindIndexByBinarySearch($pX, $x); $idx = 1 if($idx <= 1); $idx = $nData-2 if($idx >= $nData-3); #my $idxm1=$idx-1; #my $idxp1=$idx+1; #print "idx=$idx\tx=$x ($pX->[$idxm1],$pX->[$idx],$pX->[$idxp1])\n"; my $y = InterpolateByLagrange( [@$pX[$idx-1..$idx+1]], [@$pY[$idx-1..$idx+1]], $x); #print "\t\ty=$y ($pY->[$idxm1],$pY->[$idx],$pY->[$idxp1])\n"; return $y; } sub InterpolateBy4thPolynomial { my ($pX, $pY, $x) = @_; return $pY->[0] if($x <= $pX->[0]); my $nData = @$pX; return $pY->[$nData-1] if($x >= $pX->[$nData-1]); my $idx = FindIndexByBinarySearch($pX, $x); $idx = 2 if($idx <= 2); $idx = $nData-3 if($idx >= $nData-4); #my $idxm1=$idx-1; #my $idxp1=$idx+1; #print "idx=$idx\tx=$x ($pX->[$idxm1],$pX->[$idx],$pX->[$idxp1])\n"; my $y = InterpolateByLagrange( [@$pX[$idx-2..$idx+2]], [@$pY[$idx-2..$idx+2]], $x); #print "\t\ty=$y ($pY->[$idxm1],$pY->[$idx],$pY->[$idxp1])\n"; return $y; } sub InterpolateByCubicSpline { my ($pX, $pY, $x) = @_; my $nData = @$pX; return undef if($nData < 3); return $pY->[0] if($x <= $pX->[0]); return $pY->[$nData-1] if($x >= $pX->[$nData-1]); for(my $i = 0 ; $i < $nData-1 ; $i++) { if($pX->[$i+1] <= $pX->[$i]) { my $i1 = $i+1; print "Warning in Algorism::InterpolateByCubicSpline: $i: d[i+1] must be > d[i] ($pX->[$i1] < $pX->[$i1])\n"; return undef; } } my ($ph, $psp) = subspl($pX, $pY); for(my $i = 1 ; $i < $nData ; $i++) { if($pX->[$i-1] <= $x and $x < $pX->[$i]) { my $sm = $psp->[$i-1]; my $si = $psp->[$i]; my $hi = $ph->[$i]; my $hi2 = $hi * $hi; my $dxp = $pX->[$i] - $x; my $dxm = $x - $pX->[$i-1]; my $v = $sm * $dxp * $dxp * $dxp + $si * $dxm * $dxm * $dxm + (6.0 * $pY->[$i-1] - $hi2 * $sm) * $dxp + (6.0 * $pY->[$i] - $hi2 * $si) * $dxm; return $v / $hi / 6.0; } } return undef; } sub subspl { my ($pX, $pY) = @_; my $nData = @$pX; my (@h, @sp, @du, @dc); $du[$nData-1] = 0.0; $dc[$nData-1] = 0.0; $sp[$nData-1] = 0.0; for(my $i = 1 ; $i < $nData ; $i++) { $h[$i] = $pX->[$i] - $pX->[$i-1]; } for(my $i = 1 ; $i < $nData-1 ; $i++) { my $hip1 = $pX->[$i+1] - $pX->[$i]; $sp[$i] = 6.0 * (($pY->[$i+1] - $pY->[$i]) / ($h[$i] * $hip1) - ($pY->[$i] - $pY->[$i-1]) / ($h[$i] * $h[$i])); $du[$i] = $h[$i+1] / $h[$i]; $dc[$i] = 2.0 * (1.0 + $du[$i]); } $dc[$nData-1] += 1.0; $dc[$nData-2] += $h[$nData-1] / $h[$nData-2]; $du[1] /= $dc[1]; $sp[1] /= $dc[1]; for(my $i = 2 ; $i < $nData ; $i++) { #print "$i: $dc[$i], $du[$i-1]\n"; my $g = 1.0 / ($dc[$i] - $du[$i-1]); $du[$i] *= $g; $sp[$i] = ($sp[$i] - $sp[$i-1]) * $g; } for(my $i = $nData-2 ; $i >= 1 ; $i--) { #print "$i: $sp[$i+1], $du[$i]\n"; $sp[$i] -= $sp[$i+1] * $du[$i]; } $sp[0] = $sp[1]; $sp[$nData-1] = $sp[$nData-2]; return (\@h, \@sp); } sub dydxFunc { my ($x, $y) = @_; # dy / dx = y return $y; } sub SolveByEuler { my ($pdydxFunc, $x0, $x1, $h, $pYinitial) = @_; my (@x, @y); $x[0] = $x0; $y[0] = $pYinitial->[0]; for(my $i = 1, my $x = $x0 + $h ; $x <= $x1 ; $i++, $x = $x0 + $i * $h) { $x[$i] = $x; my $dydx = &$pdydxFunc($x[$i-1], $y[$i-1]); $y[$i] = $y[$i-1] + $h * $dydx; #print "$i: ($x[$i], $y[$i])\n"; } return (\@x, \@y); } sub dydxMultiFunc { my ($idx, $x, $py) = @_; # dy / dx = y if($idx == 0) { return $py->[0]; } else { print "error: Function index idx=$idx is not found.\n"; exit; } } sub SolveSimultaneousDifferentialEquationsByEuler { my ($nEq, $pdydxMultiFunc, $x0, $x1, $h, $ppYinitial) = @_; my (@x, @py); $x[0] = $x0; for(my $ie = 0 ; $ie < $nEq ; $ie++) { $py[$ie] = []; $py[$ie][0] = $ppYinitial->[$ie][0]; #print "ie=$ie: y(x0) = $py[$ie][0]\n"; } my $x; for(my $i = 1 ; $x <= $x1 ; $i++) { $x[$i] = $x = $x0 + $i * $h; my @YArray; for(my $ie = 0 ; $ie < $nEq ; $ie++) { $YArray[$ie] = $py[$ie][$i-1]; } for(my $ie = 0 ; $ie < $nEq ; $ie++) { my $dydx = &$pdydxMultiFunc($ie, $x[$i-1], \@YArray); $py[$ie][$i] = $py[$ie][$i-1] + $h * $dydx; #print "i=$i, ie=$ie: ($x[$i], $py[$ie][$i]) (dy/dx=$dydx)\n"; } } return (\@x, \@py); } sub SolveByHeun { my ($pdydxFunc, $x0, $x1, $h, $pYinitial) = @_; my (@x, @y); $x[0] = $x0; $y[0] = $pYinitial->[0]; for(my $i = 1, my $x = $x0 + $h ; $x <= $x1 ; $i++, $x = $x0 + $i * $h) { $x[$i] = $x; my $dydx = &$pdydxFunc($x[$i-1], $y[$i-1]); my $k1 = $h * $dydx; my $k2 = $h * &$pdydxFunc($x[$i], $y[$i-1] + $k1); $y[$i] = $y[$i-1] + ($k1 + $k2) / 2.0; } return (\@x, \@y); } sub SolveSimultaneousDifferentialEquationsByHeun { my ($nEq, $pdydxMultiFunc, $x0, $x1, $h, $ppYinitial) = @_; my (@x, @py); $x[0] = $x0; for(my $ie = 0 ; $ie < $nEq ; $ie++) { $py[$ie] = []; $py[$ie][0] = $ppYinitial->[$ie][0]; #print "ie=$ie: y(x0) = $py[$ie][0]\n"; } my $x; for(my $i = 1 ; $x <= $x1 ; $i++) { $x[$i] = $x = $x0 + $i * $h; my @YArray; for(my $ie = 0 ; $ie < $nEq ; $ie++) { $YArray[$ie] = $py[$ie][$i-1]; } my @k1; for(my $ie = 0 ; $ie < $nEq ; $ie++) { my $dydx = &$pdydxMultiFunc($ie, $x[$i-1], \@YArray); $k1[$ie] = $h * $dydx; } for(my $ie = 0 ; $ie < $nEq ; $ie++) { $YArray[$ie] = $py[$ie][$i-1] + $k1[$ie]; } for(my $ie = 0 ; $ie < $nEq ; $ie++) { my $k2 = $h * &$pdydxMultiFunc($ie, $x[$i], \@YArray); $py[$ie][$i] = $py[$ie][$i-1] + ($k1[$ie] + $k2) / 2.0; #print "i=$i, ie=$ie: ($x[$i], $py[$ie][$i]) (dy/dx=$dydx)\n"; } } return (\@x, \@py); } sub SolveByRungekutta3 { my ($pdydxFunc, $x0, $x1, $h, $pYinitial) = @_; my (@x, @y); $x[0] = $x0; $y[0] = $pYinitial->[0]; for(my $i = 1, my $x = $x0 + $h ; $x <= $x1 ; $i++, $x = $x0 + $i * $h) { $x[$i] = $x; my $dydx = &$pdydxFunc($x[$i-1], $y[$i-1]); my $k1 = $h * $dydx; my $k2 = $h * &$pdydxFunc($x[$i-1] + $h / 2.0, $y[$i-1] + $k1 / 2.0); my $k3 = $h * &$pdydxFunc($x[$i], , $y[$i-1] + $k2 + $k2 - $k1); $y[$i] = $y[$i-1] + ($k1 + 4.0 * $k2 + $k3) / 6.0; } return (\@x, \@y); } sub SolveSimultaneousDifferentialEquationsByRungekutta3 { my ($nEq, $pdydxMultiFunc, $x0, $x1, $h, $ppYinitial) = @_; my (@x, @py); $x[0] = $x0; for(my $ie = 0 ; $ie < $nEq ; $ie++) { $py[$ie] = []; $py[$ie][0] = $ppYinitial->[$ie][0]; #print "ie=$ie: y(x0) = $py[$ie][0]\n"; } my $x; for(my $i = 1 ; $x <= $x1 ; $i++) { $x[$i] = $x = $x0 + $i * $h; my @YArray; for(my $ie = 0 ; $ie < $nEq ; $ie++) { $YArray[$ie] = $py[$ie][$i-1]; } my @k1; for(my $ie = 0 ; $ie < $nEq ; $ie++) { my $dydx = &$pdydxMultiFunc($ie, $x[$i-1], \@YArray); $k1[$ie] = $h * $dydx; } for(my $ie = 0 ; $ie < $nEq ; $ie++) { $YArray[$ie] = $py[$ie][$i-1] + $k1[$ie] / 2.0; } my @k2; for(my $ie = 0 ; $ie < $nEq ; $ie++) { $k2[$ie] = $h * &$pdydxMultiFunc($ie, $x[$i-1] + $h / 2.0, \@YArray); } for(my $ie = 0 ; $ie < $nEq ; $ie++) { $YArray[$ie] = $py[$ie][$i-1] + $k2[$ie] + $k2[$ie] - $k1[$ie]; } for(my $ie = 0 ; $ie < $nEq ; $ie++) { my $k3 = $h * &$pdydxMultiFunc($ie, $x[$i], \@YArray); $py[$ie][$i] = $py[$ie][$i-1] + ($k1[$ie] + 4.0 * $k2[$ie] + $k3) / 6.0; #print "i=$i, ie=$ie: ($x[$i], $py[$ie][$i]) (dy/dx=$dydx)\n"; } } return (\@x, \@py); } sub SolveByRungekutta4 { my ($pdydxFunc, $x0, $x1, $h, $pYinitial) = @_; my (@x, @y); $x[0] = $x0; $y[0] = $pYinitial->[0]; for(my $i = 1, my $x = $x0 + $h ; $x <= $x1 ; $i++, $x = $x0 + $i * $h) { $x[$i] = $x; my $dydx = &$pdydxFunc($x[$i-1], $y[$i-1]); my $k1 = $h * $dydx; my $k2 = $h * &$pdydxFunc($x[$i-1] + $h / 2.0, $y[$i-1] + $k1 / 2.0); my $k3 = $h * &$pdydxFunc($x[$i-1] + $h / 2.0, $y[$i-1] + $k2 / 2.0); my $k4 = $h * &$pdydxFunc($x[$i] , $y[$i-1] + $k3); $y[$i] = $y[$i-1] + ($k1 + 2.0 * $k2 + 2.0 * $k3 + $k4) / 6.0; } return (\@x, \@y); } sub SolveSimultaneousDifferentialEquationsByRungekutta4 { my ($nEq, $pdydxMultiFunc, $x0, $x1, $h, $ppYinitial) = @_; my (@x, @py); $x[0] = $x0; for(my $ie = 0 ; $ie < $nEq ; $ie++) { $py[$ie] = []; $py[$ie][0] = $ppYinitial->[$ie][0]; #print "ie=$ie: y(x0) = $py[$ie][0]\n"; } my $x; for(my $i = 1 ; $x <= $x1 ; $i++) { $x[$i] = $x = $x0 + $i * $h; my @YArray; for(my $ie = 0 ; $ie < $nEq ; $ie++) { $YArray[$ie] = $py[$ie][$i-1]; } my @k1; for(my $ie = 0 ; $ie < $nEq ; $ie++) { my $dydx = &$pdydxMultiFunc($ie, $x[$i-1], \@YArray); $k1[$ie] = $h * $dydx; } for(my $ie = 0 ; $ie < $nEq ; $ie++) { $YArray[$ie] = $py[$ie][$i-1] + $k1[$ie] / 2.0; } my @k2; for(my $ie = 0 ; $ie < $nEq ; $ie++) { $k2[$ie] = $h * &$pdydxMultiFunc($ie, $x[$i-1] + $h / 2.0, \@YArray); } for(my $ie = 0 ; $ie < $nEq ; $ie++) { $YArray[$ie] = $py[$ie][$i-1] + $k2[$ie] / 2.0; } my @k3; for(my $ie = 0 ; $ie < $nEq ; $ie++) { $k3[$ie] = $h * &$pdydxMultiFunc($ie, $x[$i-1] + $h / 2.0, \@YArray); } for(my $ie = 0 ; $ie < $nEq ; $ie++) { $YArray[$ie] = $py[$ie][$i-1] + $k3[$ie]; } for(my $ie = 0 ; $ie < $nEq ; $ie++) { my $k4 = $h * &$pdydxMultiFunc($ie, $x[$i], \@YArray); $py[$ie][$i] = $py[$ie][$i-1] + ($k1[$ie] + 2.0 * $k2[$ie] + 2.0 * $k3[$ie] + $k4) / 6.0; #print "i=$i, ie=$ie: ($x[$i], $py[$ie][$i]) (dy/dx=$dydx)\n"; } } return (\@x, \@py); } sub SolveByVerlet { my ($pdydxFunc, $x0, $x1, $h, $ppYinitial) = @_; my (@x, @y, @y2); my $kh2 = 1.0 / 2.0 / $h; my $h2 = $h * $h; $x[0] = $x0; $y[0] = $ppYinitial->[0][0]; $y2[0] = $ppYinitial->[1][0]; $x[1] = $x0 + $h; $y[1] = $ppYinitial->[0][1]; $y2[1] = $ppYinitial->[1][1]; # $y2[0] = ($y[1] - $y[0]) / $h; my $i = 2; for(my $x = $x0 + 2.0 * $h ; $x <= $x1 + $h ; $i++, $x = $x0 + $i * $h) { $x[$i] = $x; $y[$i] = 2.0 * $y[$i-1] - $y[$i-2] + $h2 * &$pdydxFunc($x[$i-1], [$y[$i-1], $y2[$i-1]]); $y2[$i-1] = ($y[$i] - $y[$i-2]) * $kh2; } $y2[$i] = ($y[$i] - $y[$i-1]) / $h; return (\@x, [\@y, \@y2]); } # Sande-Tukey Algorism (科学計測のための波形データ処理) # ar[n], ai[n]: Real and imaginary parts of input data. # n: datum number # $mode: 'forward': Fourier transform. 'reverse': Reverse Fourier transform sub FFT_ST # proper operation not confirmed { my ($n, $pX, $ar_org, $ai_org, $mode) = @_; $ar_org = [] if(!defined $ar_org); $ai_org = [] if(!defined $ai_org); my $ar = [@$ar_org]; my $ai = [@$ai_org]; if($n < 2) { print "Error in Algorism::FFT: n ($n) is smaller than 2.\n"; return undef; } my $nf = $n / 2; my $m = int(log($n) / log(2.0) + 0.001); #print "iter=$m (n=$n) (", 2**$m, ")\n"; if(2**$m != $n) { print "Error in Algorism::FFT: n ($n) must be 2^k.\n"; return undef; } my (@S, @C); my $A = 0; my $B = $pi * 2.0 / $n; for(my $i= 0 ; $i <= $nf ; $i++) { if($mode eq 'reverse') { $S[$i] = sin($A); $C[$i] = cos($A); } else { $S[$i] = -sin($A); $C[$i] = cos($A); } $A += $B; } # FFT my $j = 0; my $p = 0; my $l = $n; my $k = 0; my $h = 1; for(my $g = 1 ; $g <= $m ; $g++) { $l = $l / 2; $k = 0; for(my $q = 1 ; $q <= $h ; $q++) { $p = 0; for(my $i = $k ; $i <= $l + $k - 1 ; $i++) { $j = $i + $l; $A = $ar->[$i] - $ar->[$j]; $B = $ai->[$i] - $ai->[$j]; $ar->[$i] += $ar->[$j]; $ai->[$i] += $ai->[$j]; if($p == 0) { $ar->[$j] = $A; $ai->[$j] = $B; } else { $ar->[$j] = $A * $C[$p] + $B * $S[$p]; $ai->[$j] = $B * $C[$p] - $A * $S[$p]; } $p += $h; } $k += $l + $l; } $h += $h; } # bit reversal $j = $nf; for(my $i = 1; $i <= $n-1; $i++) { $k = $n; if($j < $i) { my $r = $ar->[$i]; $ar->[$i] = $ar->[$j]; $ar->[$j] = $r; $r = $ai->[$i]; $ai->[$i] = $ai->[$j]; $ai->[$j] = $r; } $k /= 2; while($j >= $k) { $j -= $k; $k /= 2; } $j += $k; } my $df = 1.0 / ($pX->[1] + $pX->[$n-1] - 2.0 * $pX->[0]); my @f; for(my $i = 0; $i < $nf; $i++) { $f[$i] = $df * ($i+1); } if($mode eq 'reverse') { my $w = -1.0 / $n; for(my $i = 0; $i < $nf; $i++) { $ar->[$i] = $w * $ar->[$i]; $ai->[$i] = $w * $ai->[$i]; } } #print "a=[$ar, $ai]\n"; return($nf, \@f, $ar, $ai); } # ar[n], ai[n]: Real and imaginary parts of input data. # n: datum number # $mode: 'forward': Fourier transform. 'reverse': Reverse Fourier transform sub FFT { my ($n, $pX, $ar_org, $ai_org, $mode) = @_; $ar_org = [] if(!defined $ar_org); $ai_org = [] if(!defined $ai_org); my $ar = [@$ar_org]; my $ai = [@$ai_org]; if($n < 2) { print "Error in Algorism::FFT: n ($n) is smaller than 2.\n"; return undef; } my $nf = $n / 2; my $iter = int(log($n) / log(2.0) + 0.001); #print "iter=$iter (n=$n)\n"; my $j = 1; for(my $i = 0; $i < $iter; $i++) { $j *= 2; } if($n != $j) { print "Error in Algorism::FFT: n ($n) must be 2^k.\n"; return undef; } my $sign = 1.0; if($mode eq 'reverse') { $sign = -1.0; } my $df = 1.0 / ($pX->[1] + $pX->[$n-1] - 2.0 * $pX->[0]); my ($j1, $j2, $dr1, $dr2, $di1, $di2, $tr, $ti); my $xp2 = $n; for(my $it = 0; $it < $iter; $it++) { my $xp = $xp2; $xp2 = $xp / 2; my $w = $pi / $xp2; for(my $k = 0; $k < $xp2; $k++) { my $arg = $k * $w; my $wr = cos($arg); my $wi = $sign * sin($arg); my $i = $k - $xp; for($j = $xp; $j <= $n; $j += $xp) { $j1 = $j + $i; $j2 = $j1 + $xp2; $dr1 = $ar->[$j1]; $dr2 = $ar->[$j2]; $di1 = $ai->[$j1]; $di2 = $ai->[$j2]; $tr = $dr1 - $dr2; $ti = $di1 - $di2; $ar->[$j1] = $dr1 + $dr2; $ai->[$j1] = $di1 + $di2; $ar->[$j2] = $tr * $wr - $ti * $wi; $ai->[$j2] = $ti * $wr + $tr * $wi; } } } $j1 = $nf; $j2 = $n - 1; $j = 1; for(my $i = 1; $i <= $j2; $i++) { if($i < $j) { my $im1 = $i - 1; my $jm1 = $j - 1; my $tr = $ar->[$jm1]; my $ti = $ai->[$jm1]; $ar->[$jm1] = $ar->[$im1]; $ai->[$jm1] = $ai->[$im1]; $ar->[$im1] = $tr; $ai->[$im1] = $ti; } my $k = $j1; while($k < $j) { $j -= $k; $k /= 2; } $j += $k; } my @f; for(my $i = 0; $i < $nf; $i++) { $f[$i] = $df * ($i+1); } if($mode eq 'reverse') { my $w = -1.0 / $n; for(my $i = 0; $i < $nf; $i++) { $ar->[$i] = $w * $ar->[$i]; $ai->[$i] = $w * $ai->[$i]; } } return($nf, \@f, $ar, $ai); } sub DFT { my ($n, $pX, $pR, $pI, $mode) = @_; # my $df = 1.0 / ($pX->[$n-1] - $pX->[0]); my $df = 1.0 / ($pX->[1] + $pX->[$n-1] - 2.0 * $pX->[0]); my (@f, @yr, @yi); my $C = 1.0; $C = -1.0 if($mode eq 'inverse'); my (@wr, @wi); $wr[0] = 1.0; $wi[0] = 0.0; my $phi = $C * 2.0*$pi / $n; my $wr = cos($phi); my $wi = sin($phi); $wr[1] = $wr; $wi[1] = $wi; for(my $k = 2 ; $k < $n ; $k++) { # for(my $k = 0 ; $k < $n ; $k++) { # my $phi = $C * 2.0*$pi / $n * $k; # $wr[$k] = cos($phi); # $wi[$k] = sin($phi); # w^k = (wr(k-1) + i * wi(k-1)) * (wr + i * wi) # = (wr(k-1) * wr - wi(k-1) * wi) + i * (wi(k-1) * wr + wr(k-1) * wi) $wr[$k] = $wr[$k-1] * $wr - $wi[$k-1] * $wi; $wi[$k] = $wr[$k-1] * $wi + $wi[$k-1] * $wr; } for(my $k = 0 ; $k < $n/2 ; $k++) { $f[$k] = $df * ($k+1); $yr[$k] = 0.0; $yi[$k] = 0.0; for(my $i = 0 ; $i < $n ; $i++) { my $ik = ($i * $k) % $n; my $wr = $wr[$ik]; my $wi = $wi[$ik]; # x*exp(2pik) = (R + i I)(cos(phi) + i sin(phi)) # Rcos(phi) - Isin(phi) + i(Rsin(phi) + Icos(phi)) my $R = ($pR)? $pR->[$i] : 0.0; my $I = ($pI)? $pI->[$i] : 0.0; $yr[$k] += $R * $wr - $I * $wi; $yi[$k] += $R * $wi + $I * $wr; } } return ($n/2, \@f, \@yr, \@yi); } sub DFT2 { my ($n, $pX, $pR, $pI, $mode) = @_; # my $df = 1.0 / ($pX->[$n-1] - $pX->[0]); my $df = 1.0 / ($pX->[1] + $pX->[$n-1] - 2.0 * $pX->[0]); my (@f, @yr, @yi); my $C = 1.0; $C = -1.0 if($mode eq 'inverse'); for(my $k = 0 ; $k < $n/2 ; $k++) { $f[$k] = $df * ($k+1); $yr[$k] = 0.0; $yi[$k] = 0.0; for(my $i = 0 ; $i < $n ; $i++) { my $phi = $C * 2.0*$pi / $n * $i*$k; my $cosp = cos($phi); my $sinp = sin($phi); # x*exp(2pik) = (R + i I)(cos(phi) + i sin(phi)) # Rcos(phi) - Isin(phi) + i(Rsin(phi) + Icos(phi)) my $R = ($pR)? $pR->[$i] : 0.0; my $I = ($pI)? $pI->[$i] : 0.0; $yr[$k] += $R * $cosp - $I * $sinp; $yi[$k] += $R * $sinp + $I * $cosp; } } return ($n/2, \@f, \@yr, \@yi); } sub randomGauss { my ($avg, $sigma, $seed) = @_; $avg = 0.0 if(!defined $avg); $sigma = 1.0 if(!defined $sigma); srand($seed) if(defined $seed); my $x = rand(1.0); $x = 1.0e-100 if($x == 0.0); my $y = rand(1.0); my $z = sqrt(-2.0*log($x)) * sin(2.0 * $pi * $y); return $avg + $sigma * $z; } sub randomExp { my ($rambda, $seed) = @_; $rambda = 1 if(!defined $rambda); srand($seed) if(defined $seed); my $x = rand(1.0); $x = 1.0e-100 if($x == 0.0); return -log($x) / $rambda; } #NTSCで使われているYIQカラーモデルに基づく方法 # 松田晃一、HTML5+JavaScriptによる画像・動画像処理入門、、カットシステム (2014) sub RGBtoGray { my ($R, $G, $B) = @_; return int(0.3 * $R + 0.59 * $G + 0.11 * $B); } 1;