1的数目

Published: 06 Jun 2013 Category: 编程

题目:给定一个十进制正整数N,写下从1开始到N的所有整数,数一下其中出现所有1的个数。

例如N=12,写下1,2,3,4,。。。,12,1的个数为5。

问题一

写出函数f(N),表示对于给定的N,计算1到N之间的"1"的个数。

解法一

最直接的解法:从1遍历到N,将其每一个数中含有"1"的个数加起来。

此方法复杂度太高,为O(nlogn)。每个数计算此数的每一个位的数。

解法二

此方法是《编程之美》给的解法。

计算每一位的出现1的个数,如2345,分别计算千位、百位、十位、个位上可能出现1的个数。

以百位为例,可以找到以下规律:

  • 当百位上的数字为0,如12023,百位上出现1的个数由更高位12决定。每一千个数0~999或1000~1999中,百位数出现1的数量为100次。因此,出现的1的个数为更高位(12)*当前位数(100)=1200.
  • 当百位上的数字为1,如12123,百位上出现1的个数由更高位(12)和低位(23)决定。除了上一情况出现的个数,还有12100~12123中百位数中1出现的个数。即:出现的1的个数为更高位(12)*当前位数(100)+低位(23)+1.
  • 当百位上出现的数字>1,如12223和第一种情况类似,但是要将高位+1(因为12***中的那一个也要考虑进去)。即:出现的1的个数为更高位+1(12+1)*当前位数(100)=1300.

[cpp]

int oneCount(int n){

int iCount=0;

    int iFactor=1;

    int iLow=0,iCurrent=0,iHigh=0;

    while(n/iFactor!=0){

        iLow=n%iFactor;

        iCurrent=(n/iFactor)%10;

        iHigh=n/(iFactor*10);

        switch(iCurrent){

        case 0:

            iCount+=iHigh*iFactor;

            break;

        case 1:

            iCount+=(iHigh*iFactor+iLow+1);

            break;

        default:

            iCount+=(iHigh+1)*iFactor;

            break;

        }

        iFactor*=10;

    }

    return iCount;

}

[/cpp]

解法3

此方法是我自己想的解法。

分析发现以下规律:

f(9)=1,f(99)=20,f(999)=300,f(9999)=4000。及f(10^b-1)=b*10^(b-1)。(定理一)

为什么会这样呢。因为对于每个位,出现1的概率为1/10,然后乘以位的数量。如999,个位出现1的概率为1/10,则个位出现100个1,同理,十位100个,百位100个。即300个。

解法:

  1. 将数字2345分解为2000+300+40+5,分别计算他们中含有的1的数量。
  2. 对于每个数字,如2000,我们知道(0~999)中1的个数为300,1000~1999中除了千位1出现的个数同样为300.即2*300.
  3. 在以上基础上,考虑千位出现1的个数,分为以下情况:
    1. 如果千位为0,不会出现。
    2. 如果千位为1(如1234),则千位出现的数量为千位后面的数+1,即234+1.
    3. 如果千位>1,则千位出现1的数量为1000.

[cpp]

int bitCount(int n){

int b=0;

int flag=n/(pow((float)10,b));

while(flag!=0){

    b++;

    int t=pow((float)10,b);

    flag=n/t;

}

return b;

}

int oneCount2(int n){

int count=0;

int b=bitCount(n);

for(int i=b;i>0;i--){

    int tennow=pow((float)10,i-1);

    int tCur=n/tennow;

    n%=tennow;

    if(tCur==0){

        continue;

    }

    if(tCur==1){

        count+=n+1;

    }

    else{

        count+=tennow;

    }

    count+=(tCur*((tennow/10)*(i-1)));

}

return count;

}

[/cpp]

问题二:

满足条件f(N)=N的最大的N是多少?

根据定理一可知,当N的位数b<=10时,N=10^b-1,f(N)=b*10^(b-1),则N>f(N)。

而b=11时,f(N)>N。

当n>10^(11-1)时,能否证明不可能出现f(n)<n?如果可以证明,则在b=10~11中肯定能找到一个最大的N,使f(N)=N.

我证明不太出来,但是大体感觉是不能出现。因此,最大的N应该在b=10~11。

因此,令N=10^11-1=99 999 999 999,使n从N递减,检查是否f(n)=n,第一个就是最大的。

思想:找到N的范围,然后使用穷举法。不能直接使用穷举法,第一,时间复杂度太大。第二,因为要求最大的,因此没有一个终止条件。 当求最大的某个数的时候,首先要去判断一个上界,然后可以从上界朝下去穷举。

扩展问题:

对于其他进制,如二进制,f(1)的计算方法。

类比于问题一中的解法二和解法三,可以有两种解法。

解法一(对应于问题一解法二)

计算每个位出现1的次数。对于每一位:

根据当前位b的值(0或1),如果当前的值为0,则根据此位之前的数乘以当前位数(2^b)。

若当前值为1,此位之前的数乘以当前位数+此位之后的数+1.

解法二(对应问题一中解法三)

分析发现f(1)=1,f(11)=4,f(1…1<b个>)=b*2^(b-1)

将10101分为10000+100+1分别计算1的个数。

f(10101)=f(10000)+count(10000后面的数中该位1的数量)+f(100)+count(100后该位1的数量)+f(1)

根据定义可知f(1000)=f(1000-1)+1;

综上所述:

f(10101)= f(10000)+(101+1) +f(100)+(1+1)+f(1)= f(9999)+1+(101+1) +f(99)+1+(1+1)+f(1)