最近项目中需要利用C/C++
求解一个非线性方程组,经过调研,发现GSL
库可以满足该要求。
先在win10
下编译安装一下这个库:VS2019下编译与配置GSL2.7【Release x64版】
参考GSL2.7
官方求多维根的教程:Multidimensional Root-Finding
也可以直接看我翻译的文章:通过GSL2.7解非线性方程组的多维根
本人将求解多维根的程序写成一个类GSLSLover
,方便外部程序调用其方法GSLSLover::iterationSlover
待求解的非线性方程组如下:
{ x x 3 c o s α s i n β − x y 3 s i n α + x z 3 c o s α c o s β = z x 3 c o s β − z z 3 s i n β y x 3 c o s α s i n β − y y 3 s i n α + y z 3 c o s α c o s β = z x 3 s i n α s i n β + z y 3 c o s α + z z 3 s i n α c o s β \begin{cases} \begin{aligned} xx_3cos{\alpha}sin\beta-xy_3sin\alpha+xz_3cos{\alpha}cos\beta &= zx_3cos{\beta}-zz_3sin\beta\\ yx_3cos{\alpha}sin\beta-yy_3sin\alpha+yz_3cos{\alpha}cos\beta &= zx_3sin{\alpha}sin\beta+zy_3cos\alpha+zz_3sin{\alpha}cos\beta \end{aligned} \end{cases} { xx3cosαsinβ−xy3sinα+xz3cosαcosβyx3cosαsinβ−yy3sinα+yz3cosαcosβ=zx3cosβ−zz3sinβ=zx3sinαsinβ+zy3cosα+zz3sinαcosβ
其中,坐标 ( x , y , z ) (x,y,z) (x,y,z)和 ( x 3 , y 3 , z 3 ) (x_3,y_3,z_3) (x3,y3,z3)是待传入参数,但是其值是确定的。该二元非线性方程组的待解量是: α \alpha α和 β \beta β
gsl.h
#include <stdlib.h>
#include <stdio.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_multiroots.h>
struct sparams
{
double x_1;
double y_1;
double z_1;
double x_3;
double y_3;
double z_3;
};
class GSLSlover {
public:
const gsl_multiroot_fsolver_type* T;
gsl_multiroot_fsolver* s;
int status;
size_t i, iter = 0;
const size_t n = 2;
struct sparams p; // 待定
double x_init[2] = {
0, 0 };
gsl_vector* x;
gsl_multiroot_function f;
GSLSlover();
~GSLSlover();
//static int camera_f(const gsl_vector* x, void* params, gsl_vector* f);
void print_state(size_t iter, gsl_multiroot_fsolver* s);
void iterationSlover(double x1, double y1, double z1, double x3, double y3, double z3);
};
gsl.cpp
#include "gsl.h"
int camera_f(const gsl_vector* x, void* params, gsl_vector* f)
{
double a = ((struct sparams*)params)->x_1; // 第一帧的目标点坐标
double b = ((struct sparams*)params)->y_1;
double c = ((struct sparams*)params)->z_1;
double x_3 = ((struct sparams*)params)->x_3; // 传进去的坐标
double y_3 = ((struct sparams*)params)->y_3;
double z_3 = ((struct sparams*)params)->z_3;
const double alpha = gsl_vector_get(x, 0);
const double beta = gsl_vector_get(x, 1);
const double y0 = a * x_3 * cos(alpha) * sin(beta) - a * y_3 * sin(alpha) + a * z_3 *
cos(alpha) * cos(beta) - c * x_3 * cos(beta) + c * z_3 * sin(beta);
const double y1 = b * x_3 * cos(alpha) * sin(beta) - b * y_3 * sin(alpha) + b * z_3 *
cos(alpha) * cos(beta) - c * x_3 * sin(alpha) * sin(beta) - c * y_3 * cos(alpha) - c * z_3 *
sin(alpha) * cos(beta);
gsl_vector_set(f, 0, y0);
gsl_vector_set(f, 1, y1);
return GSL_SUCCESS;
}
GSLSlover::GSLSlover()
{
x = gsl_vector_alloc(n);
gsl_vector_set(x, 0, x_init[0]);
gsl_vector_set(x, 1, x_init[1]);
T = gsl_multiroot_fsolver_hybrids;
s = gsl_multiroot_fsolver_alloc(T, 2);
}
GSLSlover::~GSLSlover()
{
gsl_multiroot_fsolver_free(s);
gsl_vector_free(x);
}
void GSLSlover::print_state(size_t iter, gsl_multiroot_fsolver* s)
{
printf("iter = %3u x = % .3f % .3f "
"f(x) = % .3e % .3e\n",
iter,
gsl_vector_get(s->x, 0),
gsl_vector_get(s->x, 1),
gsl_vector_get(s->f, 0),
gsl_vector_get(s->f, 1));
}
void GSLSlover::iterationSlover(double x1, double y1, double z1, double x3, double y3, double z3)
{
p.x_1 = x1;
p.y_1 = y1;
p.z_1 = z1;
p.x_3 = x3;
p.y_3 = y3;
p.z_3 = z3;
f = {
&camera_f, n, &p };
gsl_multiroot_fsolver_set(s, &f, x);
iter = 0; // 多次调用该方法时一定需要清零!
do
{
iter++;
status = gsl_multiroot_fsolver_iterate(s);
//print_state(iter, s);
if (status) /* check if solver is stuck */
break;
status =
gsl_multiroot_test_residual(s->f, 1e-7);
} while (status == GSL_CONTINUE && iter < 50);
printf("status = %s\n", gsl_strerror(status));
}
在主程序main.cpp
中测试代码如下
#include <stdlib.h>
#include <stdio.h>
#include "gsl.h"
#include <iostream>
using namespace std;
int main(void)
{
GSLSlover gs;
double x3 = 9.0;
double y3 = 6.0;
double z3 = 8.0;
double x = 5.0;
double y = 5.0;
double z = 5.0;
gs.iterationSlover(x, y, z, x3, y3, z3);
cout << "alpha = " << gsl_vector_get(gs.s->x, 0) << endl;
cout << "beta = " << gsl_vector_get(gs.s->x, 1) << endl;
return 0;
}
输出结果
status = success
alpha = 0.207568
beta = 0.143063
编译生成时可能会遇到错误如下
错误 LNK2001 无法解析的外部符号 gsl_multiroot_fsolver_hybrids
不用担心,去https://github.com/ampl/gsl找到缺失的.c
文件并添加到工程下,即可运行程序。具体细节可参考文末给出的第二篇文章。
参考文献