C语言编程练习day8

1、二分法求函数根的原理为:如果连续函数f(x)在区间[a,b]的两个端点取值异号,即f(a)f(b)<0,则它在这个区间内至少存在1个根r,即f®=0。

二分法的步骤为:
检查区间长度,如果小于给定阈值,则停止,输出区间中点(a+b)/2;否则
如果f(a)f(b)<0,则计算中点的值f((a+b)/2);
如果f((a+b)/2)正好为0,则(a+b)/2就是要求的根;否则
如果f((a+b)/2)与f(a)同号,则说明根在区间[(a+b)/2,b],令a=(a+b)/2,重复循环;
如果f((a+b)/2)与f(b)同号,则说明根在区间[a,(a+b)/2],令b=(a+b)/2,重复循环。

本题目要求编写程序,计算给定3阶多项式在这里插入图片描述在给定区间[a,b]内的根。

输入:输入在第1行中顺序给出多项式的4个系数a3,a2,a1,a0 ,在第2行中顺序给出区间端点a和b。题目保证多项式在给定区间内存在唯一单根。
输入样例:3 -1 -3 1
-0.5 0.5
输出:在一行中输出该多项式在该区间内的根,精确到小数点后2位。
输出样例:0.33
优化目标:本题用递归,还不是最简单的方法,具体见优化代码。

解题过程:我在读题的时候,看到了解题步骤中的最后两步“重复循环”,脑子里立马想到了递归,用递归也写出来了。在PTA里面提交的时候,结果显示部分正确,错误的一部分是没有考虑到阈值,也就是当区间长度小于一个阈值时(我的代码中,阈值是0.000001),函数值已经非常接近0了,此时只需取区间的中点即可。但当时,我没有想到这个问题,想了半天也没有解决,所以最后去网上看了看别人是怎么写的,看了过后,才发现,通过递归还不是本题最简单的方法。
我的代码

#include <stdio.h>

#define ACCURACY 0.000001

float a3, a2, a1, a0;
float result_start, result_end, result_middle;
/*
*函数的计算
*@start: 区间起点
*@end:区间终点
*@middle:区间中点
*/
void calculat(float start, float end, float middle){
    result_start = a3*start*start*start+a2*start*start+a1*start+a0;
    result_end = a3*end*end*end+a2*end*end+a1*end+a0;
    result_middle = a3*middle*middle*middle+a2*middle*middle+a1*middle+a0;
}

/*
*查找根
*@start: 区间起点
*@end:区间终点
*@middle:区间中点
*/
float findRoot(float start, float end, float middle){
   if((end - start) >= ACCURACY){//当区间小于阈值时,直接取区间中点
        calculat(start, end, middle);//计算函数值
    
        if(result_start == 0){
            printf("%.2f\n", start);//根为区间起点
            return 0;
        }else if(result_end == 0){
            printf("%.2f\n", end);//根为区间终点
            return 0;
        }else{
            if(result_start * result_end < 0){

            if(result_middle == 0){
                printf("%.2f\n", middle);
                return 0;
            }else{
                if(result_middle * result_start >0){
                    //递归(middle, end)右半部分
                    findRoot(middle, end, (middle+end)/2);
                }else{
                    //递归(start, middle)左半部分
                    findRoot(start, middle, (start+middle)/2);
                }
            }
        }
      }
    }else{
       printf("%.2f\n", middle);
   }
     
}

int main(){
    
    float start, end, middle;//根所在区间两端的变量以及区间中点
    scanf("%f%f%f%f", &a3, &a2, &a1, &a0);
    scanf("%f%f", &start, &end);
    middle = (start + end) / 2;
   
    findRoot(start, end, middle);

    return 0;
}

优化代码:这个代码通过while循环来替代递归,也就是每一次当起点(start)与中点(middle)值同号时,则将起点(start)改为中点(middle),即实现的了右半部分的递归。当中点(middle)与终点(end)同号时,将终点(end)改为中点(middle),即实现了左半部分的递归,而最后检测”终点减去起点“是否满足阈值,满足则继续循环。最后打印结果。

#include <stdio.h>
#include <math.h>
#define ACCURACY 0.0001
int main()
{
    double a3, a2, a1, a0;
    double a, b, mid;

    scanf("%lf%lf%lf%lf", &a3, &a2, &a1, &a0);
    scanf("%lf%lf", &a, &b);

    /* 若区间长度小于给定阈值,直接输出区间中点,不进入while循环 */
    mid = (a + b) / 2;

    while (fabs(a - b) >= ACCURACY)
    {
        /* 区间中点恰好是根 */
        if ((a3 * pow(mid, 3) + a2 * pow(mid, 2) + a1 * mid + a0) == 0)
        {
            break;
        }

        /* 根在前半区间 */
        if ((a3 * pow(a, 3) + a2 * pow(a, 2) + a1 * a + a0) * (a3 * pow(mid, 3) + a2 * pow(mid, 2) + a1 * mid + a0) < 0)
        {
            b = mid;
        }
        else     // 根在后半区间
        {
            a = mid;
        }

        mid = (a + b) / 2;

    }

    printf("%.2f\n", mid);

    return 0;
}

2、乌龟与兔子进行赛跑,跑场是一个矩型跑道,跑道边可以随地进行休息。乌龟每分钟可以前进3米,兔子每分钟前进9米;兔子嫌乌龟跑得慢,觉得肯定能跑赢乌龟,于是,每跑10分钟回头看一下乌龟,若发现自己超过乌龟,就在路边休息,每次休息30分钟,否则继续跑10分钟;而乌龟非常努力,一直跑,不休息。假定乌龟与兔子在同一起点同一时刻开始起跑,请问T分钟后乌龟和兔子谁跑得快?
输入:输入在一行中给出比赛时间T(分钟)。
**输出:**在一行中输出比赛的结果:乌龟赢输出@@,兔子赢输出_,平局则输出--;后跟1空格,再输出胜利者跑完的距离。
优化目标:看能否把代码写简洁一点。
思路:通过一个while循环来判断,条件为大于10分钟,因为主要考虑兔子每10便要抬头看情况,是睡还是继续跑。在while循环里,一进循环就让各自的路程加上10分钟所跑,然后做判断,当兔子的路程大于乌龟的路程,则兔子要睡觉,但要考虑两种情况,一是现在的时间还大于40分钟(while进来就各自跑10分钟,然后兔子睡30分钟),此时只需乌龟的路程加上30分钟跑的,并且时间减去40分钟。二是,当前时间小于40分钟,此时乌龟的路程加上剩下时间跑的,并且时间置为0,while循环结束。最后如果时间大于0小于10,还要根据情况加上这一部分时间的路程。
代码如下

#include <stdio.h>

int main(){
    
    int time;
    int r_distance =0, t_distance=0;//兔子和乌龟所跑的距离
    scanf("%d", &time);
    while(time > 10){
        r_distance += 10*9;
        t_distance += 3*10;
        if(r_distance > t_distance && time>40){
            t_distance += 3*30;
            time -= 40;
        }else if(r_distance > t_distance && time < 40){//睡觉中赢
            t_distance += 3*(time-10);
            time = 0;
        }else{
            time -= 10;
        }
    }
    if(time != 0){
        if(r_distance>t_distance){
        t_distance += 3*time;
        }else{
            r_distance += 9*time;
            t_distance += 3*time;
        }
    }
    
    
    if(r_distance > t_distance){
        printf("^_^ %d", r_distance);
    }else if(r_distance < t_distance){
        printf("@_@ %d", t_distance);
    }else{
        printf("-_- %d", r_distance);
    }
    return 0;
}

3、今天学写了动态数组及对数组的基本操作。但还没写完,就不放在今天的内容中了。明天给出。

总结:今天最深刻的就是第一题了,递归很好,但当左右两边都同时需要递归时,递归才是最好的。而今天的第一题只有一半需要递归,所以这里用递归就有点不恰当了。此外,在PTA上做题的时候,如果遇到逻辑比较复杂的题,在自己写出来后,也尽量看看别人写的好的代码,有时候自己的逻辑还是稍显复杂了,多学学别人写的。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

别偷我的猪_09

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

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

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

打赏作者

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

抵扣说明:

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

余额充值