统计从1到n所有正整数中1出现的次数
Posted Flammable_ice
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了统计从1到n所有正整数中1出现的次数相关的知识,希望对你有一定的参考价值。
这是来自《编程之美》2.4中的题目,我只给出我的新方法,书上的方法这里略过不提,因为网上已经有很多解释书上的内容的博文了。
以下是分析:
我们先从最简单的数字开始分析。
0~9:个位数>=1时,1的出现次数均为1.个位数=0时,出现0次1.......................................................................①
10~99:十位数字=1时,比如17.先分析10~17中所有两位数中个位数上1的个数=1,再分析10~17所有两位数中十位数上1的个数=17-10+1,然后再分析1~9中所有个位数1的出现个数=1,所以总和为第一次循环计算10~17所有两位数中个位数中1的个数为1+第二次循环计算10~17所有1出现次数和2+17-10(1~10中1的个数+11~17所有十位数上1的出现次数)=10
十位数>1时,比如58,先分析10~58所有两位数中个位数上1的个数=5,再分析10~58所有两位数中十位数上1的个数=19-10+1,然后再分析1~9中所有个位数1的出现个数=1,所以总和为第一次循环计算个位数中1的个数为1+第二次循环计算10~58中所有1出现次数和10+(2-1)*5=16。
十位数=0时,转到①执行。
100~999:百位数=1时,比如123,先分析101~123中个位数+十位数中1的个数和相当于求0~23的所有1出现次数为13,再计算101~123中百位数上的1的个数和=123-101+1.然后计算1~100中1出现次数和。所以总和为(21+123-100)(百位上1的出现次数+100以前所有1出现次数)+13(1~23所有1的个数和相当于100~123中十位数和个位数1的个数和)。
百位数>1时,比如246,先分析前200中1的出现次数=前99中1的出现次数+100~200中1的出现次数=21+101~200中个位和十位1个数和+百位数1的个数和=21+前99中的出现次数和+101~200中百位数1的出现次数和=21-1+21-1+100=100+(21-1)*2.然后再计算200~246中十位和个位数1的个数和(相当于求0~46所有1的个数和15)。最后总和=100+(21-1)*2+15=155
百位数=0时,转到上面十位数的情况。
我写的程序是,比如计算123,先计算1~3中1的出现次数和,然后再次循环计算1~23中1的出现次数和,这些都计算好了,第3次循环才计算1~123中1的出现次数和。另外在求给定数字之前,我们先把n=10,100,1000,10000........诸如这些整数求好存到数组里,然后利用这些数来求给定数。
现在给出代码:
#include <iostream>
#include <time.h>
using namespace std;
#define n 11//11位
__int64 f(int x)
__int64 s=1;
while (x--)
s*=10;
return s;
__int64 Total_Count(__int64 x,__int64 a[])
int temp[n]=0,i=0,t=x;
__int64 sum=0,s=1,q=0;
while (x)
temp[i++]=x%10;
x/=10;
for (int j=0;j<i;j++)
q+=temp[j]*s;
if (temp[j]>1)
if(s==1) sum+=1;
else sum+=s+(a[j]-1)*temp[j];
else if(temp[j]==1)
if(s==1) sum+=1;
else sum+=a[j]+q-s;
s*=10;
return sum;
void main()
long i = 10000000L;
clock_t start, finish;
double duration;
/* 测量一个事件持续的时间*/
printf( "Time to do %ld empty loops is ", i );
start = clock();
srand(unsigned int (time(NULL)));
__int64 a[n+1]=1,2;
for (int k=2;k<=n;k++)
a[k]=10*a[k-1]+f(k-1)-9;
int t=0;//注释部分是求1出现次数和最大正整数相等时的值,通过这个循环比较两种方法的运行时间不失为一种好的方法。
/*for (__int64 j=1;j<1000000000;j++)
if (Total_Count(j,a)==j)//这里换成下面那个书上的函数作为对比,您就知道这种方法的效率了。
printf("%I64d\\n", j);
t++;
*/
cout<<endl;
printf("%I64d\\n", Total_Count(246,a));
cout<<"共"<<t<<"个"<<endl;
finish = clock();
duration = (double)(finish - start) / CLOCKS_PER_SEC;
printf( "%f seconds\\n", duration );
下面给出《编程之美》上面的代码作为对比:
__int64 Count(__int64 m)
//1的个数
__int64 count = 0;
//当前位
__int64 Factor = 1;
//低位数字
__int64 LowerNum = 0;
//当前位数字
__int64 CurrNum = 0;
//高位数字
__int64 HigherNum = 0;
if(m <= 0)
return 0;
while(m / Factor != 0)
//低位数字
LowerNum = m - (m / Factor) * Factor;
//当前位数字
CurrNum = (m / Factor) % 10;
//高位数字
HigherNum = m / (Factor * 10);
//如果为0,出现1的次数由高位决定
if(CurrNum == 0)
//等于高位数字 * 当前位数
count += HigherNum * Factor;
//如果为1,出现1的次数由高位和低位决定
else if(CurrNum == 1)
//高位数字 * 当前位数 + 低位数字 + 1
count += HigherNum * Factor + LowerNum + 1;
//如果大于1,出现1的次数由高位决定
else
//(高位数字+1)* 当前位数
count += (HigherNum + 1) * Factor;
//前移一位
Factor *= 10;
return count;
以上是关于统计从1到n所有正整数中1出现的次数的主要内容,如果未能解决你的问题,请参考以下文章