一元三次方程求解——新的尝试:可变参数

/*
* 编写人    黄铎彦
* 编写日期  2023/1/11
* 程序功能  求解福州大学程序设计练习与竞赛系统 lq1086 题,一元三次方程求解
*           蓝桥杯原题链接 http://lx.lanqiao.cn/problem.page?gpid=T85
* 修改日期  2023/1/16
* 修改原因  函数 dichotomy 中有多处相同的函数调用表达式,
            将它们用变量存起来,减少调用次数
*/
/*
* 测试输入
-2 7 -5 -1

* 测试输出
-0.16 1.32 2.34
*/
#define     _CRT_SECURE_NO_WARNINGS
#define     MIN     -100    // 根最小的可能值
#define     MAX     100     // 根最大的可能值
#define     EPS     0.01    // 根的精度
#include    <stdio.h>
#include    <math.h>
#include    <stdarg.h>

int     solve_quadratic(const double a, const double b, const double c,
    double* const x1, double* const x2);
int     solve_cubic(const double a, const double b, const double c, 
    const double d, double* const x1, double* const x2, double* const x3);
double  cubic_func(const double x, ...);
int     dichotomy(double left,
    double right, double(*func)(double), double* const solution);
int     cubic_have3real(const double a, 
    const double b, const double c, const double d);

int main(void)
{
    double a; // 三次项系数
    double b; // 二次项系数
    double c; // 一次项系数
    double d; // 常数项
    scanf("%lf%lf%lf%lf", &a, &b, &c, &d);
    double x1, x2, x3;
    int err = solve_cubic(a, b, c, d, &x1, &x2, &x3);
    if (err)    puts("求解过程中出现了错误。");
    else        printf("%.2f %.2f %.2f", x1, x2, x3);
    return 0;
}

/// <summary>
/// 求一元二次方程的根
/// <para>依赖:math.h、stdio.h</para>
/// <para>保证第一个根不大于第二个根</para>
/// </summary>
/// <param name="a">二次项系数</param>
/// <param name="b">一次项系数</param>
/// <param name="c">常数项</param>
/// <param name="x1">参数输出:第一个根</param>
/// <param name="x2">参数输出:第二个根</param>
/// <returns>如果有实根,返回 0;否则返回 1。</returns>
int solve_quadratic(const double a, const double b, const double c,
    double* const x1, double* const x2)
{
    double delta = fma(b, b, -4 * a * c);
    if (delta < 0)
    {
        puts("判别式为负,没有实根!");
        return 1;
    }

    double mid = b / a / -2;
    if (delta == 0) *x1 = *x2 = mid;
    else
    {
        double offset = fabs(sqrt(delta) / a / 2); // 必须加绝对值!
        *x1 = mid - offset;
        *x2 = mid + offset;
    }
    return 0;
}

/// <summary>
/// 求一元三次方程的根。注意:参数必须保证有三个不同的实根。
/// </summary>
/// <para>依赖:头文件 stdio.h;函数 solve_quadratic、
/// cubic_func、cubic_have3real 和 dichotomy;宏 MAX 和 MIN</para>
/// <para>保证第一个根小于第二个根,且第二个根小于第三个根</para>
/// <param name="a">三次项系数</param>
/// <param name="b">二次项系数</param>
/// <param name="c">一次项系数</param>
/// <param name="d">常数项</param>
/// <param name="x1">参数输出:第一个根</param>
/// <param name="x2">参数输出:第二个根</param>
/// <param name="x3">参数输出:第三个根</param>
/// <returns>
/// 如果方程没有三个相异的实根,或者实根超出了最小或最大的可能值,
/// 返回 1;否则返回 0。
/// </returns>
int solve_cubic(const double a, const double b, const double c, 
    const double d, double* const x1, double* const x2, double* const x3)
{
    /* 计算判别式来检查是否有三个相异的实根 */
    if (!cubic_have3real(a, b, c, d))
    {
        puts("根据判别式,不存在三个相异的实根!");
        return 1;
    }

    /* 根据一阶导数求出极值点 */
    double m, n; // 极值点,其中 m < n
    int err = solve_quadratic(3 * a, 2 * b, c, &m, &n);
    if (err)
    {
        puts("求极值点时出错!");
        return 1;
    }
    if (m < MIN || n > MAX)
    {
        puts("极值超出了根最小或最大的可能值!");
        return 1;
    }

    /* 根据二分法,在左端点、两个极值点和右端点形成的三个区间中求根 */
    cubic_func(0, a, b, c, d); // 初始化三次函数
    err =   dichotomy(MIN, m, cubic_func, x1)
        ||  dichotomy(m, n, cubic_func, x2)
        ||  dichotomy(n, MAX, cubic_func, x3);
    if (err)
    {
        puts("二分法求根失败!");
        return 1;
    }
    return 0;
}

/// <summary>
/// 求三次函数的函数值
/// <para>本函数的用法是:</para>
/// <para>首次使用本函数,需要对自变量的三次项、二次项和一次项系数,
/// 以及常数项进行初始化,方法是,将它们按照顺序,作为第 2 个至第 5 个参数,
/// 传入函数内。这样,以后调用本函数,只需要一个参数作自变量就可以了。</para>
/// <para>注意:初始化时传递的额外参数必须是 double 型!</para>
/// <para>依赖:stdarg.h 和 math.h</para>
/// </summary>
/// <param name="x">自变量</param>
/// <param name="...">
/// 初始化参数(double 型):三次项、二次项和一次项系数,以及常数项
/// </param>
/// <returns>关于自变量的三次函数值</returns>
double cubic_func(const double x, ...)
{
    static int first_call = 1;
    static double a; // 三次项系数
    static double b; // 二次项系数
    static double c; // 一次项系数
    static double d; // 常数项
    if (first_call)
    {
        first_call = 0;
        va_list ap;
        va_start(ap, x);
        a = va_arg(ap, double);
        b = va_arg(ap, double);
        c = va_arg(ap, double);
        d = va_arg(ap, double);
        va_end(ap);
    }
    return fma(x, fma(x, fma(x, a, b), c), d);
}

/// <summary>
/// 利用二分法求零点
/// <para>依赖:头文件 stdio.h 和宏 EPS</para>
/// </summary>
/// <param name="left">左端点</param>
/// <param name="right">右端点</param>
/// <param name="func">函数</param>
/// <param name="solution">参数输出:解</param>
/// <returns>
/// 如果区间两端点的函数值同号,或者左端点比右端点大,
/// 则无法求解,返回 1;否则返回 0。
/// </returns>
int dichotomy(double left, 
    double right, double(*func)(double), double* const solution)
{
    double f_left = func(left), f_right = func(right);
    if (left >= right || f_left * f_right > 0)
    {
        puts("端点非法,无法求零点!");
        return 1;
    }

    if      (f_left     == 0)   *solution = left;
    else if (f_right    == 0)   *solution = right;
    else
    {
        double mid = (left + right) / 2, f_mid = func(mid);
        for (; right - left >= EPS && f_mid != 0; 
            mid = (left + right) / 2, f_mid = func(mid))
        {
            if      (f_left * f_mid     < 0)    right   = mid;
            else if (f_mid  * f_right   < 0)    left    = mid;
        }
        *solution = mid;
    }
    return 0;
}

/// <summary>
/// 通过判别式,判断由函数参数确定的一元三次方程是否有三个不同的实根
/// <para>依赖:math.h</para>
/// </summary>
/// <param name="a">三次项系数</param>
/// <param name="b">二次项系数</param>
/// <param name="c">一次项系数</param>
/// <param name="d">常数项</param>
/// <returns>如果有三个不同的实根,返回 1;否则返回 0。</returns>
int cubic_have3real(const double a, 
    const double b, const double c, const double d)
{
    double p = c / a - pow(b, 2) / pow(a, 2) / 3;
    double q = d / a - b * c / pow(a, 2) / 3 + 2 * pow(b, 3) / pow(a, 3) / 27;
    return pow(q, 2) / 4 + pow(p, 3) / 27 < 0;
}

基于SSM框架的智能家政保洁预约系统,是一个旨在提高家政保洁服务预约效率和管理水平的平台。该系统通过集成现代信息技术,为家政公司、家政服务人员和消费者提供了一个便捷的在线预约和管理系统。 系统的主要功能包括: 1. **用户管理**:允许消费者注册、登录,并管理他们的个人资料和预约历史。 2. **家政人员管理**:家政服务人员可以注册并更自己的个人信息、服务类别和服务时间。 3. **服务预约**:消费者可以浏览不同的家政服务选项,选择合适的服务人员,并在线预约服务。 4. **订单管理**:系统支持订单的创建、跟踪和管理,包括订单的确认、完成和评价。 5. **评价系统**:消费者可以在家政服务完成后对服务进行评价,帮助提高服务质量和透明度。 6. **后台管理**:管理员可以管理用户、家政人员信息、服务类别、预约订单以及处理用户反馈。 系统采用Java语言开发,使用MySQL数据库进行数据存储,通过B/S架构实现用户与服务的在线交互。系统设计考虑了不同用户角色的需求,包括管理员、家政服务人员和普通用户,每个角色都有相应的权限和功能。此外,系统还采用了软件组件化、精化体系结构、分离逻辑和数据等方法,以便于未来的系统升级和维护。 智能家政保洁预约系统通过提供一个集中的平台,不仅方便了消费者的预约和管理,也为家政服务人员提供了一个展示和推广自己服务的机会。同时,系统的后台管理功能为家政公司提供了强大的数据支持和决策辅助,有助于提高服务质量和管理效率。该系统的设计与实现,标志着家政保洁服务向现代化和网络化的转型,为管理决策和控制提供保障,是行业发展中的重要里程碑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黄铎彦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值