第13周报告2:定义自定义函数,计算sin和cos的近似值

任务2:先听故事,再编程序。故事是这样的:话说sin和cos是一对夫妇。一天,sin去听相声了,cos在家。过了一会,有人敲门,cos开门一看,是一个不认识的多项式函数。cos问:你是谁啊?他说:我是你的老公sin啊。cos说:你不是去听相声了吗?怎么成这幅摸样了?他说:是啊,太乐了!故事讲完了。不懂吗?好好学高数。否则,挂了不冤。


 
编程序求出sin(π/2)、cos(87°)
程序的要求是这样的:(1)求sin、cos时,不能用数学库函数(即不得用#include<Cmath>),而是自己编函数实现,为区别,可以分别起名为mysin和mycos;(2)自定义函数要写在main函数之后;(3)自定义函数的效率问题必须考虑;(4)关于精度:当最后一项的绝对值小于0.00001时,累加结束。

实验目的:学会使用自定义函数解决实际问题
实验内容:定义自定义函数,计算sin和cos的近似值

【先上调试后正确的程序】此程序上我也经历了和大家一样的磨难,犯的错误很“隐蔽”,将在后面细表。

/* 程序头部注释开始
* 程序的版权和版本声明部分
* Copyright (c) 2011, 烟台大学计算机学院学生
* All rights reserved.
* 文件名称: sin_and_cos.cpp
* 作    者: 贺利坚
* 完成日期: 2011 年 11 月 22 日
* 版本 号: v2.0        

* 对任务及求解方法的描述部分
* 输入描述:无
* 问题描述:自定义函数,用泰勒公式实现计算sin和cos的近似值,要求:(1)求sin、cos时,不能用数学库函数(即不得用#include<Cmath>),而是自己编函数实现,为区别,可以分别起名为mysin和mycos;(2)自定义函数要写在main函数之后;(3)自定义函数的效率问题必须考虑;(4)关于精度:当最后一项的绝对值小于0.00001时,累加结束。
* 程序输出:sin(π/2)、cos(87°)的值(提示:用泰勒公式在π/2附近误差较大,输出分别为0.911557和-0.26322,而真值分别为1和0.052336,当度数较小时,效果要好一些。)
* 算法设计:使用泰勒公式
* 程序头部的注释结束(此处也删除了斜杠)
*/
#include <iostream>
#include<Cmath>  //为便于对比结果,main函数中调用了Cmath中的库函数sin和cos
using namespace std;
const double pi=3.1415926;
double mysin(double);
double mycos(double);
double myabs(double);   //程序中需要求精度的绝对值,也用自定义函数完成吧
int main( )
{
   cout<<"sin(π/2)的值为"<<mysin(pi/2)<<endl;
   cout<<"cos(87°)的值为"<<mycos((87.0/180)*pi)<<endl;
   cout<<"利用库函数求得sin(π/2)的值为"<<sin(pi/2)<<endl;
   cout<<"利用库函数求得cos(87°)的值为"<<cos((87.0/180)*pi)<<endl;
   system("PAUSE");
   return 0;
}
//下面定义mysin函数
double mysin(double x)
{
   double sum=x,x_pow=x,item;
   int n=1,fact=1,sign=1;     //定义变量时赋初值,已经将第一项考虑到累加和sum中
   do
   {
            fact=fact*(n+1)*(n+2);  //fact用于表示阶乘,在公式中作分母
             x_pow*=x*x;             //x_pow是分子中用于表示阶乘,在公式中作分母
             sign=-sign;             //确定即将要累加的这一项的符号
             item =x_pow/fact*sign; //计算出要累加的项
             sum+=item;              //将该项累加上去
             n+=2;
  }while(myabs(item)>1e-5);
   return sum;
}

//下面定义mycos函数
double mycos(double x)
{
   double sum=1,x_pow=1,item;
   int n=0,fact=1,sign=-1;
   do
   {
            fact=fact*(n+1)*(n+2);
             x_pow*=x*x;
             item =x_pow/fact*sign;
             sum+=item;
             sign=-sign;
             n+=2;
  }while(myabs(item)>0.00001);
   return sum;
}

//下面定义myabs函数
double myabs(double x)
{
   return ((x>=0)?x:-x);
}

运行结果:

 

 

经验积累:

1. 做科学计算时,需要对所用方法的数学性质有所了解

2. 取合适的变量名(sum,x_pow,item,n,fact,sign)有助于以一种清晰的思路解题,保证了程序的可读性

3. 对于复杂的计算,不妨多设几个变量,将他们间的关系分清楚,可以会多费些内存,但对正确性的保证无可替代

 

  【下面讲讲我犯的愚蠢的错误】仔细看看这个程序,和我一起分析清楚问题,是我对大家最大的贡献。

  原先,我的nysin函数是这样写的,貌似合理:

double mysin(double x)
{
 double sum=x,item=x;
 int n=1,fact=1,sign=-1;
 do
 {
  fact=fact*(n+1)*(n+2);  //求阶乘
  item =item*x*x/fact*sign;  //要加的项
  sum+=item;   //累加
  sign=-sign;
  n+=2;
 }while(myabs(item)>1e-5);
 return sum;
}

  据此计算得到sin(pi/2)的结果是0.911557。人的心理一般是这样的,没错呀?怎么会错呢?我怎么会错呢?看了一遍又一遍,结果当然是我没有错,然而输出结果和库函数给出的结果就是不一样。上周三我在准备和这个错误一直作斗争,已经过了12:00点了,得吃饭,13:20 需要从家出发,去教室上课,但是实验报告的模板还得出来。

  怎么办?泰勒公式会有问题?我没错!带着这个心理,我在指导书写下了泰勒公式可能的误差的文字。现在想来,这是多么不严谨的做法,以至于后来一再改模板。

  逐渐想到,还是程序中有错误。我们现在一起找一下。

  针对泰勒公式:

  在变量定义的同时,通过赋初值,已经考虑了将x加到sum中:

     double sum=x,item=x;

     int n=1,fact=1,sign=-1;

  下面需要构造进入循环以后将各项一正一负地累加到sum中。

  当第1次进行循环:

    fact=fact*(n+1)*(n+2);  得到了3!

    item =item*x*x/fact*sign;  求得要加的项是-x^3/3!,没错。(x^3表示x的3次方,在此只为方便表达,并不是C++中的合法运算。)

    sum+=item; 得到了x-x^3/3!。

    随后sign变为1、n自增2变为5。都没有问题。

  第2次进入循环:

    fact=fact*(n+1)*(n+2);  得到了5!,没错

    item = item*x*x/fact*sign;呢?这时才发现item将变为(-x^3/3!)*x*x/5!*1=-(x^5)/(3!*5!),不仅分母不对,符号也不对。

  !!!!

  这时,只有敲自己的脑袋了(师生似乎都一样的)。

  于是,有了上面提交的结果。

  仔细反思,这儿犯的错误根源是,让item变量承担了多项职责。我们设置变量的原则是,每个变量的功能尽可能单一。

  再看正确做法中,fact、 x_pow、item 、sum和sign的含义,清楚多了。很多同学的程序中,即使功能单一,用诸如a、b、c、d、e、f、g等做变量,只能搞糊涂自己。

  至于mycos,类似的问题,不再多说。

 

  【同学们解法中的一个典型错误】

  有不止一位同学的程序中,函数定义由double mysin(double x)开始,这没有问题,x是形式参数。

  但是在函数体内,有些同学对x重新赋值:x=pi/2;有些同学将本该出现x的地方直接写作了(pi/2)。的确,这样做能够求出sin(pi/2)的值,但是,这也使得你定义的函数只能求得sin(pi/2)的值了。我后悔在任务中应该多加一个求sin(pi/4),这样能启发同学们改过来。

  一定要注意,尽管我们编程序解决某个具体问题,但是写出的函数还是要“通用”一些才好,这体现出的是程序与数据的“独立性”。慢慢体会吧。

 

时间: 2024-11-08 17:57:35

第13周报告2:定义自定义函数,计算sin和cos的近似值的相关文章

第13周报告1:用函数完成分段数学函数的求解

任务1:编写函数,求解分段函数的值,并设计能够满足路径覆盖的测试用例完成测试.           0                           当x<0.3时; f(x)=  (x-0.3)/(0.8-0.3)  当0.3≤x≤0.8时          1                             当x>0.8时 实验目的:学会使用函数编程,以及设计比较完备的测试用例完成对小程序的测试 实验内容:用函数完成分段数学函数的求解 /* * 程序的版权和版本声明部分 *

C++第13周项目5——银行系统用函数组织(v0.2)

课程首页地址:http://blog.csdn.net/sxhelijian/article/details/7910565 说明:此项目是为未学习数组的同学拟出的,目的是让他们对"工程"有所体验.一些实现并不是最佳的,请参考后续的题目 [项目5任务2]引入函数改善程序结构 1.在输入密码环节,实现有限次密码输入的限制,如果三次都没有输入正确,程序将直接退出,不允许用户继续操作. 查验密码功能做成一个函数.例如: bool pass() { bool 密码不正确=true; //先假设

Python自定义函数计算已经过去了多少个周末实例分享

这个Python自定义函数的功能是计算过去了多少个周末. 参数说明:d1表示过去的日期,d2表示现在的日期 返回值是时间差周末的个数 #Python代码  代码如下 复制代码 def weekends_between(d1, d2):     days_between = (d2-d1).days     weekends, leftover = divmod(days_between,7)     if leftover:         start_day = (d2-timedelta(l

第14周报告2:使用函数解决实际问题

任务2:(函数及数组的简单应用) 在数组score中将要存储某小组C++程序设计的成绩,请设计完成下面功能函数,并将它们组合成一个完整的应用: (1)输入小组人数及成绩: (2)输出该小组的最高成绩.最低成绩.平均成绩和成绩的标准偏差: (3)输出考得最高成绩和最低成绩的同学的人数及对应的学号(设成绩对应的下标即学号,可能有相同的成绩) 实验目的:学会使用函数解决实际问题 实验内容:用函数编写程序,成绩的计算 /* 程序头部注释开始 * 程序的版权和版本声明部分 * Copyright (c)

第16周报告1:冒泡排序指针版

题目:(数组的排序) 按模板中的要求,用指向数组的指针变量作为形式参数编写函数,完成冒泡排序.重点体会:(1)冒泡排序算法:(2)用指向数组的指针变量作为形式参数,用数组名(即数组的地址)作为实际参数,函数中对于形参的改变实质上也就是对实参对应内存单元的改变:(3)形式参数中不指定数组大小,实际数组的大小也一并作为参数传递.详见 指导书 实验目的:学会冒泡排序算法 实验内容:实现冒泡排序算法,并将之定义为一个函数,其中参数是指向数组的指针变量 /* 程序头部注释开始 * 程序的版权和版本声明部分

第15周报告1: 冒泡排序

任务1:(数组的排序)编函数,完成冒泡排序.要求不能改变下面的main函数. 重点体会:(1)排序算法:(2)数组名作形式参数,将能改变作为实际参数的数组的值,实际参数传递给形式参数的是数组的地址值,也是传值:(3)形式参数中不指定数组大小,实际数组的大小也一并作为参数传递. 实验目的:学会冒泡排序算法 实验内容:实现冒泡排序算法,并将之定义为一个函数 /* 程序头部注释开始 * 程序的版权和版本声明部分 * Copyright (c) 2011, 烟台大学计算机学院学生 * All right

2014秋C++第13周项目4参考-数组的排序

课程主页在http://blog.csdn.net/sxhelijian/article/details/39152703,课程资源在云学堂"贺老师课堂"同步展示,使用的帐号请到课程主页中查看.  [项目4 - 数组的排序](1)编写函数,完成冒泡排序,要求不能改变下面的main函数. //两个函数bubble_sort和output_array的声明 int main( ) { int a[20]={86,76,62,58,77,85,92,80,96,88,77,67,80,68,

C02-程序设计基础提高班(C++)第13周上机任务-多态与虚函数

第13周:阅读教材第12章(p395-416),主要内容是多态性,完成第13周上机任务 (回到C02-程序设计基础提高班(C++)学习安排) [任务1]下面给出了基类Animal和main()函数.(1)根据main()函数给出的注释提示,设计出相关的各个类.(2)显然,Animal设计为抽象类更合适,Animal不需要能够实例化,是专门作基类使用的.改造程序,使Animal设计为抽象类,这时main()函数中p = new Animal();将出错,将此行删除.(3)每一个Animal的派生类

T-SQL: 15 个与日期时间相关的自定义函数(UDF),周日作为周的最后一天,均不受 @@DateFirst、语言版本影响

函数 /*T-SQL: 15 个与日期时间相关的自定义函数(UDF),周日作为周的最后一天,均不受 @@DateFirst.语言版本影响都是从老文章里收集或提炼出来的!提示:(@@Datefirst + datepart(weekday,@Date)) % 7 判断周几是最保险的! 与 @@DateFirst 无关,与语言版本无关@@DateFirst 可能会导致 datepart(weekday,@Date) 不一样!无论 @@DateFirst 等于几,无论是什么语言版本的 SQL Serv