c++:求解二元二次方程组(解析解)

这个问题从产生到解决,困扰了LZ快一周(当然因为中间也有其他事情要做),本来从LZ的角度是想找第三方库使用,不要说matlab就可以啊o(╥﹏╥)o,确实matlab一个solve函数就解决问题了,但是问题是不能用matlab,所以第一方案排除。

用第三方库?google了半天,发现一个gsl库确实可以求解二元二次方程组,但是也有问题,1)只能求解出一个解,而且无法求解复数解(源代码定义中没有复数定义,除非自己扒源程序,自己写);2)使用的是优化算法,例如牛顿迭代等,所求出的是近似解,非解析解,也就是说求得的结果并不精确。而且初始值的设定会影响迭代结果,容易进入局部最优。3)复数解无法收敛。

PS:花钱的库还是算了,毕竟也不是特别难解决的问题。。。

只剩一个方案:自己动手,丰衣足食O(∩_∩)O哈哈~

列出基本的问题:
a1x2+b1xy+c1y2+d1x+e1y+f1=0

a2x2+b2xy+c2y2+d2x+e2y+f2=0

求上述方程的解(包含复数),也就是两个二次曲线的交点。

step 1: 利用两式相减,用x来表示y
得到类似如下的方程:

(Bx+D)y=Ax2CxE

其中的A,B,C,D,E都是用 a1f1,a2f2 所表示的。

step 2:带入原方程得到关于 x 的一元四次方程

M4x4+M3x3+M2x2+M1x+M0=0

将四次项归一化

x4+bx3+cx2+dx+e=0

step 3: 利用费拉里法求一元四次方程

具体对费拉里法求解一元四次方程的推导:https://baike.baidu.com/item/%E4%B8%80%E5%85%83%E5%9B%9B%E6%AC%A1%E6%96%B9%E7%A8%8B

这里就不在解释了,因为公式很多,手打太浪费时间了。

讲了这么多,贴出源代码:(如果有小伙伴有更好的方法可以和LZ私聊哦)

#include 
#include 
#include 
#include 
// #include 
#include 
#include 


void quadratic2quartic(Eigen::Matrix6, 1> & _par1, 
                     Eigen::Matrix6, 1> & _par2,
                     Eigen::Matrix5, 1> & _y2x_par,
                     Eigen::Matrix, 5, 1> &_quartic_par)
{
  double A, B, C, D, E;
  double a1, b1, c1, d1, e1, f1, a2, b2, c2, d2, e2, f2;

  a1 = _par1[0];
  b1 = _par1[1];
  c1 = _par1[2];
  d1 = _par1[3];
  e1 = _par1[4];
  f1 = _par1[5];

//   std::cout  << a1 << "   " << b1 <<  "   " <<  c1  << "   " << d1 <<  "   " << e1 <<  "   " << f1 << std::endl;

  a2 = _par2[0];
  b2 = _par2[1];
  c2 = _par2[2];
  d2 = _par2[3];
  e2 = _par2[4];
  f2 = _par2[5];

//   std::cout  << a2 << "   " << b2 <<  "   " <<  c2  << "   " << d2 <<  "   " << e2 <<  "   " << f2 << std::endl;


  A = a1*c2 - c1*a2;
  B = b1*c2 - c1*b2;
  C = d1*c2 - c1*d2;
  D = e1*c2 - c1*e2;
  E = f1*c2 - c1*f2;
  _y2x_par << A, B, C, D, E;
//   std::cout << "A-E: \n";
//   std::cout << A << "   " << B << "   " << C <<"   " << D << "   " << E << std::endl; 

  double M0, M1, M2, M3, M4;
  M4 = a1*B*B - b1*A*B + c1*A*A;
  M3 = 2*a1*B*D - b1*A*D - b1*B*C + 2*c1*A*C + d1*B*B - e1*A*B;
  M2 = a1*D*D - b1*C*D - b1*B*E + c1*C*C + 2*c1*A*E + 2*d1*B*D - e1*A*D - e1*B*C + f1*B*B;
  M1 = -b1*D*E + 2*c1*C*E + d1*D*D - e1*C*D - e1*B*E + 2*f1*B*D;
  M0 = c1*E*E - e1*D*E + f1*D*D;
//   std::cout << "m4-m0: \n";
//   std::cout << M4 << "   " << M3 << "   " << M2 << "   " << M1 << "   " << M0 << std::endl;
//   
  double b, c, d, e;
  b = M3 / M4;
  c = M2 / M4;
  d = M1 / M4;
  e = M0 / M4;
//   std::cout  << "b, c, d, e: \n";
//   std::cout  << " " << b << "   " << c << "   " << d<< "   " << e << "   \n"; 
  _quartic_par << 1.0, b, c, d, e;

}

//extract a root
void sqrtn(const std::complex &_input, double n,
          std::complex & _out)
{
//   double r = sqrt(_input.real()*_input.real() + _input.imag()*_input.imag());
//   std::cout << _input << std::endl;
  double r = hypot(_input.real(), _input.imag());

//   std::cout << "the norm is " << r << std::endl;
  if(r > 0.0)
  {
    double a = atan2(_input.imag(), _input.real());
//     std::cout << "the a is: " << a << std::endl;

//     double a = arg(_input);
    n = 1 / n;
    r = pow(r, n);
    a *= n;
    _out.real() = r * cos(a) ;
    _out.imag() = r * sin(a);
  }
  else
  {
    _out.real() = 0.0;
    _out.imag() = 0.0;
  }
//   std::cout << "out :" << _out << std::endl;
}

void Ferrari(Eigen::Matrix, 5, 1> & _quartic_par, 
                 Eigen::Matrix, 4, 1> & _x)
{
  std::complex a = _quartic_par[0];
  std::complex b = _quartic_par[1];
  std::complex c = _quartic_par[2];
  std::complex d = _quartic_par[3];
  std::complex e = _quartic_par[4];

  std::complex P = (c*c + 12.0*e - 3.0*b*d) / 9.0;
  std::complex Q = (27.0*d*d + 2.0*c*c*c + 27.0*b*b*e - 72.0*c*e - 9.0*b*c*d) / 54.0;
//   std::cout << "P: " << P << "   \nQ: " << Q << std::endl;
  std::complex D, u, v; 
//   D = cabs(sqrt(Q*Q - P*P*P));

  sqrtn(Q*Q - P*P*P, 2.0, D);
//   std::cout << "D: " << D << std::endl;
  u = Q + D;
  v = Q - D;
//   std::cout << "u: " << u << "  \nv: " << v << std::endl;
  if(v.real()*v.real() + v.imag()*v.imag() > u.real()*u.real() + u.imag()*u.imag())
  {
    sqrtn(v, 3.0, u);
  }
  else
  {
    sqrtn(u, 3.0, u);
  }
//   std::cout <<"u: " << u << std::endl;
  std::complex y;
  if(u.real()*u.real() + u.imag()*u.imag() > 0.0)
  {
    v = P / u;
    std::complex o1(-0.5,+0.86602540378443864676372317075294);
    std::complex o2(-0.5,-0.86602540378443864676372317075294);
    std::complex &yMax = _x[0];
    double m2 = 0.0;
    double m2Max = 0.0;
    int iMax = -1;

    for(int i = 0; i < 3; ++i)
    {
      y = u + v + c / 3.0;
      u *= o1;
      v *= o2;
      a = b*b + 4.0*(y-c);
      m2 = a.real()*a.real() + a.imag()*a.imag();

      if(0==i || m2Max < m2)
      {
    m2Max = m2;
    yMax = y;
    iMax = i;
      }
    }
    y = yMax;
  }
  else
  {//cubic equation
    y = c / 3.0;  
  }
  std::complex m;
  sqrtn(b*b + 4.0*(y-c), 2.0, m);
  if(m.real()*m.real() + m.imag()*m.imag() >= DBL_MIN)
  {
    std::complex n = (b*y - 2.0*d) / m;
    sqrtn((b+m)*(b+m) - 8.0*(y+n), 2.0, a);
    _x[0] = (-(b+m) + a) / 4.0;
//     std::cout << "_x[0]: " << _x[0] << std::endl;
    _x[2] = (-(b+m) - a) / 4.0;
//     std::cout << "_x[1]: " << _x[1] << std::endl;
    sqrtn((b-m)*(b-m) - 8.0*(y-n), 2.0, a);
    _x[1] = (-(b-m) + a) / 4.0;
    _x[3] = (-(b-m) -a) / 4.0;
//     std::cout << "_x[2]: " << _x[2] << std::endl;
//     std::cout << "_x[3]: " << _x[3] << std::endl;
  }
  else
  {
     sqrtn(b*b - 8.0*y, 2.0, a);
     _x[0] = _x[1] = (-b + a) / 4.0;
     _x[2] = _x[3] = (-b - a) / 4.0;
  }

//   for(int i = 0; i <4; i++)
//   {
//     std::cout << _x[i] << std::endl;
//   }
}

void compute_y(Eigen::Matrix, 4, 1> &_x, 
           Eigen::Matrix5, 1> &_y2x_par, 
           Eigen::Matrix, 4, 1> &_y)
{
  for(int i = 0; i < 4; i++)
  {
    _y[i] = (-_y2x_par[0]*_x[i]*_x[i] - _y2x_par[2]*_x[i] - _y2x_par[4]) / (_y2x_par[1]*_x[i] + _y2x_par[3]);
//     std::cout << "_y[" << i << "]" << _y[i] << std::endl;
  }
}

int main(int argc, char **argv) {
  Eigen::Matrix6, 1> ic1, ic2;
  ic1 <<414181, -12635.8, 398758, -9.53484e+08, -4.16931e+08, 5.00382e+11;
  ic2 << 923938, -41764.7, 932015, -2.10858e+09, -9.79279e+08, 6.2537e+11;
  Eigen::Matrix, 5, 1> quartic_par;
  Eigen::Matrix5, 1> y2x_par;
//   quadratic2quartic(ic1, ic2, quartic_par);
  quadratic2quartic(ic1, ic2, y2x_par, quartic_par);
  Eigen::Matrix, 4, 1> x, y;
  Ferrari(quartic_par, x);
  compute_y(x, y2x_par, y);
  std::cout <<"the result :" << std::endl;
  for(int i = 0; i < 4; i++)
  {
    std::cout << "x: " << x[i] <<  "\ty: " << y[i] << std::endl;
  }

//     std::cout << "Hello, world!" << std::endl;

    return 0;
}

使用了Eigen处理矩阵会比较方便。

贴出最后运行结果:
c++:求解二元二次方程组(解析解)_第1张图片

使用matlab对上述算法进行验证:

c++:求解二元二次方程组(解析解)_第2张图片

最后运行结果如下所示:

c++:求解二元二次方程组(解析解)_第3张图片

好,最后检验完毕。最后感慨一下还是matlab功能强大,如果要使用c++还是要对具体算法原理了解,才能编写代码,对编程能力、算法理解要求更高,所以小伙伴们自行决定需要什么工具哦O(∩_∩)O哈哈~解决一个问题好开心(。◕ˇ∀ˇ◕)

你可能感兴趣的:(c++)