实验项目:统计数字问题 实验日期:2017.9.7
实验目的
1.深入理解算法的概念及性质
2.学会运用算法的思想求解相应问题。
实验内容
任务:从以下题目中任选一题完成
1. 统计数字问题:一本书的页码从自然数1开始计数,直到自然数n。书的页码按照通常的习惯编排,每个页码都不包含多余的前导数字0。例如,第6页用数字6表示,而不是06或006等。数字计数问题要求对给定书的总页码n,计算出书的全部页码中分别用到多少次数字0,1,2,...,9。
2. 最多约数问题:正整数x的约数是能整除x的正整数。正整数x 的约数个数记为div(x)。例如,1,2,5,10 都是正整数10 的约数,且div(10)=4。设a 和b 是2 个正整数,a≤b,找出a和b之间约数个数最多的数x及其最多约数个数。
实验原理
仔细考虑m个n位十进制数的特点,在一个n位十进制数的由低到高的第i个数位上,总是连续出现10^i个0,然后是10^i个1……一直到10^i个9,9之后又是连续的10^i个0,这样循环出现。找到这个规律,就可以在常数时间内算出第i个数位上每个数字出现的次数。而在第i个数位上,最前面的10^i个0是前导0,应该把它们减掉。
这样,可以只分析给定的输入整数n的每个数位,从而可以得到一个log10(n)的算法
算法描述及程序
解法一:无论页码是多少都是从1...n,所以我们可以从1到n进行遍历并对每个数进行分解即可得到结果
#include<stdio.h>
int main(){
int n,i,temp; //声明并且初始化数组
int count[10]={0};
printf("input.txt");
scanf("%d",&n); //从1到n遍历数字,并分解将对应数字加1
for(i=1;i<=n;i++){
for(temp=i;temp>0;temp/=10){
count[temp%10]++;
}
} //遍历输出
for(i=0;i<10;i++)
printf("%d\n",count[i]);
return 1;
}
解法二:考虑一个数字12345,在个数上,数字出现的频率是1次,即0到9不断循环出现;而在10位数字上,每个数字是连续出现10次后再出现另一个数字;百位数字上依此类推……基于这个思路,如果我们能计算出0到9这10个数字在每一位上出现的次数,对它们进行求和,即可计算出这10个数字出现的次数。最后把那些多余的0全部去掉就可以了。
#include<stdio.h>
#include<math.h>
int main()
{
int count[10];
int i,j,k,L;
int n,len,m; /* len表示当前数字的位权 */
printf("input.txt");
while(scanf("%d",&n)!=EOF)
{
m=n;
L=ceil(log10(n+1));
for(i=0;i<10;i++)
count[i]=0;
for(j=0;j<L;j++)
{
len=ceil(log10(m+1));
k=m/pow(10.0,len-1); //从高位到低位取各个位数的值
for(i=0;i<10;i++) //小K*len的数数值0-9出现的次数
count[i]+=k*(len-1)*pow(10.0,len-2);
for(i=0;i<k;i++)
count[i]+=pow(10.0,len-1); //在j位小于数值K的数出现的次数
count[k]+=m-k*pow(10.0,len-1)+1; //在j位数值K的数出现的次数
m=m-k*pow(10.0,len-1);
}
for(i=0;i<L;i++) //去掉前导0;
count[0]-=pow(10.0,i);
for(i=0;i<10;i++)
printf( "%d\n",count[i]);
}
return 0;
}
测试与分析
(记录算法单次运行时间,并进行时间/空间复杂性分析)
1.81second(s) O(n*log10(n))
1.02second(s) O(log10(n))