먼 곳에서 $v$의 속력으로 달에 접근하는 우주선이 달(속력: $U$)의 중력에 의해서 그림과 같은 궤도를 그리면서 움직인다(궤도는 이심률이 매우 큰 타원으로 간주한다). 다시 우주선이 달에서 충분히 멀어졌을 때 속력은 얼마쯤 될까? 단, 달의 질량은 우주선에 비해 매우 크다고 생각할 수 있다.
동물이 달릴 때 다리와 바닥 사이의 마찰력으로 가속을 하거나 감속할 수 있다. 개가 얼마나 가속/감속을 할 수 있는가는 사냥개로써의 특성에 중요한 요소 중 하나이다. 그럼 어떤 신체적인 요건이 이를 결정하는 알아보기 위해 우선 개를 그림과 같이 간단히 강체로 근사를 하여 가능한 가속도를 구해보자.
개의 가속도가 $a_x$일 때, 뉴턴의 운동 방정식을 세우면
$$ \sum F_x = f_f + f_h = m a_x ,$$
$$ \sum F_y = N_f + N_h -m g = 0$$
그리고 넘어지지 않고 안정적으로 가속하기 위해서는 회전하지(넘어지지) 않아야 한다. 질량중심에 대한 회전운동방정식을 적으면
을 이용하자. 원의 경우는 $A=0$인 경우는 직선을 나타내고, $A\ne0$인 경우가 원을 표현한다. 물론 $A=1$로 설정을 할 수 있으나 이 경우는 주어진 데이터가 원의 일부 부분만 나타내는 경우 문제가 생긴다. 원은 중심과 반지름을 알면 결정되므로 3개의 변수가 필요한데 위의 이차 다항식은 4개의 변수를 가진다. 원을 제대로 표현하기 위해서는 제한 조건이 들어오는데
로 표현된다. 질량중심 좌표계에서 계산을 하면 $M_x= M_y =0$이므로 행렬이 더 간단해진다.
$\epsilon$을 $\tt x^T$에 대해서 미분하면
$$ \tt M.x = \lambda \tt N.x$$
인 lagrange multiplier가 고윳값이 되는 일반화된 고윳값 방정식을 얻는다. 이 고윳값 식이 해를 가지려면
$$\text {det}( \tt M -\lambda \tt N)=0$$
이어야 한다. 이 특성 방정식은 4차 다항식이므로 closed form 형태의 해를 쓸 수 있으나 실질적으로 수치해석적으로 구하는 것이 더 편하다(Newton-Raphson). $\text {min}(\epsilon)$은 제한조건 때문에
$$ n^{-1}\epsilon = \lambda \tt x^T. N. x = \lambda > 0$$
이므로 $0$보다 큰 최소 고윳값이 원하는 해이다. ($\lambda = 0$은 $\text {det}(\tt M)=0$, 즉, $\tt M$이 singular 한 경우)
그런데 주어진 고윳값 $\lambda$ 해당하는 고유 벡터가 $\tt x$일 때, 임의의 0이 아닌 상수 $\mu$에 대해 $\mu \tt x$도 역시 고유벡터이다. 고유 벡터의 크기를 고정하기 위해서 제한조건을 적용하면 $1=\mu^2 \tt x^T .N .x = \mu^2 \tt x^T. M. x/\lambda$이므로 $\mu = \sqrt{\lambda/ \tt x^T. M. x}$로 선택하면 된다. 그렇지만 해가 원을 나타내는 경우 원의 중심 $(-B/2A, -C/2A)$, 반지름 $\sqrt{ (B/2A)^2 + (C/2A)^2 - D/A}$은 계수의 비에만 의존하므로 굳이 normalized 된 고유벡터를 구할 필요가 없다. 따라서, 해 중에 직선인 경우는 무시하고($A=0$) 원만을 선택할 때는 $A=1$로 놓고 계산을 해도 영향을 받지 않는다. 또한 이 경우 고유벡터 성분을 구체적으로 적을 수 있어 수치해석에 의존할 필요도 없어진다.
** 추가: Sylvester's law of inertia에 의하면 위에서 구하는 고유값 $\lambda$의 부호 개수는 $\tt N$의 고유값 부호별 개수와 같아야 하는데, $\tt N$의 고유값이 $\{-2,1,1,2\}$이므로 양수인 고유값 $\lambda$는 3개가 존재한다.
Ref: V. Pratt, Direct least-squares fitting of algebraic surfaces, Computer Graphics 21, 1987, 145–152.
double circleFit(std::vector<CPoint>& data, double& centerx, double& centery, double& radius) {
const int maxIter = 99;
double mx = 0, my = 0;
for (int i = data.size(); i-- > 0;)
mx += data[i].x, my += data[i].y;
// center of mass;
mx /= data.size(); my /= data.size();
// moment calculation;
double Mxy, Mxx, Myy, Mzx, Mzy, Mzz;
Mxx = Myy = Mxy = Mzx = Mzy = Mzz = 0.;
for (int i = data.size(); i-- > 0;) {
double xi = data[i].x - mx; // center of mass coordinate
double yi = data[i].y - my; // center of mass coordinate
double zi = xi * xi + yi * yi;
Mxy += xi * yi; Mxx += xi * xi;
Myy += yi * yi; Mzx += xi * zi;
Mzy += yi * zi; Mzz += zi * zi;
}
Mxx /= data.size(); Myy /= data.size();
Mxy /= data.size(); Mzx /= data.size();
Mzy /= data.size(); Mzz /= data.size();
double Mz = Mxx + Myy;
double Cxy = Mxx * Myy - Mxy * Mxy;
double Vz = Mzz - Mz * Mz;
// coefficients of characteristic polynomial;
double C2 = 4 * Cxy - 3 * Mz * Mz - Mzz;
double C1 = Vz * Mz + 4 * Cxy * Mz - Mzx * Mzx - Mzy * Mzy;
double C0 = Mzx * (Mzx * Myy - Mzy * Mxy) + Mzy * (Mzy * Mxx - Mzx * Mxy) - Vz * Cxy;
// Newton's method starting at lambda = 0
double lambda = 0;
double y = C0; // det(lambda = 0)
for (int iter = 0; iter < maxIter; iter++) {
double Dy = C1 + lambda * (2. * C2 + 16.*lambda * lambda);
double lambdaNew = lambda - y / Dy;
if ((lambdaNew == lambda)) break;
double ynew = C0 + lambdaNew * (C1 + lambdaNew * (C2 + 4 * lambdaNew * lambdaNew));
if (fabs(ynew) >= fabs(y)) break;
lambda = lambdaNew;
y = ynew;
}
double DEL = lambda * lambda - lambda * Mz + Cxy;
double cx = (Mzx * (Myy - lambda) - Mzy * Mxy) / DEL / 2;
double cy = (Mzy * (Mxx - lambda) - Mzx * Mxy) / DEL / 2;
centerx = cx + mx; // recover origianl coordinate;
centery = cy + my;
radius = sqrt(cx * cx + cy * cy + Mz + 2 * lambda);
return fitError(data, centerx, centery, radius);
}