上周五的C语言思考题
题目
求和:
(1)
1
+
1
3
+
1
5
+
⋯
1+\dfrac13+\dfrac15+\cdots
1+31+51+⋯
(2) 1 − 1 2 + 1 3 + ⋯ 1-\dfrac12 + \dfrac 13 + \cdots 1−21+31+⋯
分析
这两个求和都是C语言中比较基础的(仅次于 1 + 2 + 3 + ⋯ 1+2+3+\cdots 1+2+3+⋯和 1 + 1 2 + 1 3 ⋯ 1+\dfrac12+\dfrac13\cdots 1+21+31⋯).编程题一般要求的都是给定的有限个求和,所以找出前 n n n项和的一般项公式是不必要的.计算机与数学不同(就目前的课程而言),一般要利用计算机敏与计算而不善变通的特点.所以,我们考虑最朴素的算法:没有到想要的项就一直加.
做这种求和题首先要找出可行的构造第 n n n项的方法.能以基础运算实现( + − × ÷ +-\times\div +−×÷)为上,尽量不利用幂运算或其他运算.第一问的通项很容易找到,显然是 a n = 1 2 n − 1 a_n=\dfrac{1}{2n-1} an=2n−11.而第二问则涉及奇偶,我们先写出其数学表达式 b n = ( − 1 ) n + 1 1 n b_n = (-1)^{n+1} \dfrac{1}{n} bn=(−1)n+1n1.然后利用循环即可实现.
细节
我们在有些语言中,可以把
a
i
a_i
ai这样的通项直接写成1/(2*i-1)
,但是在强类型的语言中(比如C)需要注意的是注意变量的类型.在操作中i
我们一般以整型声明,因为浮点型可能会带来一系列的麻烦(比如精度等),而1/(2*i-1)
是整型除以整型,最终的结果一定是整型,那么无论你输入多少最后的结果都会是1
,因为只有第一项1/1
时得到1,剩下的所有运算结果都会被从小数点截断变成0.
所以在这里我们必须进行强制类型转换,不建议对i
转换,我们可以考虑对分子上的1
进行转换,即(float)(1) / (2 * i - 1)
,这样i
只有在参与运算时才是一个浮点型,而作为下标(循环指标)它一直是一个整型,这是非常优雅的.
另一个值得注意的细节就在于
b
i
b_i
bi如何表示.如果一股脑地套用数学公式,用幂次来求符号是十分不划算的,因为首先stdio.h
并不支持幂运算,加数学库徒增代码长度;其次,幂运算比基本的加减乘除要慢得多.要交替改变符号我们完全可以用一个变量symbol=1
来专门储存符号(当然初始值的正负视情况而定),每一次运算改变一次就可以写成symbol*=-1
.我特地写了一个程序来验证幂运算与交替改变符号的优劣,我们直接看结果,代码稍后附上.下面的结果中Time1
是交替改变符号的耗时,Time2
是幂运算的耗时,可以看到,在n
为100000000
时运行耗时已经不在一个数量级上.
Please enter an integar n:
100000000
The sum is 0.693138.
Time1:0.277000
The sum is 0.693138.
Time2:9.219000
这其实不难理解,因为交替改变符号法为了得到正确的符号只进行了 n n n次乘法运算(即时间复杂度 O ( n ) O(n) O(n)),而幂运算法为了得到相同的效果要花费 ∑ i = 1 n ( i + 1 ) = n 2 + 3 n 2 \sum\limits_{i=1}^n(i+1)=\dfrac{n^2+3n}{2} i=1∑n(i+1)=2n2+3n次乘法(即时间复杂度 O ( n 2 ) O(n^2) O(n2)).
代码
问题一
int main(){
//输入n
int n;
printf("Please enter an odd number n:\n");
scanf("%d", &n);
//判断n是否合法
if( n%2 == 0 )
{
printf("Illega value!");
return 0;
}
n = (n + 1) / 2;//把n从值转为下标
//利用while循环计算
int i = 0;
float sum = 0;
while (i < n)
{
i++;
sum += (float)(1) / (2 * i - 1);//将1强制类型转换避免i的转换
}
printf("The sum is %f.\n", sum);
return 0;
}
问题2
#include <stdio.h>
int main(){
//输入n
int n;
printf("Please enter an integar n:\n");
scanf("%d", &n);
int i = 0;
float sum = 0;
int symbol = -1;
while (i<n)
{
i++;
symbol *= (-1);//交替改变符号
sum += symbol*((float)(1) / i);//对常量进行强制类型转换
}
printf("The sum is %f.\n", sum);
return 0;
}
程序计时
#include <stdio.h>
#include <math.h>
#include <time.h>
int main(){
int n;
printf("Please enter an integar n:\n");
scanf("%d", &n);
clock_t start, end;
double duration;
start = clock();
int i = 0;
float sum = 0;
int symbol = -1;
while (i<n)
{
i++;
symbol *= (-1);//交替改变符号
sum += symbol*((float)(1) / i);
}
printf("The sum is %f.\n", sum);
end = clock();
duration = (double)(end - start) / CLOCKS_PER_SEC;
printf("Time1:%lf\n", duration);
start = clock();
i = 0;
sum = 0.0;
while (i<n)
{
i++;
sum += pow(-1,i+1)*((float)(1) / i);//幂运算
}
printf("The sum is %f.\n", sum);
end = clock();
duration = (double)(end - start) / CLOCKS_PER_SEC;
printf("Time2:%lf\n", duration);
return 0;
}
其中time.h
就是用来实现计时功能的库文件,有兴趣的读者可以参考上述代码给自己的程序计时.