算法

来源:互联网 发布:改图片格式的软件 编辑:程序博客网 时间:2024/06/13 10:59

实验项目:统计数字问题      实验日期:2017.9.7

实验目的

1.深入理解算法的概念及性质

2.学会运用算法的思想求解相应问题。

实验内容

任务:从以下题目中任选一题完成

1. 统计数字问题:一本书的页码从自然数1开始计数,直到自然数n。书的页码按照通常的习惯编排,每个页码都不包含多余的前导数字0。例如,第6页用数字6表示,而不是06或006等。数字计数问题要求对给定书的总页码n,计算出书的全部页码中分别用到多少次数字0,1,2,...,9。

2. 最多约数问题:正整数x的约数是能整除x的正整数。正整数的约数个数记为div(x)。例如,12510 都是正整数10 的约数,且div(10)=4。设个正整数,ab,找出ab之间约数个数最多的数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))

原创粉丝点击