matlab精度决定因素问题

时间:2010-05-07 13:06:20

标签: matlab matrix fortran precision determinants

我有以下程序

format compact; format short g; clear; clc;  
L = 140; J = 77; Jm = 10540; G = 0.8*10^8; d = L/3;  
for i=1:500000  
omegan=1.+0.0001*i;

a(1,1) = ((omegan^2)*(Jm/(G*J))*d^2)-2; a(1,2) = 2; a(1,3) = 0; a(1,4) = 0;
a(2,1) = 1; a(2,2) = ((omegan^2)*(Jm/(G*J))*d^2)-2; a(2,3) = 1; a(2,4) = 0;
a(3,1) = 0; a(3,2) = 1; a(3,3) = ((omegan^2)*(Jm/(G*J))*d^2)-2; a(3,4) = 1;
a(4,1) = 0; a(4,2) = 0; a(4,3) = 2; a(4,4) = ((omegan^2)*(Jm/(G*J))*d^2)-2;

if(abs(det(a))<1E-10) sprintf('omegan= %8.3f det= %8.3f',omegan,det(a))
end          
end

上述系统的解析解,同样program written in fortran给出的omegan值等于16.3818和32.7636(fortran值;分析略有不同,但它们在某处)。

所以,现在我想知道......我在哪里错了?为什么matlab没有给出预期的结果?

(这可能非常简单,但令我头疼)

3 个答案:

答案 0 :(得分:3)

你正在寻找太小的行列式值,因为Matlab正在使用不同的行列式函数(或者其他一些原因,比如与两种不同方法中涉及的浮点精度有关)。我将向您展示Matlab实际上为您提供了正确的值,并且更好地解决了这个问题。

首先,让我们把你的代码稍微改一下。

format compact; format short g; clear; clc;
L = 140; J = 77; Jm = 10540; G = 0.8*10^8; d = L/3;
vals = zeros(1,500000);
for i=1:500000
    omegan=1.+0.0001*i;

    a(1,1) = ((omegan^2)*(Jm/(G*J))*d^2)-2; a(1,2) = 2; a(1,3) = 0; a(1,4) = 0;
    a(2,1) = 1; a(2,2) = ((omegan^2)*(Jm/(G*J))*d^2)-2; a(2,3) = 1; a(2,4) = 0;
    a(3,1) = 0; a(3,2) = 1; a(3,3) = ((omegan^2)*(Jm/(G*J))*d^2)-2; a(3,4) = 1;
    a(4,1) = 0; a(4,2) = 0; a(4,3) = 2; a(4,4) = ((omegan^2)*(Jm/(G*J))*d^2)-2;
    vals(i) = abs(det(a));
    if(vals(i)<1E-10)
        sprintf('omegan= %8.3f det= %8.3f',omegan,det(a))
    end
end
plot(1.+0.0001*(1:500000),log(vals))

我所做的一切都是记录了所有omegan值的行列式值,并将这些决定因素值的对数绘制为omegan的函数。这是情节:

alt text

您注意到图表中有三个主要下降。两个与16.3818和32.7636的结果一致,但是还有一个你缺少的(可能是因为你的行列式小于1e-10的条件太低了,即使你的Fortran代码拿起它也是如此)。因此,Matlab也告诉你那些是你正在寻找的omegan的值,但是因为决定因素是在Matlab中以不同的方式确定的,所以这些值并不相同 - 在处理条件差的矩阵时也就不足为奇了。此外,正如其他人所说,它可能与使用单精度浮动的Fortran有关。我不打算研究为什么他们不是因为我不想浪费我的时间。相反,让我们看看你想要做什么,并尝试不同的方法。

我确信你知道,你正试图找到矩阵的特征值

a = [[-2 2 0 0]; [1 -2 1 0]; [0 1 -2 1]; [0 0 2 -2]];

,将它们设为等于

-omegan^2*(Jm/(G*J)*d^2)

并解决omegan。这就是我的方式:

format compact; format short g; clear; clc;
L = 140; J = 77; Jm = 10540; G = 0.8*10^8; d = L/3;
C1 = (Jm/(G*J)*d^2);
a = [[-2 2 0 0]; [1 -2 1 0]; [0 1 -2 1]; [0,0,2,-2]];
myeigs = eig(a);
myeigs(abs(myeigs) < eps) = 0.0;
for i=1:4
    sprintf('omegan= %8.3f', sqrt(-myeigs(i)/C1))
end

这为您提供了所有四种解决方案 - 不仅仅是您使用Fortran代码找到的两种解决方案(尽管其中一种,零,超出了omegan的测试范围)。如果你想通过检查Matlab中的行列式来解决这个问题,就像你一直试图做的那样,那么你将不得不使用你正在检查行列式的绝对值小于的值。我得到它的值为1e-4(它提供了3个解决方案:16.382,28.374和32.764)。

很抱歉这么长的解决方案,但希望它有所帮助。

<强>更新

在我上面的第一段代码中,我替换了

vals(i) = abs(det(a));

[L,U] = lu(a);
s = det(L);
vals(i) = abs(s*prod(diag(U)));

这是根据Matlab文档使用det的算法。现在,我可以使用1E-10作为条件并且它可以工作。那么也许Matlab并不像文档所说的那样完全计算行列式?这有点令人不安。

答案 1 :(得分:2)

新答案:

你可以使用符号方程来研究这个问题,它给出了正确的答案:

>> clear all             %# Clear all existing variables
>> format long           %# Display more digits of precision
>> syms Jm d omegan G J  %# Your symbolic variables
>> a = ((Jm*(d*omegan)^2)/(G*J)-2).*eye(4)+...  %# Create the matrix a
       diag([2 1 1],1)+...
       diag([1 1 2],-1);
>> solns = solve(det(a),'omegan')  %# Solve for where the determinant is 0

solns =

                                0
                                0
            (G*J*Jm)^(1/2)/(Jm*d)
           -(G*J*Jm)^(1/2)/(Jm*d)
       -(2*(G*J*Jm)^(1/2))/(Jm*d)
        (2*(G*J*Jm)^(1/2))/(Jm*d)
  (3^(1/2)*(G*J*Jm)^(1/2))/(Jm*d)
 -(3^(1/2)*(G*J*Jm)^(1/2))/(Jm*d)

>> solns = subs(solns,{G,J,Jm,d},{8e7,77,10540,140/3})  %# Substitute values

solns =

                   0
                   0
  16.381862247021893
 -16.381862247021893
 -32.763724494043785
  32.763724494043785
  28.374217734436371
 -28.374217734436371

我认为你要么只是在你的循环中选择的值足够接近omegan的解决方案,要么你的阈值与行列式为零的距离过于严格。当我将给定值插入a时,以及omegan = 16.3819(这是您的循环产生的一个解决方案的最接近值),我得到了这个:

>> det(subs(a,{omegan,G,J,Jm,d},{16.3819,8e7,77,10540,140/3}))

ans =

    2.765476845475786e-005

绝对振幅仍大于1e-10。

答案 2 :(得分:1)

我把它作为答案,因为我无法将其粘贴到评论中:以下是Matlab如何计算determinant。我假设舍入误差来自于计算U中多个对角元素的乘积。

  

算法

     

行列式是从   获得的三角因子   高斯消除

[L,U] = lu(A) s =  det(L)        
%# This is always +1 or -1  
det(A) = s*prod(diag(U))