Come segue al mio precedente post su questo argomento, voglio condividere alcune esplorazioni provvisorie (anche se incomplete) delle funzioni dietro l'algebra lineare e le relative funzioni R. Questo dovrebbe essere un lavoro in corso.
Parte dell'opacità delle funzioni ha a che fare con la forma "compatta" della decomposizione Householder . L'idea alla base della scomposizione della Capofamiglia è quella di riflettere i vettori attraverso un iperpiano determinato da un vettore unitario come nel diagramma seguente, ma selezionando questo piano in modo mirato in modo da proiettare ogni vettore di colonna della matrice originale sul vettore unità standard . Il vettore norm-2 normalizzato può essere utilizzato per calcolare le diverse trasformazioni della famiglia .u A e 1 1 u I - 2QRuAe11uI−2uuTx
La proiezione risultante può essere espressa come
sign(xi=x1)×∥x∥⎡⎣⎢⎢⎢⎢⎢⎢⎢100⋮0⎤⎦⎥⎥⎥⎥⎥⎥⎥+⎡⎣⎢⎢⎢⎢⎢⎢⎢x1x2x3⋮xm⎤⎦⎥⎥⎥⎥⎥⎥⎥
Il vettore rappresenta la differenza tra i vettori di colonna nella matrice che vogliamo scomporre e i vettori corrispondenti al riflesso attraverso il sottospazio o "specchio" determinati da .vxAyu
Il metodo usato da LAPACK libera la necessità di conservare la prima voce nei riflettori Householder trasformandoli in . Invece di normalizzare il vettore in con , è solo la voce del pugno che viene convertita in ; tuttavia, questi nuovi vettori - chiamali possono ancora essere usati come vettori direzionali.1vu∥u∥=11w
La bellezza del metodo è che dato che in una decomposizione è triangolare superiore, possiamo effettivamente sfruttare gli elementi in sotto la diagonale per riempirli con questi riflettori . Per fortuna, le voci principali in questi vettori sono tutte uguali a , evitando un problema nella diagonale "contestata" della matrice: sapendo che sono tutte non è necessario includerle e possono restituire la diagonale alle voci di .RQR0Rw11R
La matrice "QR compatto" nella funzione qr()$qr
può essere intesa approssimativamente come l'aggiunta della matrice e della matrice triangolare "memoria" inferiore per i riflettori "modificati".R
La proiezione di Household avrà ancora la forma , ma non lavoreremo con ( ), ma piuttosto con un vettore , di cui solo la prima voce è garantita come eI−2uuTxu∥x∥=1w1
I−2uuTx=I−2w∥w∥wT∥w∥x=I−2wwT∥w∥2x(1) .
Si potrebbe presumere che sarebbe giusto memorizzare questi riflettori sotto la diagonale o escludendo la prima voce di e chiamarla un giorno. Tuttavia, le cose non sono mai così facili. Invece ciò che è memorizzato al di sotto della diagonale in è una combinazione di e dei coefficienti nella trasformazione della famiglia espressa come (1), tale che, definendo
come:wR1qr()$qr
wtau
τ=wTw2=∥w∥2 , i riflettori possono essere espressi come . Questi vettori "riflettori" sono quelli memorizzati proprio sotto nel cosiddetto "compatto ".reflectors=w/τRQR
Ora siamo a un grado di distanza dai vettori e la prima voce non è più , quindi l'output di dovrà includere la chiave per ripristinarli poiché insistiamo sull'esclusione della prima voce dei vettori "riflettore" per si adatta a tutto . Quindi stiamo vedendo i valori nell'output? Bene, no, sarebbe prevedibile. Invece nell'output di (dove è memorizzata questa chiave) troviamo .w1qr()
qr()$qr
τqr()$qraux
ρ=∑reflectors22=wTwτ2/2
Così incorniciati in rosso sotto, vediamo i "riflettori" ( ), escluso il loro primo ingresso.w/τ
Tutto il codice è qui , ma poiché questa risposta riguarda l'intersezione di codifica e algebra lineare, incollerò l'output per facilità:
options(scipen=999)
set.seed(13)
(X = matrix(c(rnorm(16)), nrow=4, byrow=F))
[,1] [,2] [,3] [,4]
[1,] 0.5543269 1.1425261 -0.3653828 -1.3609845
[2,] -0.2802719 0.4155261 1.1051443 -1.8560272
[3,] 1.7751634 1.2295066 -1.0935940 -0.4398554
[4,] 0.1873201 0.2366797 0.4618709 -0.1939469
Ora ho scritto la funzione House()
come segue:
House = function(A){
Q = diag(nrow(A))
reflectors = matrix(0,nrow=nrow(A),ncol=ncol(A))
for(r in 1:(nrow(A) - 1)){
# We will apply Householder to progressively the columns in A, decreasing 1 element at a time.
x = A[r:nrow(A), r]
# We now get the vector v, starting with first entry = norm-2 of x[i] times 1
# The sign is to avoid computational issues
first = (sign(x[1]) * sqrt(sum(x^2))) + x[1]
# We get the rest of v, which is x unchanged, since e1 = [1, 0, 0, ..., 0]
# We go the the last column / row, hence the if statement:
v = if(length(x) > 1){c(first, x[2:length(x)])}else{v = c(first)}
# Now we make the first entry unitary:
w = v/first
# Tau will be used in the Householder transform, so here it goes:
t = as.numeric(t(w)%*%w) / 2
# And the "reflectors" are stored as in the R qr()$qr function:
reflectors[r: nrow(A), r] = w/t
# The Householder tranformation is:
I = diag(length(r:nrow(A)))
H.transf = I - 1/t * (w %*% t(w))
H_i = diag(nrow(A))
H_i[r:nrow(A),r:ncol(A)] = H.transf
# And we apply the Householder reflection - we left multiply the entire A or Q
A = H_i %*% A
Q = H_i %*% Q
}
DECOMPOSITION = list("Q"= t(Q), "R"= round(A,7),
"compact Q as in qr()$qr"=
((A*upper.tri(A,diag=T))+(reflectors*lower.tri(reflectors,diag=F))),
"reflectors" = reflectors,
"rho"=c(apply(reflectors[,1:(ncol(reflectors)- 1)], 2,
function(x) sum(x^2) / 2), A[nrow(A),ncol(A)]))
return(DECOMPOSITION)
}
Confrontiamo l'output con le funzioni integrate di R. Innanzitutto la funzione fatta in casa:
(H = House(X))
$Q
[,1] [,2] [,3] [,4]
[1,] -0.29329367 -0.73996967 0.5382474 0.2769719
[2,] 0.14829152 -0.65124800 -0.5656093 -0.4837063
[3,] -0.93923665 0.13835611 -0.1947321 -0.2465187
[4,] -0.09911084 -0.09580458 -0.5936794 0.7928072
$R
[,1] [,2] [,3] [,4]
[1,] -1.890006 -1.4517318 1.2524151 0.5562856
[2,] 0.000000 -0.9686105 -0.6449056 2.1735456
[3,] 0.000000 0.0000000 -0.8829916 0.5180361
[4,] 0.000000 0.0000000 0.0000000 0.4754876
$`compact Q as in qr()$qr`
[,1] [,2] [,3] [,4]
[1,] -1.89000649 -1.45173183 1.2524151 0.5562856
[2,] -0.14829152 -0.96861050 -0.6449056 2.1735456
[3,] 0.93923665 -0.67574886 -0.8829916 0.5180361
[4,] 0.09911084 0.03909742 0.6235799 0.4754876
$reflectors
[,1] [,2] [,3] [,4]
[1,] 1.29329367 0.00000000 0.0000000 0
[2,] -0.14829152 1.73609434 0.0000000 0
[3,] 0.93923665 -0.67574886 1.7817597 0
[4,] 0.09911084 0.03909742 0.6235799 0
$rho
[1] 1.2932937 1.7360943 1.7817597 0.4754876
alle funzioni R:
qr.Q(qr(X))
[,1] [,2] [,3] [,4]
[1,] -0.29329367 -0.73996967 0.5382474 0.2769719
[2,] 0.14829152 -0.65124800 -0.5656093 -0.4837063
[3,] -0.93923665 0.13835611 -0.1947321 -0.2465187
[4,] -0.09911084 -0.09580458 -0.5936794 0.7928072
qr.R(qr(X))
[,1] [,2] [,3] [,4]
[1,] -1.890006 -1.4517318 1.2524151 0.5562856
[2,] 0.000000 -0.9686105 -0.6449056 2.1735456
[3,] 0.000000 0.0000000 -0.8829916 0.5180361
[4,] 0.000000 0.0000000 0.0000000 0.4754876
$qr
[,1] [,2] [,3] [,4]
[1,] -1.89000649 -1.45173183 1.2524151 0.5562856
[2,] -0.14829152 -0.96861050 -0.6449056 2.1735456
[3,] 0.93923665 -0.67574886 -0.8829916 0.5180361
[4,] 0.09911084 0.03909742 0.6235799 0.4754876
$qraux
[1] 1.2932937 1.7360943 1.7817597 0.4754876
qr.qy()
concordano con i calcoli manualiqr.Q(qr(X))
seguiti daQ%*%z
sul mio post. Mi chiedo davvero se posso dire qualcosa di diverso per rispondere alla tua domanda senza duplicazioni. Non voglio davvero fare un brutto lavoro ... Ho letto abbastanza dei tuoi post per avere molto rispetto per te ... Se trovo un modo per esprimere il concetto senza codice, concettualmente attraverso l'algebra lineare, Ci tornerò. Sono felice, tuttavia, che tu abbia trovato la mia esplorazione della questione di un certo valore. I migliori auguri, Toni.