Bài tập viết hàm Sinx - Giúp mình so sánh lợi và hại của 2 code này với :|
Mình có làm 1 bài tập về sin, trong đó mình tạo các hàm về mũ, giai thừa,… sau khi làm ra kết quả rồi thì mình có đi tham khảo 1 số topic trên web, có 1 đoạn code này mà tác giả có ghi chú không nên sử dụng hàm giai thừa vì sẽ làm xử lý chậm khi yêu cầu độ chính xác cao 0.00…001. Tuy nhiên khi mình test thử 2 đoạn code của mình và của tác giả đó thì tốc độ không đáng kể, mà code của mình độ chính xác cao hơn nếu tăng chỉ số 0.0…001. Vậy mình muốn hỏi mọi người, bản chất của việc tác giả nói như vậy là gì, mình mới học môn này, nên muốn rõ 1 chút. và nên làm theo code nào thì hợp lý, sao lại thế vậy
// 1. Tinh Sin(x)
// 2. Tim so pi voi do chinh xac Epsilon.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#define GH 0.00000000001
int menu(void);
void tinhsin1(void);
void tinhpi(void);
main()
{
int chon;
for(;;)
{
chon=menu();
switch (chon)
{
case 1: tinhsin1();
break;
case 2: tinhpi();
break;
case 3: exit(1);
}
}
}
int menu(void)
{
char s[10];
int c;
system("cls");
printf("1.Tinh Sin(x) khong dung ham giai thua.
");
printf("2.Tinh Pi voi do chinh xac Epsilon.
");
printf("3.Thoat.
");
do
{
printf("Moi ban bam so de chon: 1/2/3
");
gets(s);
c=atoi(s);
}
while (c<1 || c>3);
return c;
}
void tinhsin1(void)//k su dung ham giai thua
{
system("cls");
int i=1;
float x,m;
double e,s;
printf("NOTE: Khong nen dung ham tinh giai thua, ct chay lau va rat lau khi do chinh xac nho
");
printf("
Nhap vao goc x= ");
scanf("%f",&x);
m=x;
x=x*3.14/180;
s=e=x;
printf("%f radian",x);
do
{
e=-e*x*x/(2*i*((2*i)+1));
printf(" %f",e,s);
i++;
s=s+e;
}while(fabs(e)<GH);
printf("
Sin(%3.4f) = %f",m,s);
getch();
}
void tinhpi(void)
{
system("cls");
float n=3,s=1,E;
int i=0;
printf("Nhap so thuc Epsilon, vd: 0.0001: ");scanf("%f",&E);
while ((1/n)>E)
{
if (i%2==0)
s-=(1/n);
else
s+=(1/n);
i++;
n+=2;
}
printf("Ta tinh duoc pi : %f",s*4);
getch();
}
Còn đây là code của mình :
#include<iostream>
#include<stdlib.h>
using namespace std;
#define DoChinhXac 0.00000000000000000000000000001
#define PI 3.14159265358979323846
float soMu(float SoNhap, int mu)
{
float a(SoNhap);
int b(mu);
for(int i = 2; i<= mu; i++)
a*=SoNhap;
return a;
}
double giaiThua(int so)
{
double a(1);
for(int i = so;i>=1;i--)
a*=i;
return a;
}
float gtTuyetDoi(float x)
{
if(x >= 0)
return x;
if(x<0)
return -x;
}
float sin(float soX)
{
float kqua;
float x = soX*PI/180;//doi so do sang so radian
kqua = x; //gan kq = x tuong ung voi khi n = 0
int n = 1; //khoi tao n = 1 de sdung lam dieu kien <0.00001 trong vong lap
float doCX;// do chinh xac
while(1)
{
kqua += (soMu(-1,n)*soMu(x,2*n+1)/giaiThua(2*n+1)); //ap dung cong thuc gan dung Taylor cho sin(x)
n++;
if(gtTuyetDoi(soMu(x,n)/giaiThua(n))<DoChinhXac) //neu do chinh xac <0.00001 thi dat dieu kien
{
break;
}
}
return kqua;
}
int main()
{
float goc;
cout<<"Nhap goc: ";
cin>>goc;
cout<<"sin("<<goc<<") la: "<<sin(goc)<<endl;
}
Hàm sử dụng giai thừa theo cach 1 sẽ chạy nhanh hơn. Chắc là ý tác giả nói là không cần phải tính lại giai thừa và mũ vì có thể sử dụng kết quả trước đó. Hơn nữa giai thừa nên dùng kiểu số lớn 1 chút như long double để hạn chế tràn số
Mình có một bài phân tích về cách tính giá trị của các khai triển Taylor-Maclaurin.
chingovan.blogspot.com
Python: Tìm giá trị của biểu thức Taylor.
taylor, maclaurin, loop, khai triển, xấp xỉ
Cách thứ nhất là hơp lý hơn vì nếu bạn làm theo cách thứ 2 thì sẽ phải tính đi tính lại nhiều lần hàm số mũ và hàm giai thừa trong khi không cần thiết và rất có thể bị tràn số vì hàm giai thừa tăng rất nhanh. Tuy nhiên, mình thấy suy nghĩ của bạn rất tự nhiên nên bạn cần đọc thêm một số sách về phân tích thuật toán, làm sao để chọn được cách giải phù hợp (đôi khi cũng là do kinh nghiệm).
Cho cpu tính nhiều giá trị thực quá thì sẽ chậm hơn vì vậy nên hạn chế phép tính với số thực ở mức tối đa có thể