function [f] = gammax(z) % GAMMA Gamma function valid in the entire complex plane. % Gives exact results for integer arguments. % Relative accuracy is better than 10^-13. % This routine uses an excellent Lanczos series % approximation for the complex Gamma function. % %usage: [f] = gamma(z) % z may be complex and of any size. % Also n! = prod(1:n) = gamma(n+1) % %tested under version 5.3.1 % %References: C. Lanczos, SIAM JNA 1, 1964. pp. 86-96 % Y. Luke, "The Special ... approximations", 1969 pp. 29-31 % Y. Luke, "Algorithms ... functions", 1977 % J. Spouge, SIAM JNA 31, 1994. pp. 931 % W. Press, "Numerical Recipes" % S. Chang, "Computation of special functions", 1996 % %see also: GAMMA GAMMALN GAMMAINC PSI %see also: mhelp GAMMA %Paul Godfrey %pgodfrey@intersil.com %11-15-00 twopi=pi+pi; [row, col] = size(z); z=z(:); zz=z; f = 0.*z; % reserve space in advance p=find(real(z)<0); if ~isempty(p) z(p)=-z(p); end %Lanczos approximation for the complex plane %calculated with vpa digits(128) c = [ 1.000000000000000174663; 5716.400188274341379136; -14815.30426768413909044; 14291.49277657478554025; -6348.160217641458813289; 1301.608286058321874105; -108.1767053514369634679; 2.605696505611755827729; -0.7423452510201416151527e-2; 0.5384136432509564062961e-7; -0.4023533141268236372067e-8]; %Matlab's own Gamma is based upon one by W.J. Cody from Oct. 12, 1989 % c is calculated from c=D*B*C*F % where D is [1 -Sloane's sequence A002457], % fact(2n+2)/(2fact(n)fact(n+1)), diagonal % and B is rows from the odd cols of A & Stegun table 24.1 (Binomial), % unit Upper triangular % and C is cols from the even cols of A & Stegun table 22.3 (Cheby), % C(1)=1/2, Lower triangular % and F is sqrt(2)/pi*gamma(z+0.5).*exp(z+g+0.5).*(z+g+0.5).^-(z+0.5) % gamma(z+0.5) can be computed using factorials,2^z, and sqrt(pi). % A & Stegun formulas 6.1.8 and 6.1.12) % % for z=0:(g+1) where g=9 for this example. Num. Recipes used g=5 % gamma functions were made for g=5 to g=13. % g=9 gave the best error performance % for n=1:171. This accuracy is comparable to Matlab's % real only Gamma function. %for example, for g=5 we have dimension (g+2) as % %D=diag([1 -1 -6 -30 -140 -630 -2772]) % %B=[1 1 1 1 1 1 1; % 0 1 -2 3 -4 5 -6; % 0 0 1 -4 10 -20 35; % 0 0 0 1 -6 21 -56; % 0 0 0 0 1 -8 36; % 0 0 0 0 0 1 -10; % 0 0 0 0 0 0 1] % %C=[0.5 0 0 0 0 0 0; % -1 2 0 0 0 0 0; % 1 -8 8 0 0 0 0; % -1 18 -48 32 0 0 0; % 1 -32 160 -256 128 0 0; % -1 50 -400 1120 -1280 512 0; % 1 -72 840 -3584 6912 -6144 2048] % % %F=[ 83.2488738328781364; % 16.0123164052516814; % 7.0235516528280364; % 4.1065601620725384; % 2.7864466488340058; % 2.0690586753686773; % 1.6309293967598249] % %so c = (D*(B*C))*F % %this will generate the Num. Recp. values %(if you used vpa for calculating F) %notice that (D*B*C) always contains only integers %(except for the 1/2 value) %Note: 24*sum(c) ~= Integer = 12*g*g+23 if you calculated c correctly %for this example g=5 so 24*sum(c) = 322.99... ~= 12*5*5+23 = 323 %Spouge's approximate formula for the c's can be calculated from applying %L'Hopitals rule to the partial fraction expansion of the sum portion of %Lanczos's formula. These values are easyer to calculate than D*B*C*F %but are not as accurate. (An approx. of an approx.) g=9; %Num Recipes used g=9 %for a lower order approximation t=z+g; s=0; for k=g+2:-1:2 s=s+c(k)./t; t=t-1; end s=s+c(1); ss=(z+g-0.5); s=log(s.*sqrt(twopi)) + (z-0.5).*log(ss)-ss; LogofGamma = s; f = exp(LogofGamma); if ~isempty(p) f(p)=-pi./(zz(p).*f(p).*sin(pi*zz(p))); end p=find(round(zz)==zz & imag(zz)==0 & real(zz)<=0); if ~isempty(p) f(p)=Inf; end %make results exact for integer arguments p=find(round(zz)==zz & imag(zz)==0 & real(zz)>0 ); if ~isempty(p) zmax=max(zz(p)); pp=1; for zint=1:zmax p=find(zz==zint); if ~isempty(p) f(p)=pp; end pp=pp*zint; end end f=reshape(f,row,col); return %A demo of this routine is: clc clear all close all x=-4:1/16:4.5; y=-4:1/16:4; [X,Y]=meshgrid(x,y); z=X+i*Y; f=gamma(z); p=find(abs(f)>10); f(p)=10; mesh(x,y,abs(f),phase(f)); view([-40 30]); rotate3d; return %to see the relative accuracy for real n clc clear all close all warning off format long g n=[0.5:0.5:171.5]; n=n(:); which gamma lg=gamma(n);%Lanczos complex gamma wd=pwd; %get ready to use the other Gamma function cd ../ which gamma mg=gamma(n);%Matlab's gamma cd(wd) %let's consider Maple's Gamma to be the true and accurate standard G=mfun('gamma',n); elg=abs((G-lg)./G); meanelg=mean(elg); stdelg =svd( elg); eelg=log10(elg); emg=abs((G-mg)./G); meanemg=mean(emg); stdemg =svd( emg); eemg=log10(emg); figure(1) plot(n,eelg,'bh') hold on; plot(n,eemg,'r*') grid on xlabel('n') ylabel('Relative accuracy compared to Maple Gamma') title('Matlab real Gamma in red, Lanczos complex Gamma in blue') MeanErrorAndStd=[meanemg stdemg; meanelg stdelg] return