【问题标题】:How many times "2" occurs in all numbers from 0 to n从 0 到 n 的所有数字中出现多少次“2”
【发布时间】:2014-04-23 10:07:59
【问题描述】:

找出从 0 到 n 的所有数字中出现了多少次“2”。

示例: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

【问题讨论】:

  • 为了正确性,您是否使用哑循环进行检查?
  • 一个循环给出了 n=10^7 的运行时错误,这里 n 可以到 10^18
  • 您始终可以将解决方案的结果与蛮力实施的结果进行比较。理论上它不能证明您的解决方案对于每个输入都是正确的,但它可以提供一些信心...
  • @aseem:这个想法是用小数字测试它......例如:它是否为所有 n
  • 只要你坚持使用 base-2,所有 n 的答案都是 0 ;p

标签: algorithm math language-agnostic


【解决方案1】:

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

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

假设除法被截断。一些例子:

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

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

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的倒数第二个数字本身是一个二,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;
}

【讨论】:

    【解决方案2】:

    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;
    }  
    

    【讨论】:

    • 这部分我做对了,但是当 n 不是 10 的幂时失败
    • @aseem:当然会失败,因为 f(k) 被定义为对 0..10^k-1 范围内的出现次数进行计数。你必须做额外的工作来处理任何数字。
    • 嗯,这就是任务。我不明白这是如何回答这个问题的。
    • @KarolyHorvath:我不确定我是否理解。 OP 询问他的方法是否正确,以及如何提高计算效率。我想我解决了这两个问题。
    • 在某些情况下给出错误答案 ideone.com/ewOc49 。顺便说一句,谢谢你的解释。
    猜你喜欢
    • 2022-10-30
    • 2020-11-24
    • 1970-01-01
    • 2012-08-07
    • 1970-01-01
    • 1970-01-01
    • 2015-09-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多