Fortran 中的 QR 分解

问题描述 投票:0回答:1

我正在尝试构建一个程序来进行矩阵的 QR 分解

我试过这个:

program QR_Factorization
    implicit none
    
    integer, parameter :: n = 3
    real(8), dimension(1:9) :: AA =(/1, 6, -4, -51, 167, 24, 4, -68, -41/)
    real(8), dimension(n,n) :: A, Q, R
    real(8), dimension(n) :: v
    real(8) :: alpha
    integer :: i, j, k

    ! Initialize matrix A (replace with your own matrix)
    A = reshape( AA, (/n,n/))
    
    print *, "Matrix A:"
    do i = 1, n
        print *, A(i, :)
    end do

    ! Initialize Q as an identity matrix
    Q = identity_matrix(n)
    
    print *, "Matrix Q:"
    do i = 1, n
        print *, Q(i, :)
    end do

    ! Perform QR Factorization
    do k = 1, n-1
        call householder(A(:, k:n),k , v)
        A(k:n, k:n) = A(k:n, k:n) - 2.0 * outer_product(v, v)* A(k:n, k:n)
        Q(:, k:n) = Q(k:n, k:n) - 2.0 * outer_product(v, v)* Q(k:n, k:n)
    end do

    ! Display results
    print *, "Matrix A:"
    do i = 1, n
        print *, A(i, :)
    end do

    print *, "Matrix Q:"
    do i = 1, n
        print *, Q(i, :)
    end do

    print *, "Matrix R:"
    R = matmul(transpose(Q), A)
    do i = 1, n
        print *, R(i, :)
    end do

contains

    subroutine householder(A, k, v)
        real(8), dimension(:,:) :: A
        real(8), dimension(:) :: v
        real(8) :: alpha, beta
        integer, intent(in) :: k
        integer :: i
        
        alpha = sqrt(sum(A(k:n, k)**2))
        if (A(k, k) < 0.0) alpha = -alpha
        beta = 1.0 / (alpha * (alpha - A(k, k)))
        
        v = 0.0
        v(k) = 1.0
        v(k+1:) = A(k+1:, k) / A(k, k)
        
        v = beta * v
    end subroutine householder


    function identity_matrix(n) result(mat)
        integer, intent(in) :: n
        real(8), dimension(n, n) :: mat
        integer :: i, j

        mat = 0.0
        do i = 1, n
            mat(i, i) = 1.0
        end do
    end function identity_matrix

    function outer_product(u, v) result(mat)
        real(8), dimension(:), intent(in) :: u, v
        real(8), dimension(size(u), size(v)) :: mat
        integer :: i, j

        mat = 0.0
        do i = 1, size(u)
            do j = 1, size(v)
                mat(i, j) = u(i) * v(j)
            end do
        end do
    end function outer_product

end program QR_Factorization

得到了这个:

Matrix A:
   1.0000000000000000       -51.000000000000000        4.0000000000000000     
   6.0000000000000000        167.00000000000000       -68.000000000000000     
  -4.0000000000000000        24.000000000000000       -41.000000000000000     
 Matrix Q:
   1.0000000000000000        0.0000000000000000        0.0000000000000000     
   0.0000000000000000        1.0000000000000000        0.0000000000000000     
   0.0000000000000000        0.0000000000000000        1.0000000000000000     
 Matrix A:
   5.9655462561413701       -50.707220229258688        4.0153087461825523     
  -4.0153052049990920        161.24773862190591       -69.561492110620350     
  -4.0153087461825523        24.551114862571886       -40.372235704486464     
 Matrix Q:
  0.99904320336359043       0.96555532108925701        0.0000000000000000     
   0.0000000000000000        0.0000000000000000       0.98468867571918195     
   0.0000000000000000        0.0000000000000000        4.2439851328916695E-314
 Matrix R:
   5.9598384415491488       -50.658703731501653        4.0114669122800590     
   5.7600649308213958       -48.960626310005544        3.8770027256927961     
  -3.9538255649188945        158.77882220631733       -68.496413547457081  

我知道这是完全错误的。对于愚蠢的错误,我提前表示歉意,我对 Fortran 很陌生。

编辑:

好吧,所以我做了更改(添加辅助矩阵 B 来进行计算并使用 real64),但我仍然得到错误的矩阵:

program QR_Factorization
    use iso_Fortran_env, only: real64
    implicit none
    
    integer, parameter :: n = 3
    real(real64), dimension(1:9) :: AA =(/1, 6, -4, -51, 167, 24, 4, -68, -41/)
    real(real64), dimension(n,n) :: A, Q, R, B
    real(real64), dimension(n) :: v
    real(real64) :: alpha
    integer :: i, j, k

    ! Initialize matrix A (replace with your own matrix)
    A = reshape( AA, (/n,n/))
    B = A
    
    
    
    print *, "Matrix A:"
    do i = 1, n
        print *, A(i, :)
    end do

    ! Initialize Q as an identity matrix
    Q = identity_matrix(n)
    
    print *, "Matrix Q:"
    do i = 1, n
        print *, Q(i, :)
    end do

    ! Perform QR Factorization
    do k = 1, n-1
        call householder(B(:, k:n),k , v)
        B(k:n, k:n) = B(k:n, k:n) - 2._real64 * outer_product(v, v)* B(k:n, k:n)
        Q(:, k:n) = Q(k:n, k:n) - 2._real64 * outer_product(v, v)* Q(k:n, k:n)
    end do

    ! Display results
    print *, "Matrix A:"
    do i = 1, n
        print *, A(i, :)
    end do

    print *, "Matrix Q:"
    do i = 1, n
        print *, Q(i, :)
    end do

    print *, "Matrix R:"
    R = matmul(transpose(Q), A)
    do i = 1, n
        print *, R(i, :)
    end do

contains

    subroutine householder(A, k, v)
        real(real64), dimension(:,:) :: A
        real(real64), dimension(:) :: v
        real(real64) :: alpha, beta
        integer, intent(in) :: k
        integer :: i
        
        alpha = sqrt(sum(A(k:n, k)**2))
        if (A(k, k) < 0._real64) alpha = -alpha
        beta = 1.0 / (alpha * (alpha - A(k, k)))
        
        v = 0._real64
        v(k) = 1._real64
        v(k+1:) = A(k+1:, k) / A(k, k)
        
        v = beta * v
    end subroutine householder


    function identity_matrix(n) result(mat)
        integer, intent(in) :: n
        real(real64), dimension(n, n) :: mat
        integer :: i, j

        mat = 0._real64
        do i = 1, n
            mat(i, i) = 1._real64
        end do
    end function identity_matrix

    function outer_product(u, v) result(mat)
        real(real64), dimension(:), intent(in) :: u, v
        real(real64), dimension(size(u), size(v)) :: mat
        integer :: i, j

        mat = 0._real64
        do i = 1, size(u)
            do j = 1, size(v)
                mat(i, j) = u(i) * v(j)
            end do
        end do
    end function outer_product

end program QR_Factorization

有人有一个不使用 Lapack 的 QR 分解代码我可以看一下吗?

fortran linear-algebra
1个回答
0
投票

我整理了你的主程序和v向量的评估。还添加了一个辅助例程来打印矩阵。我没有理会你的其他日常事务。

确保您进行了适当的检查:即 Q 是正交的 (QT.Q=I),R 是上三角的(您可能仍然需要清理它 - 有些元素很小,而不是 0)并且 QR 等于您的原始矩阵.

以下是基本版本。

program QR_Factorization
   use iso_Fortran_env, only: real64
   implicit none
   integer, parameter :: n = 3
   real(real64), dimension(1:9) :: AA = (/1, 6, -4, -51, 167, 24, 4, -68, -41 /)
   real(real64), dimension(n,n) :: A, Q, R, Qk
   real(real64), dimension(n) :: v
   integer k
   character( len=* ), parameter :: FMT = "( *( f8.3 ) )"

   ! Initialize matrix A (replace with your own matrix)
   A = reshape( AA, (/ n, n /) )

   ! Initialise Q and R
   Q = identity_matrix( n )
   R = A

   ! Perform QR Factorization
   do k = 1, n-1
      call householder( R, k, v )
      Qk = identity_matrix(n)
      Qk(k:n,k:n) = Qk(k:n,k:n) - 2.0 * outer_product( v(k:n), v(k:n) )

      Q = matmul( Qk, Q )
      R = matmul( Qk, R )
   end do

   Q = transpose( Q )

   ! Display results
   call printMatrix( "Original matrix ", A, FMT )
   call printMatrix( "CHECK QR: ", matmul( Q, R ), FMT )
   call printMatrix( "CHECK ORTHOGONALITY (QT.Q): ", matmul( transpose( Q ), Q ), FMT )
   call printMatrix( "Q: ", Q, FMT )
   call printMatrix( "R: ", R, FMT )

contains

   subroutine printMatrix( text, M, fmt )
      character(len=*), intent( in ) :: text
      real(real64), dimension(:,:), intent(in) :: M
      character(len=*), intent( in ) :: fmt
      integer i

      print *
      print *, text

      do i = 1, size( M, 1 )
         print fmt, M(i,:)
      end do

   end subroutine printMatrix
   

   subroutine householder( R, k, v )
      real(real64), dimension(:,:), intent(in) :: R
      real(real64), dimension(:), intent(out) :: v
      integer, intent(in) :: k
      real(real64) vnorm
      
      v = 0
      v(k) = R(k,k) - norm2( R(k:n,k) )
      v(k+1:) = R(k+1:,k)
      
      vnorm = norm2( v )
      if ( vnorm > 1.0e-10 ) v = v / vnorm

  end subroutine householder

   
   function identity_matrix(n) result(mat)
       integer, intent(in) :: n
       real(real64), dimension(n,n) :: mat
       integer :: i
   
       mat = 0._real64
       do i = 1, n
           mat(i, i) = 1._real64
       end do

   end function identity_matrix
   
   
   function outer_product(u, v) result(mat)
       real(real64), dimension(:), intent(in) :: u, v
       real(real64), dimension(size(u), size(v)) :: mat
       integer :: i, j
   
       mat = 0._real64
       do i = 1, size(u)
           do j = 1, size(v)
               mat(i, j) = u(i) * v(j)
           end do
       end do
   end function outer_product
   
end program QR_Factorization

输出:

 Original matrix 
   1.000 -51.000   4.000
   6.000 167.000 -68.000
  -4.000  24.000 -41.000

 CHECK QR: 
   1.000 -51.000   4.000
   6.000 167.000 -68.000
  -4.000  24.000 -41.000

 CHECK ORTHOGONALITY (QT.Q): 
   1.000  -0.000   0.000
  -0.000   1.000  -0.000
   0.000  -0.000   1.000

 Q: 
   0.137  -0.511   0.849
   0.824   0.534   0.188
  -0.549   0.674   0.494

 R: 
   7.280 117.443 -32.967
   0.000 131.427 -65.986
   0.000   0.000 -29.666
© www.soinside.com 2019 - 2024. All rights reserved.