這個是《編程之美》上的一個題目,題目如題:
給定一個十進制正整數N,寫下從1開始,到N的所有整數,然後數一下其中出現的所有1的個數。
書上給出了兩個解法,第一個就是笨方法,挨個數唄,最後加一塊。
第二個解法,有興趣的自己看書上的分析吧,這裏先把Java實現的代碼貼下:
public static long F_Sum1s(long n){
long iCount = 0;
long iFactor = 1;
long iLowerNum = 0;
long iCurrNum = 0;
long iHigherNum = 0;
while(n/iFactor != 0){
iLowerNum = n - (n/iFactor)*iFactor;
iCurrNum = (n/iFactor)%10;
iHigherNum=n/(iFactor*10);
switch(String.valueOf(iCurrNum)){
case "0":
iCount += iHigherNum*iFactor;
break;
case "1":
iCount += iHigherNum*iFactor+iLowerNum+1;
break;
default:
iCount += (iHigherNum+1)*iFactor;
break;
}
iFactor *= 10;
}
return iCount;
}
之前我想到一個遞推公式,想來和書上的解法本質應該差不多,現把公式和代碼貼出來供大家討論下,是否正確(自己簡單試了下,12位以下的數字,和書上的結果一樣,但沒嚴格證明過)。
實際求的時候,先計算該數長度,然後判斷Gn存在否,不存在,就求出之,最後遞推求出結果,代碼如下:
package algorithm;
public class Sum1s {
public static long F_Sum1s(long n){ //上面已貼出}
int[] GArr = new int[100]; //存放G的數組
int GArrIndex = 0;
public Sum1s(){
GArr[0]=0; GArr[1]=0; GArr[2]=1;
GArrIndex = 2;
}
public long My_Sum1s(long n){ //計算的函數
if(n == 0){
return 0;
}
int numLength = 1;
long tempNum = n;
int An = 0;
while(tempNum/10 != 0){
numLength += 1;
tempNum = tempNum/10;
}
An = (int)tempNum;
//System.out.println("NumLength = "+numLength);
if(numLength == 1){
return 1;
}
if(numLength > GArrIndex){
InitGArr(numLength);
}
long numLength_1_10 = 1;
for(int i=1; i<numLength; i++){
numLength_1_10 *= 10;
}
long Vn_1 = n-An*numLength_1_10;
if(An == 1){
return GArr[numLength]+1+Vn_1+My_Sum1s(Vn_1);
}else{
return (An*GArr[numLength]+numLength_1_10)+My_Sum1s(Vn_1);
}
}
public void InitGArr(long numLength){
long numLength_1_10 = 1;
for(int i=1; i<GArrIndex; i++){
numLength_1_10 *= 10;
}
for(++GArrIndex;GArrIndex<=numLength;GArrIndex++){
numLength_1_10 *= 10;
GArr[GArrIndex] = (int)My_Sum1s(numLength_1_10-1);
}
GArrIndex--;
}
public static void main(String args[]){
//System.out.println(Sum1s.F_Sum1s(1999));
Sum1s sum1s = new Sum1s();
//long sum1,sum2;
//int i=999;
//while(true){
// sum1 = Sum1s.F_Sum1s(i);
// sum2 = sum1s.My_Sum1s(i);
// System.out.println(i);
// if(sum1 != sum2){
// System.out.println("sum1="+sum1+",sum2="+sum2);
// break;
// }
// i++;
//}
System.out.println(sum1s.My_Sum1s(1000));
}
}
接着看《編程之美》發現求Gn有個歸納出的公式
9: 1個
99: 20個
999: 300個
9999: 4000個
。。。
999 999 999: 900 000 000個
9 999 999 999: 10 000 000 000個
f(10^n -1) = n*10^(10-1)。那麼前面Gn的地方就簡單了。直接帶入公式,當然10的冪沒必要從頭計算。