8

これは、1ページのグラムシュミットを実行するためのMATLABコードです http://web.mit.edu/18.06/www/Essays/gramschmidtmat.pdf

私はMATLABを持っていないので、Rでこれを実行するために何時間も何時間も試みていますこれが私のRです

f=function(x){
m=nrow(x);
n=ncol(x);
Q=matrix(0,m,n);
R=matrix(0,n,n);

for(j in 1:n){
v=x[,j,drop=FALSE];

for(i in 1:j-1){
R[i,j]=t(Q[,i,drop=FALSE])%*%x[,j,drop=FALSE];
v=v-R[i,j]%*%Q[,i,drop=FALSE]
}

R[j,j]=max(svd(v)$d);
Q[,j,,drop=FALSE]=v/R[j,j]}

return(list(Q,R))}

それはどちらかにエラーがあると言い続けます:

v=v-R[i,j]%*%Q[,i,drop=FALSE] 

また

R[j,j]=max(svd(v)$d);

MATLABコードをRに間違って変換しているのは何ですか?

4

4 に答える 4

11

楽しみのために、このコードのArmadilloバージョンを追加し、ベンチマークしました

アルマジロコード:

#include <RcppArmadillo.h>
// [[Rcpp::depends(RcppArmadillo)]]

using namespace Rcpp;

//[[Rcpp::export]]
List grahm_schimdtCpp(arma::mat A) {
    int n = A.n_cols;
    int m = A.n_rows;
    arma::mat Q(m, n);
    Q.fill(0);
    arma::mat R(n, n);
    R.fill(0);  
    for (int j = 0; j < n; j++) {
    arma::vec v = A.col(j);
    if (j > 0) {
        for(int i = 0; i < j; i++) {
        R(i, j) = arma::as_scalar(Q.col(i).t() *  A.col(j));
        v = v - R(i, j) * Q.col(i);
        }
    }
    R(j, j) = arma::norm(v, 2);
    Q.col(j) = v / R(j, j);
    }
    return List::create(_["Q"] = Q,
                     _["R"] = R
    );
    }

最適化されていないRコード(アルゴリズムに直接基づく)

grahm_schimdtR <- function(A) {
    m <- nrow(A)
    n <- ncol(A)
    Q <- matrix(0, nrow = m, ncol = n)
    R <- matrix(0, nrow = n, ncol = n)
    for (j in 1:n) {
    v <- A[ , j, drop = FALSE]
        if (j > 1) {
    for(i in 1:(j-1)) {
            R[i, j] <- t(Q[,i,drop = FALSE]) %*% A[ , j, drop = FALSE]
            v <- v - R[i, j] * Q[ ,i]
    }
    }
    R[j, j] = norm(v, type = "2")
    Q[ ,j] = v / R[j, j]
    }

    list("Q" = Q, "R" = R)

}

RでのネイティブQR分解

qrNative <- function(A) {
    qrdec <- qr(A)
    list(Q = qr.R(qrdec), R = qr.Q(qrdec))
}

元のドキュメントと同じマトリックスでテストします(上記の投稿のリンク)

A <- matrix(c(4, 3, -2, 1), ncol = 2)

all.equal(grahm_schimdtR(A)$Q %*% grahm_schimdtR(A)$R, A)
## [1] TRUE

all.equal(grahm_schimdtCpp(A)$Q %*% grahm_schimdtCpp(A)$R, A)
## [1] TRUE

all.equal(qrNative(A)$Q %*% qrNative(A)$R, A)
## [1] TRUE

それではベンチマークを行いましょう

require(rbenchmark)
set.seed(123)
A <- matrix(rnorm(10000), 100, 100)
benchmark(qrNative(A),
          grahm_schimdtR(A),
          grahm_schimdtCpp(A),
          order = "elapsed")
##                  test replications elapsed relative user.self
## 3 grahm_schimdtCpp(A)          100   0.272    1.000     0.272
## 1         qrNative(A)          100   1.013    3.724     1.144
## 2   grahm_schimdtR(A)          100  84.279  309.849    95.042
##   sys.self user.child sys.child
## 3    0.000          0         0
## 1    0.872          0         0
## 2   72.577          0         0

コードをRcppに移植するのがいかに簡単かが本当に気に入っています。

于 2013-03-23T16:51:50.307 に答える
7

MatlabのコードをRに変換する場合、コードのセマンティクス(コードロジック)は同じままである必要があります。たとえば、コードでは、指定さt(Q[,i,drop=FALSE])れたMatlabコードに従ってQを転置しています。ただしQ[,i,drop=FALSE]、列ベクトルの列は返されません。したがって、次のステートメントを使用して、列ベクトルにすることができます。

matrix(Q[,i],n,1); # n is the number of rows.

がベクトル(行または列)のR[j,j]=max(svd(v)$d)場合、エラーは発生しません。v

はい、エラーがあります

v=v-R[i,j]%*%Q[,i,drop=FALSE]

行列の乗算を使用しているためです。代わりに、通常の乗算​​を使用する必要があります。

v=v-R[i,j] * Q[,i,drop=FALSE]

ここR[i,j]に数値がありますQ[,i,drop=FALSE]が、はベクトルです。したがって、ここで寸法の不一致が発生します。

もう1つ、jが3の場合、 1:j-1[0,1,2]を返します。したがって、これをに変更する必要が1:(j-1)あります。これは、の同じ値に対して[1,2]を返しますj。しかし、落とし穴があります。jが2の場合、 1:(j-1)[1,0]を返します。したがって、0番目のインデックスはベクトルまたは行列に対して未定義です。0したがって、条件式を配置することで値をバイパスできます。

グラムシュミットアルゴリズムの動作コードは次のとおりです。

A = matrix(c(4,3,-2,1),2,2)
m = nrow(A)
n = ncol(A)
Q = matrix(0,m,n)
R = matrix(0,n,n)

for(j in 1:n)
{
    v = matrix(A[,j],n,1)
    for(i in 1:(j-1))
    {
        if(i!=0)
        {
            R[i,j] = t(matrix(Q[,i],n,1))%*%matrix(A[,j],n,1)
            v = v - (R[i,j] * matrix(Q[,i],n,1))
        }
    }
    R[j,j] = svd(v)$d 
    Q[,j] = v/R[j,j]
}

コードを関数にラップする必要がある場合は、都合に合わせてラップできます。

于 2013-03-23T08:43:53.023 に答える
4

ここでは、あなたのバージョンと非常によく似ていますが、余分な変数を使用していません。v。私はQマトリックスを直接使用しています。したがって、を使用する必要はありませんdrop。もちろんj-1、インデックスにあるので、条件を追加する必要がありますj>1

f=function(x){
  m <- nrow(x)
  n <- ncol(x)
  Q <- matrix(0, m, n)
  R <- matrix(0, n, n)
  for (j in 1:n) {
    Q[, j] <- x[, j]
    if (j > 1) {
      for (i in 1:(j - 1)) {
        R[i, j] <- t(Q[, i]) %*% Q[, j]
        Q[, j] <- Q[, j] - R[i, j] * Q[, i]
      }
    }
    R[j, j] <- max(svd(Q[, j])$d)
    Q[, j] <- Q[, j]/R[j, j]
  }
  return(list(Q = Q, R = R))
}

編集いくつかのベンチマークを追加します:

実際のケースを取得するために、パッケージのHilbertマトリックスを使用します。Matrix

library(microbenchmark)
library(Matrix)
A <- as.matrix(Hilbert(100))
microbenchmark(grahm_schimdtR(A),
               grahm_schimdtCpp(A),times = 100L)

Unit: milliseconds
expr       min         lq     median        uq        max neval
grahm_schimdtR(A) 330.77424 335.648063 337.443273 343.72888 601.793201   100
grahm_schimdtCpp(A)   1.45445   1.510768   1.615255   1.66816   2.062018   100

予想通り、 CPPソリューションは本当にfsterです。

于 2013-03-23T09:04:23.020 に答える
4

Rで変換された多くのOctave/Matlab関数を提供するHansW.Borchersのpracmaパッケージを使用するだけで済みます。

> library(pracma)
> gramSchmidt
function (A, tol = .Machine$double.eps^0.5) 
{
    stopifnot(is.numeric(A), is.matrix(A))
    m <- nrow(A)
    n <- ncol(A)
    if (m < n) 
        stop("No. of rows of 'A' must be greater or equal no. of colums.")
    Q <- matrix(0, m, n)
    R <- matrix(0, n, n)
    for (k in 1:n) {
        Q[, k] <- A[, k]
        if (k > 1) {
            for (i in 1:(k - 1)) {
                R[i, k] <- t(Q[, i]) %*% Q[, k]
                Q[, k] <- Q[, k] - R[i, k] * Q[, i]
            }
        }
        R[k, k] <- Norm(Q[, k])
        if (abs(R[k, k]) <= tol) 
            stop("Matrix 'A' does not have full rank.")
        Q[, k] <- Q[, k]/R[k, k]
    }
    return(list(Q = Q, R = R))
}
<environment: namespace:pracma>
于 2013-03-24T10:25:37.277 に答える