从0到n的所有数字中出现“2”的次数

时间:2014-04-23 10:07:59

标签: algorithm math language-agnostic

  

查找多少次" 2"发生在从0到n的所有数字中。

示例:n = 112 answer = 22

numbers : 2 , 12 , 20-29 , 32 ...  92 , 102 ,  112  

我想出了以下方法:

k = n的位数

f(k) =从010^k-1的两个人的数量..

  

f(1)= 1 // 2
  f(2)= 10 * f(1)+ 10 ^(2-1)// 12,20-29,32 ... 92
  f(3)= 10 * f(2)+ 10 ^(3-1)
  等等...

twos(int n , int k)
{
    if(n<2)
        return 0;
    else if(n<10)
        return 1;

    msb = n/10^k
    remainder = n%10^k

    if(msb==2) // eg 230 ,  calculate 200 ... to 230  , and find twos from 0 on 199  and recursion on 30
        return msb*(f(k)) + (remainder+1) + twos(remainder) // 2*(below 100) + 31 + tows(30)
    else if (msb>=3)
         return msb*(f(k)) + (10^k) + twos(remainder)  // for 312 -> 3*(below 100) + 100 + twos(12)
    else
        return  msb*(f(k)) + twos(remainder)

}  

我的方法是否正确?
如果是这样,有没有比这更快的解决方案?

编辑
对于这些测试用例,我的方法似乎是正确的,here是模拟 results
第一个颜色:n,第二个颜色:蛮力,第三个颜色:我的方法

  

2 1 1   5 1 1
  91 19 19
  868 277 277
  7783 3359 3359
  55576 32718 32718
  293911 242093 242093
  10179297 7091858 7091858
  59939789 51975959 51975959

2 个答案:

答案 0 :(得分:1)

f(k)为数字2的出现次数,从0到10^k-1的数字(即最多k位数字)。 请注意,当将数字从k-1增加到k时,我们对每个新的前导数字都有f(k-1)个出现,加上前导数字本身的10 ^(k-1)个出现:

f(k) = 10*f(k-1) + 10^(k-1)

(这正是你的公式)

解决这种复发问题:

f(k) = k * 10^(k-1)

例如:

f(1) = 1
f(2) = 20
f(3) = 300
f(4) = 4000
...

实际上有一种不同的方法来获得这个公式。考虑所有k位数字(缺少的前导数字用零替换)。数字的数量是10 ^ k,每个数字都有k个数字,因此我们总共有k*10^k个数字。现在,每个可能的数字都显示相同的次数,即k*10^k / 10 = k*10^(k-1)

- 编辑 -

以下Java解决方案使用上面的公式,并解决了O(log n)时间内任意n的问题:

// Counts occurrences of digit 2 in numbers less than n
public static int count(int n) {
  int count = 0;
  int factor = 1;
  int pos = 0;
  while (true) {
    int m = n / factor;
    if (m == 0) //Past last digit
      break;
    int d = m % 10;      

    count += d * pos * factor / 10; 

    if (d == 2)
      count += n % factor;
    else if (d > 2) {
      count += factor; 
    }

    factor = 10 * factor;
    pos++;
  }

  return count;
}  

答案 1 :(得分:1)

假设数字由数字n=... n4 n3 n2 n1组成。我们希望查看所有数字x<=n并计算其中的数字2x应表示为x=... x4 x3 x2 x1。首先,我们计算有多少数字的最后一位数为x1==2

f1(n) = (n+8) / 10

假设分割截断。一些例子:

f1(1) = 0
f1(2) = 1
f1(12) = 2
f1(21) = 2
f1(22) = 3 

现在让我们计算一个倒数第二位的数字x2==2。这类似于切割n的最后一位数字,然后计算最后两位数字的数字:

f2(n) = 10 * f1(n/10)  # almost right

让我们测试一下:

f2(10) = 0
f2(20) = 10 * f1(2) = 10  # wrong
f2(25) = 10 * f1(2) = 10  # wrong
f2(29) = 10 * f1(2) = 10  
f2(30) = 10 * f1(3) = 10

n的倒数第二位是{2}本身n2==2时,我们需要对此案例进行特殊处理:

f2(n) = 10*f1(n/10) - 9 + n1 # if n2==2

使用n1 == n % 10(余数函数),可以写成:

f2(n) = 10*f1(n/10) - 9 + n%10 # if n2==2

类似于计算x3==2

的数字
f3(n) = 100*f1(n/100) # if n3 != 2
f3(n) = 100*f1(n/100) - 99 + n%100 # if n3 == 2

总结我们得到的总数为2:

f(n) = f1(n) + f2(n) + f3(n) + ...

在Java中:

public static int count2(int n) {
    int res = (n+8) / 10;
    int divider = 10;
    while (n>= 2*divider){
        int na = n / divider;
        res = res + divider * ((na+8) /10);
        if (na%10==2)
             res = res - divider + 1 + n % divider;
        divider = divider * 10;
    }
    return res;
}