ম্যাট্রিকের এসভিডি গণনার জন্য একটি সাধারণ অ্যালগরিদম কী ?
আদর্শভাবে, আমি একটি সংখ্যার শক্তিশালী অ্যালগরিদম চাই, তবে আমি সহজ এবং না-সহজ উভয় বাস্তবায়ন দেখতে চাই। সি কোড গৃহীত হয়েছে।
কাগজপত্র বা কোডের কোনও রেফারেন্স?
ম্যাট্রিকের এসভিডি গণনার জন্য একটি সাধারণ অ্যালগরিদম কী ?
আদর্শভাবে, আমি একটি সংখ্যার শক্তিশালী অ্যালগরিদম চাই, তবে আমি সহজ এবং না-সহজ উভয় বাস্তবায়ন দেখতে চাই। সি কোড গৃহীত হয়েছে।
কাগজপত্র বা কোডের কোনও রেফারেন্স?
উত্তর:
Https://math.stackexchange.com/questions/861674/decompose-a-2d-arbitrary-transform-into-only-scaling- and- rotation দেখুন (দুঃখিত, আমি এটিকে একটি মন্তব্যে রেখেছি তবে আমি নিবন্ধিত করেছি কেবল এটি পোস্ট করার জন্য আমি এখনও মন্তব্য পোস্ট করতে পারি না)।
তবে যেহেতু আমি এটি উত্তর হিসাবে লিখছি, আমি পদ্ধতিটিও লিখব:
যা ম্যাট্রিক্সকে নিম্নলিখিতভাবে পচে যায়:
এই পদ্ধতির সাথে রক্ষা করার একমাত্র জিনিস হ'ল এটিএন 2 এর জন্য বা ।আমি সন্দেহ করি যে এর চেয়ে আরও শক্তিশালী আর কিছু হতে পারে( আপডেট: দেখুন অ্যালেক্স ইফতিমিয়াদের উত্তর!)।
তথ্যসূত্রটি হ'ল: http://dx.doi.org/10.1109/38.486688 (সেখানে রাহুল প্রদত্ত) যা এই ব্লগ পোস্টের নীচে থেকে আসে: http://metamerist.blogspot.com/2006/10/linear-algebra -for-গ্রাফিক্স-গুরুদের-svd.html
আপডেট: @ ভিক্টরলিউ একটি মন্তব্যে যেমন উল্লেখ করেছেন, negative ণাত্মক হতে পারে। ইনপুট ম্যাট্রিক্সের নির্ধারকটি যদি নেতিবাচক হয় তবেই তা ঘটে। যদি এটি হয় এবং আপনি ধনাত্মক একবচনীয় মান চান, তবে এর পরম মানটি ধরুন ।
@ পেড্রো গিমেনো
"আমি সন্দেহ করি যে এর চেয়ে আরও শক্তিশালী আর কিছু হতে পারে।"
চ্যালেঞ্জ গ্রহন করা হল.
আমি লক্ষ্য করেছি যে স্বাভাবিক পন্থাটি এটিান 2 এর মতো ট্রিগার ফাংশন ব্যবহার করা। স্বজ্ঞাতভাবে, ট্রিগ ফাংশন ব্যবহার করার প্রয়োজন হবে না। প্রকৃতপক্ষে, সমস্ত ফলাফল সমুদ্র এবং আর্টিকানের কোসাইন হিসাবে শেষ হয় - যা বীজগণিত ফাংশনে সরলীকৃত করা যেতে পারে। এটি বেশ খানিকটা সময় নিয়েছিল, তবে আমি কেবলমাত্র বীজগণিতীয় ফাংশনগুলি ব্যবহার করতে পেড্রোর অ্যালগরিদমকে সহজ করে তুললাম।
নিম্নলিখিত অজগর কোডটি কৌশলটি করে।
ন্যালি আমদানি আসারায় থেকে, ডায়াগDef svd2 (মি):
y1, x1 = (m[1, 0] + m[0, 1]), (m[0, 0] - m[1, 1]) y2, x2 = (m[1, 0] - m[0, 1]), (m[0, 0] + m[1, 1]) h1 = hypot(y1, x1) h2 = hypot(y2, x2) t1 = x1 / h1 t2 = x2 / h2 cc = sqrt((1 + t1) * (1 + t2)) ss = sqrt((1 - t1) * (1 - t2)) cs = sqrt((1 + t1) * (1 - t2)) sc = sqrt((1 - t1) * (1 + t2)) c1, s1 = (cc - ss) / 2, (sc + cs) / 2, u1 = asarray([[c1, -s1], [s1, c1]]) d = asarray([(h1 + h2) / 2, (h1 - h2) / 2]) sigma = diag(d) if h1 != h2: u2 = diag(1 / d).dot(u1.T).dot(m) else: u2 = diag([1 / d[0], 0]).dot(u1.T).dot(m) return u1, sigma, u2
y1
= 0, x1
= 0, h1
= 0, এবং t1
= 0/0 = NaN
।
GSL একটি 2-দ্বারা-2 SVD জন্য প্রধান SVD আলগোরিদিম কিউ পচানি অংশ অন্তর্নিহিত সমাধানকারী হয়েছে gsl_linalg_SV_decomp
। দেখুন svdstep.c
ফাইল এবং জন্য চেহারা svd2
ফাংশন। ফাংশনটিতে কয়েকটি বিশেষ কেস রয়েছে, একেবারে তুচ্ছ নয় এবং সংখ্যাগতভাবে যত্নবান হওয়ার জন্য বেশ কয়েকটি কাজ করা দেখায় (যেমন, hypot
অতিরিক্ত প্রবাহ এড়াতে ব্যবহার করা)।
ChangeLog
আপনি জিএসএল ডাউনলোড করলে ফাইলটিতে কিছুটা আছে । এবং আপনি svd.c
সামগ্রিক অ্যালগরিদমের বিশদটি দেখতে পারেন । একমাত্র সত্য ডকুমেন্টেশন মনে হয় উচ্চ স্তরের ব্যবহারকারী-কলযোগ্য ফাংশনগুলির জন্য, যেমন gsl_linalg_SV_decomp
,।
যখন আমরা "সংখ্যার দিক থেকে দৃust়" বলি তখন আমরা সাধারণত একটি অ্যালগরিদম বোঝায় যেখানে আমরা ত্রুটি প্রচার এড়াতে পাইভটিংয়ের মতো কাজ করি। তবে, 2x2 ম্যাট্রিক্সের জন্য আপনি সুস্পষ্ট সূত্রের শর্তে ফলাফল লিখতে পারেন - যেমন, এসভিডি উপাদানগুলির জন্য সূত্রগুলি লিখুন যা ফলাফলকে কেবল ইনপুটগুলির শর্তে লিখবে, পূর্বে গণনা করা মধ্যবর্তী মানগুলির পরিবর্তে । তার মানে আপনার বাতিল হতে পারে তবে কোনও ত্রুটি প্রচার নেই no
মুল বক্তব্যটি হ'ল 2x2 সিস্টেমের জন্য দৃ about়তা সম্পর্কে চিন্তিত হওয়া জরুরি নয়।
এই কোডটি ব্লিনের কাগজ , এলিস পেপার , এসভিডি বক্তৃতা এবং অতিরিক্ত গণনার উপর ভিত্তি করে । একটি অ্যালগরিদম নিয়মিত এবং একা একক বাস্তব ম্যাট্রিক্স জন্য উপযুক্ত। পূর্ববর্তী সমস্ত সংস্করণ এটির পাশাপাশি 100% কাজ করে।
#include <stdio.h>
#include <math.h>
void svd22(const double a[4], double u[4], double s[2], double v[4]) {
s[0] = (sqrt(pow(a[0] - a[3], 2) + pow(a[1] + a[2], 2)) + sqrt(pow(a[0] + a[3], 2) + pow(a[1] - a[2], 2))) / 2;
s[1] = fabs(s[0] - sqrt(pow(a[0] - a[3], 2) + pow(a[1] + a[2], 2)));
v[2] = (s[0] > s[1]) ? sin((atan2(2 * (a[0] * a[1] + a[2] * a[3]), a[0] * a[0] - a[1] * a[1] + a[2] * a[2] - a[3] * a[3])) / 2) : 0;
v[0] = sqrt(1 - v[2] * v[2]);
v[1] = -v[2];
v[3] = v[0];
u[0] = (s[0] != 0) ? (a[0] * v[0] + a[1] * v[2]) / s[0] : 1;
u[2] = (s[0] != 0) ? (a[2] * v[0] + a[3] * v[2]) / s[0] : 0;
u[1] = (s[1] != 0) ? (a[0] * v[1] + a[1] * v[3]) / s[1] : -u[2];
u[3] = (s[1] != 0) ? (a[2] * v[1] + a[3] * v[3]) / s[1] : u[0];
}
int main() {
double a[4] = {1, 2, 3, 6}, u[4], s[2], v[4];
svd22(a, u, s, v);
printf("Matrix A:\n%f %f\n%f %f\n\n", a[0], a[1], a[2], a[3]);
printf("Matrix U:\n%f %f\n%f %f\n\n", u[0], u[1], u[2], u[3]);
printf("Matrix S:\n%f %f\n%f %f\n\n", s[0], 0, 0, s[1]);
printf("Matrix V:\n%f %f\n%f %f\n\n", v[0], v[1], v[2], v[3]);
}
আমার একটি অ্যালগরিদম দরকার ছিল
মনে আছে
তির্যক ঘূর্ণন গণনা নিম্নলিখিত সমীকরণ সমাধান করে করা যেতে পারে:
কোথায়
template <class T>
void Rq2x2Helper(const Matrix<T, 2, 2>& A, T& x, T& y, T& z, T& c2, T& s2) {
T a = A(0, 0);
T b = A(0, 1);
T c = A(1, 0);
T d = A(1, 1);
if (c == 0) {
x = a;
y = b;
z = d;
c2 = 1;
s2 = 0;
return;
}
T maxden = std::max(abs(c), abs(d));
T rcmaxden = 1/maxden;
c *= rcmaxden;
d *= rcmaxden;
T den = 1/sqrt(c*c + d*d);
T numx = (-b*c + a*d);
T numy = (a*c + b*d);
x = numx * den;
y = numy * den;
z = maxden/den;
s2 = -c * den;
c2 = d * den;
}
template <class T>
void Svd2x2Helper(const Matrix<T, 2, 2>& A, T& c1, T& s1, T& c2, T& s2, T& d1, T& d2) {
// Calculate RQ decomposition of A
T x, y, z;
Rq2x2Helper(A, x, y, z, c2, s2);
// Calculate tangent of rotation on R[x,y;0,z] to diagonalize R^T*R
T scaler = T(1)/std::max(abs(x), abs(y));
T x_ = x*scaler, y_ = y*scaler, z_ = z*scaler;
T numer = ((z_-x_)*(z_+x_)) + y_*y_;
T gamma = x_*y_;
gamma = numer == 0 ? 1 : gamma;
T zeta = numer/gamma;
T t = 2*impl::sign_nonzero(zeta)/(abs(zeta) + sqrt(zeta*zeta+4));
// Calculate sines and cosines
c1 = T(1) / sqrt(T(1) + t*t);
s1 = c1*t;
// Calculate U*S = R*R(c1,s1)
T usa = c1*x - s1*y;
T usb = s1*x + c1*y;
T usc = -s1*z;
T usd = c1*z;
// Update V = R(c1,s1)^T*Q
t = c1*c2 + s1*s2;
s2 = c2*s1 - c1*s2;
c2 = t;
// Separate U and S
d1 = std::hypot(usa, usc);
d2 = std::hypot(usb, usd);
T dmax = std::max(d1, d2);
T usmax1 = d2 > d1 ? usd : usa;
T usmax2 = d2 > d1 ? usb : -usc;
T signd1 = impl::sign_nonzero(x*z);
dmax *= d2 > d1 ? signd1 : 1;
d2 *= signd1;
T rcpdmax = 1/dmax;
c1 = dmax != T(0) ? usmax1 * rcpdmax : T(1);
s1 = dmax != T(0) ? usmax2 * rcpdmax : T(0);
}
আইডিয়াগুলি থেকে:
http://www.cs.utexas.edu/users/inderjit/public_paper/HLA_SVD.pdf
http://www.math.pitt.edu/~sussmanm/2071Spring08/lab09/index.html
http: // www.lucidarme.me/singular-value-decomposition-of-a-2x2-matrix/
আমি এই সি ++ কোডটি তৈরি করতে http://www.lucidarme.me/?p=4624 এ বর্ণনাটি ব্যবহার করেছি । ম্যাট্রিকগুলি ইগেন লাইব্রেরির মধ্যে একটি, তবে আপনি সহজেই এই উদাহরণ থেকে আপনার নিজস্ব ডেটা কাঠামো তৈরি করতে পারেন:
#include <cmath>
#include <Eigen/Core>
using namespace Eigen;
Matrix2d A;
// ... fill A
double a = A(0,0);
double b = A(0,1);
double c = A(1,0);
double d = A(1,1);
double Theta = 0.5 * atan2(2*a*c + 2*b*d,
a*a + b*b - c*c - d*d);
// calculate U
Matrix2d U;
U << cos(Theta), -sin(Theta), sin(Theta), cos(Theta);
double Phi = 0.5 * atan2(2*a*b + 2*c*d,
a*a - b*b + c*c - d*d);
double s11 = ( a*cos(Theta) + c*sin(Theta))*cos(Phi) +
( b*cos(Theta) + d*sin(Theta))*sin(Phi);
double s22 = ( a*sin(Theta) - c*cos(Theta))*sin(Phi) +
(-b*sin(Theta) + d*cos(Theta))*cos(Phi);
// calculate S
S1 = a*a + b*b + c*c + d*d;
S2 = sqrt(pow(a*a + b*b - c*c - d*d, 2) + 4*pow(a*c + b*d, 2));
Matrix2d Sigma;
Sigma << sqrt((S1+S2) / 2), 0, 0, sqrt((S1-S2) / 2);
// calculate V
Matrix2d V;
V << signum(s11)*cos(Phi), -signum(s22)*sin(Phi),
signum(s11)*sin(Phi), signum(s22)*cos(Phi);
স্ট্যান্ডার্ড সাইন ফাংশন সহ
double signum(double value)
{
if(value > 0)
return 1;
else if(value < 0)
return -1;
else
return 0;
}
এর ফলাফলগুলি হ'ল ঠিক একই মানগুলিতে Eigen::JacobiSVD
( https://eigen.tuxfamily.org/dox-devel/classEigen_1_1 জ্যাকোবিএসভিডি এইচটিএমএল দেখুন )।
S2 = hypot( a*a + b*b - c*c - d*d, 2*(a*c + b*d))
আমার ব্যক্তিগত প্রয়োজনের জন্য, আমি 2x2 এসভিডি-র সর্বনিম্ন গণনা আলাদা করার চেষ্টা করেছি। আমার ধারণা এটি সম্ভবত সবচেয়ে সহজ এবং দ্রুত সমাধানগুলির মধ্যে একটি। আপনি আমার ব্যক্তিগত ব্লগে বিশদটি পেতে পারেন: http://lucidarme.me/?p=4624 ।
সুবিধা: সহজ, দ্রুত এবং আপনার যদি তিনটি ম্যাট্রিকের প্রয়োজন না হয় তবে আপনি কেবলমাত্র তিনটি ম্যাট্রিকের (এস, ইউ বা ডি) একটি বা দুটি গণনা করতে পারেন।
অপূর্ণতা এটা ATAN2, যা inacurate হতে পারে ব্যবহার করে এবং একটি বহিস্থিত লাইব্রেরী (typ। Math.h) প্রয়োজন হতে পারে।
এখানে 2x2 এসভিডি সমাধানের একটি বাস্তবায়ন দেওয়া হয়েছে। আমি এটিকে ভিক্টর লিউ-র কোডের বাইরে রেখেছি। তার কোড কিছু ম্যাট্রিকের জন্য কাজ করছে না। সমাধানের জন্য আমি এই দুটি নথিকে গাণিতিক রেফারেন্স হিসাবে ব্যবহার করেছি: পিডিএফ 1 এবং পিডিএফ 2 ।
ম্যাট্রিক্স setData
পদ্ধতিটি সারি-প্রধান ক্রমে। অভ্যন্তরীণভাবে, আমি প্রদত্ত 2D অ্যারে হিসাবে ম্যাট্রিক্স ডেটা উপস্থাপন করি data[col][row]
।
void Matrix2f::svd(Matrix2f* w, Vector2f* e, Matrix2f* v) const{
//If it is diagonal, SVD is trivial
if (fabs(data[0][1] - data[1][0]) < EPSILON && fabs(data[0][1]) < EPSILON){
w->setData(data[0][0] < 0 ? -1 : 1, 0, 0, data[1][1] < 0 ? -1 : 1);
e->setData(fabs(data[0][0]), fabs(data[1][1]));
v->loadIdentity();
}
//Otherwise, we need to compute A^T*A
else{
float j = data[0][0]*data[0][0] + data[0][1]*data[0][1],
k = data[1][0]*data[1][0] + data[1][1]*data[1][1],
v_c = data[0][0]*data[1][0] + data[0][1]*data[1][1];
//Check to see if A^T*A is diagonal
if (fabs(v_c) < EPSILON){
float s1 = sqrt(j),
s2 = fabs(j-k) < EPSILON ? s1 : sqrt(k);
e->setData(s1, s2);
v->loadIdentity();
w->setData(
data[0][0]/s1, data[1][0]/s2,
data[0][1]/s1, data[1][1]/s2
);
}
//Otherwise, solve quadratic for eigenvalues
else{
float jmk = j-k,
jpk = j+k,
root = sqrt(jmk*jmk + 4*v_c*v_c),
eig = (jpk+root)/2,
s1 = sqrt(eig),
s2 = fabs(root) < EPSILON ? s1 : sqrt((jpk-root)/2);
e->setData(s1, s2);
//Use eigenvectors of A^T*A as V
float v_s = eig-j,
len = sqrt(v_s*v_s + v_c*v_c);
v_c /= len;
v_s /= len;
v->setData(v_c, -v_s, v_s, v_c);
//Compute w matrix as Av/s
w->setData(
(data[0][0]*v_c + data[1][0]*v_s)/s1,
(data[1][0]*v_c - data[0][0]*v_s)/s2,
(data[0][1]*v_c + data[1][1]*v_s)/s1,
(data[1][1]*v_c - data[0][1]*v_s)/s2
);
}
}
}