C语言利用函数指针进行非线性方程求根

该博客介绍了两种非线性方程的求解方法:黄金分割法和牛顿迭代法。黄金分割法通过在给定区间内寻找黄金分割点来逼近方程的根,而牛顿迭代法则利用函数的切线进行迭代求解。文章还展示了如何定义和使用函数指针以实现这两个算法,并提供了具体的C++代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

下面给出了两种方法求解非线性方程零点的程序,即求解:
f(x)=0 f(x)=0f(x)=0形式的方程的根x∗x^*x

黄金分割法

计算原理为在区间范围[a,b][a,b][a,b]内试探,每次并非去中点而是取黄金分割点。也是一种区间优化方法,只需要提供真实解x∗x^*x区间范围。
迭代公式为mk=ak+0.618(bk−ak)m_{k}=a_{k}+0.618\left(b_{k}-a_{k}\right)mk=ak+0.618(bkak)f(ak)f(a_k)f(ak)f(mk)f(m_{k})f(mk)同号,则ak+1=(赋值)mka_{k+1}={(赋值)}m_kak+1=()mk;否则bk+1=(赋值)mkb_{k+1}={(赋值)}m_kbk+1=()mk. 直至bk−ak<ϵb_{k}-a_k<\epsilonbkak<ϵ.

牛顿迭代法

计算原理为切线近似非线性函数,然后求根。是一种局部优化和求根方法,需要接近真实解x∗x^*x的初始猜测。
迭代公式为xk+1=xk−f(xk)f′(xk)x_{k+1} = x_k - \frac{f(x_k)}{f'(x_k)}xk+1=xkf(xk)f(xk)

需要提供函数的导数。终止条件为达到迭代上限k>kmaxk>k_{max}k>kmax或精度足够高:xk+1−xk<ϵx_{k+1}-x_k<\epsilonxk+1xk<ϵ

函数指针

函数指针用于指向一个函数,函数名是函数体的入口地址。下面是几个例子:

// 最简单的函数及其对应的函数指针:
void f();
void (*f_ptr)();
// 复杂点的函数和他的函数指针
int f(double b, int i);
int (*f_ptr)(double b, int i);

为了避免指针的使用,我的习惯是用typedef重新给它改个名字:
typedef int(*FUNC_P)(int, int)。以下都是FUNC_P的实例:

int func(int a, int b) {
    cout << "1997年写的func" << endl;
    return 0;
}
int func2(int a, int b) {
    cout << "1999年写的func2" << endl;
}
//2018年想添加一个新的子业务
int func2021(int a, int b) {
    cout << "2021年写的func2021" << endl;
}

显然,由于上面几个函数的输出、形参表、参数类型都完全一样,所以可以用同一个函数指针类型。使用时用以下声明和定义

    FUNC_P f1 = NULL, f2 = NULL;// 声明
    f1 = func;		// 赋值
    f2 = func2021;
    f1(10, 20);    // 等价 func(10,20);

此种定义类似MATLAB中的函数句柄@fun(handle),在多个源文件中调用时非常方便。

源文件

// functionHandleTest.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <cmath>

typedef double(*FUNC_P)(double);//定义函数句柄类型

double f1(double x) {
    return x * x - 2.0;// 求解根号2
}
double f1p(double x) {
    return 2.0* x ;
}

double f2(double x) {
    return cos(x)-x;// 求解cos(x)-x==0
}
double f2p(double x) {
    return -sin(x)-1;
}

static int iterCount = 0;

double goldenSearch(FUNC_P fun,double left, double right, double precision) {
    double mid = 0.618*right+0.382*left;
    iterCount++;
    printf("%d\t%1.16f\n", iterCount, mid);
    double result = fun(mid);
    if (right - left < precision) {
        return mid;
    }
    else if (result * fun(left) < 0) {
        return goldenSearch(fun , left, mid, precision);
    }
    else if (result * fun(right) < 0) {
        return goldenSearch(fun , mid, right, precision);
    }
    else {
        return goldenSearch(fun , mid, right, precision);
    }
    return 0;
}

double newtonIter(FUNC_P func, FUNC_P diffF,double x0, double MAX_ITER) {
    double preX;
    double x;

    preX = x0;
    x = preX - func(preX) / diffF(preX);
    double precision = 1.e-10;
    while (iterCount < MAX_ITER &&abs(preX-x)>precision) {
        preX = x;
        x = preX - func(preX) / diffF(preX);
        iterCount++;
        printf("%d\t%1.16f\n", iterCount,x);
    }
    return x;
}

int main()
{  
    FUNC_P f = NULL,fp=NULL;
    f = f2; fp = f2p;
    //f3 = fun2;
    std::cout << "Iteration \t Function Value" << std::endl;
    std::cout << goldenSearch(f,0,2,1.0e-10);

    iterCount = 0;
    std::cout << std::endl;

    std::cout << "Newton \t Function Value" << std::endl;
    std::cout << newtonIter(f,fp, 1.5,20);
}

// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单


求解结果如图
在这里插入图片描述

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值