0

我将 fortran 77 中的书面代码转换为 Matlab 代码。此函数使用 QL 算法计算矩阵的特征值和特征向量。由于某些原因,我不能在 matlab 中使用 eig 函数的结果。该方法得到的特征值与 eig 函数得到的特征值不完全相同,有的相同,有的不同。我不知道问题出在哪里。感谢您的任何帮助和建议。如果需要运行和查看结果,我可以提供输入数组。

这是fortran代码:

      SUBROUTINE tqli(d,e,n,np,z)
      INTEGER n,np
      REAL d(np),e(np),z(np,np)
CU    USES pythag
      INTEGER i,iter,k,l,m
      REAL b,c,dd,f,g,p,r,s,pythag
      do 11 i=2,n
        e(i-1)=e(i)
11    continue
      e(n)=0.
      do 15 l=1,n
        iter=0
1       do 12 m=l,n-1
          dd=abs(d(m))+abs(d(m+1))
          if (abs(e(m))+dd.eq.dd) goto 2
12      continue
        m=n
2       if(m.ne.l)then
          if(iter.eq.30)pause 'too many iterations in tqli'
          iter=iter+1
          g=(d(l+1)-d(l))/(2.*e(l))
          r=pythag(g,1.)
          g=d(m)-d(l)+e(l)/(g+sign(r,g))
          s=1.
          c=1.
          p=0.
          do 14 i=m-1,l,-1
            f=s*e(i)
            b=c*e(i)
            r=pythag(f,g)
            e(i+1)=r
            if(r.eq.0.)then
              d(i+1)=d(i+1)-p
              e(m)=0.
              goto 1
            endif
            s=f/r
            c=g/r
            g=d(i+1)-p
            r=(d(i)-g)*s+2.*c*b
            p=s*r
            d(i+1)=g+p
            g=c*r-b
C     Omit lines from here ...
            do 13 k=1,n
              f=z(k,i+1)
              z(k,i+1)=s*z(k,i)+c*f
              z(k,i)=c*z(k,i)-s*f
13          continue
C     ... to here when finding only eigenvalues.
14        continue
          d(l)=d(l)-p
          e(l)=g
          e(m)=0.
          goto 1
        endif
15    continue
      return
      END

以下是matlab代码:

function [d,z]=tqli(d,e,n,np,z)

for i=2:n
    e(i-1)=e(i);
end
e(n)=0.;
for l=1:n
    iter=0;
    for m=l:(n-1)
        dd=abs(d(m))+abs(d(m+1));
        if ((abs(e(m))+dd)==dd)
            break
        end
    end
    m=n;
    if (m~=l)
        if (iter==30)
            disp('too many iteration in tqli')
        end
        iter=iter+1;
        g=(d(l+1)-d(l))/(2.*e(l));
        r=pythag(g,1.);
        g=d(m)-d(l)+e(l)/(g+r*sign(g));
        s=1.;
        c=1.;
        p=0.;
        for i=(m-1):-1:l
            f=s*e(i);
            b=c*e(i);
            r=pythag(f,g);
            e(i+1)=r;
            if(r==0.)
                d(i+1)=d(i+1)-p;
                e(m)=0.;
                break
            end
            s=f/r;
            c=g/r;
            g=d(i+1)-p;
            r=(d(i)-g)*s+2.*c*b;
            p=s*r;
            d(i+1)=g+p;
            g=c*r-b;
            for k=1:n
                f=z(k,i+1);
                z(k,i+1)=s*z(k,i)+c*f;
                z(k,i)=c*z(k,i)-s*f;
            end
        end
        d(l)=d(l)-p;
        e(l)=g;
        e(m)=0.;
    end
end
end
4

2 回答 2

1

在您的 Fortran 代码中,您将一堆变量声明为REAL. 大多数编译器默认将这些实现为 32 位浮点数。默认情况下,您的 Matlab 版本中的相应变量是 64 位浮点数。

在没有看到您的输入或输出的情况下,很难判断这是否部分或全部是两个版本输出差异的原因。但是,更改浮点数的精度通常是您报告的这类问题的原因,尤其是在诸如特征值计算等棘手的数值方法中。

在我写作时,我还观察到您已经将比较浮点值的相等性的不良做法从 Fortran 转换为 Matlab。在 Fortran 中这是不好的做法,在 Matlab 中也是不好的做法。

在 Matlab 中编写表达式时要小心,例如

2.*c

c在 Fortran 中,将标量或数组变量的每个元素乘以REALvalue 2.0。在 Matlab 中,它将标量或向量的每个元素乘以c整数2.*是 Matlab 中的运算符(或运算符的组合,如果您愿意),意思是“逐个元素执行乘法”。您已经巧妙地更改了程序的语义,并且可能更改了您计算的某些数字的精确值。

这引出了我的最后评论:您所说的 Matlab 版本只不过是 Fortran 代码的音译,从一种语言到另一种语言的逐行复制。有时你可以侥幸逃脱,但这种幼稚的方法迟早会绊倒你。也许它已经有了。你真的应该把你的 Matlab 改写成 Matlab,就好像你打算写好的 Matlab 一样。

于 2012-07-29T16:24:49.397 回答
1

我看到了一些可能会导致您的 matlab 翻译出现问题的事情。一个是您对 fortran 符号的转换。您需要使用 abs(r) 而不仅仅是 r。

我看到的另一个更严重的问题是您尝试重构 goto 的流程结构。当我使用 f2matlab(以及我编写的一个未发布的工具 remgoto)转换它时,它提出了以下流程结构。我希望这对您有所帮助。请注意,这都是未经测试的!

function [d,e,n,np,z]=tqli(d,e,n,np,z);

remg([1:2])=true;

for i = 2 : n;
 e(i-1) = e(i);
end
e(n) = 0.;
for l = 1 : n;
 while (1);
  if(remg(2))
   if(remg(1))
    iter = 0;
   end;
   remg(1)=true;
   for m = l : n - 1;
    dd = abs(d(m)) + abs(d(m+1));
    if( abs(e(m))+dd==dd )
     remg(2)=false;
     break;
    end;
   end;
   if(~(remg(2)))
    continue;
   end;
   m = fix(n);
  end;
  remg(2)=true;
  if( m~=l )
   if( iter==30 )
    disp(['too many iterations in tqli',' -- Hit Return to continue']);
    pause ;
   end;
   iter = fix(iter + 1);
   g =(d(l+1)-d(l))./(2..*e(l));
   r = pythag(g,1.);
   g = d(m) - d(l) + e(l)./(g+(abs(r).*sign(g)));
   s = 1.;
   c = 1.;
   p = 0.;
   for i = m - 1 : -1: l ;
    f = s.*e(i);
    b = c.*e(i);
    r = pythag(f,g);
    e(i+1) = r;
    if( r==0. )
     d(i+1) = d(i+1) - p;
     e(m) = 0.;
     remg(1)=false;
     break;
    end;
    s = f./r;
    c = g./r;
    g = d(i+1) - p;
    r =(d(i)-g).*s + 2..*c.*b;
    p = s.*r;
    d(i+1) = g + p;
    g = c.*r - b;
    %        Omit lines from here ...
    for k = 1 : n;
     f = z(k,i+1);
     z(k,i+1) = s.*z(k,i) + c.*f;
     z(k,i) = c.*z(k,i) - s.*f;
    end; k = fix(n+1);
    %        ... to here when finding only eigenvalues.
   end;
   if(~(remg(1)))
    continue;
   end;
   d(l) = d(l) - p;
   e(l) = g;
   e(m) = 0.;
   remg(1)=false;
   continue;
  end;
  break;
 end;
end;
end %subroutine tqli
于 2012-07-30T11:44:12.253 回答