【问题标题】:Count the Number of Zero's between Range of integers计算整数范围之间的零的数量
【发布时间】:2015-09-14 15:55:41
【问题描述】:

。是否有任何直接公式或系统来找出不同范围之间的零的数量......让两个整数 M 和 N 给出。如果我必须找出这个范围之间的零的总数,那我该怎么办?

设 M = 1234567890 & N = 2345678901 答案是:987654304

提前致谢。

【问题讨论】:

  • 是 .... 更准确地说 .. 在 M 和 N 之间 ... Exist 有多少个“0”数字。 @mvw
  • 嘿,我想查找特定范围@mvw 之间的“0”位数
  • 我添加了一个可以解决您问题的答案。
  • 这可能取自here。顺便说一句,我需要 3000 多个。

标签: combinatorics number-theory


【解决方案1】:

重新审视问题

这是 Ruby 中的一个简单解决方案,它检查区间 [m,n] 中的每个整数,确定其数字在标准以 10 为底的位置系统中的字符串,并对出现的 0 位进行计数:

def brute_force(m, n)
  if m > n
    return 0
  end
  z = 0
  m.upto(n) do |k|
    z += k.to_s.count('0')
  end
  z
end

如果你在交互式 Ruby shell 中运行它,你会得到

irb> brute_force(1,100)
=> 11

这很好。但是使用问题示例中的区间界限

m = 1234567890
n = 2345678901

您会意识到这需要相当长的时间。在我的机器上它确实需要超过几秒钟,到目前为止我不得不取消它。

因此,真正的问题不仅是要提出正确的零计数,而且要比上述蛮力解决方案更快地做到这一点。

复杂性:运行时间

蛮力解法需要执行n-m+1次搜索以10为底的数字k,其长度为floor(log_10(k))+1,所以不会使用超过

O(n (log(n)+1))

字符串数字访问。慢示例的 n 大约为 n = 10^9。

降低复杂性

荣一鸣的回答是第一次尝试降低问题的复杂性。

如果计算区间[m,n]的零个数的函数是F(m,n),那么它有性质

F(m,n) = F(1,n) - F(1,m-1)

因此只要寻找一个最有可能具有该属性的更简单的函数 G 就足够了

G(n) = F(1,n)。

分而治之

为函数 G 提出一个封闭的公式并不容易。例如。 区间 [1,1000] 包含 192 个零,但区间 [1001,2000] 包含 300 个零,因为像第一个区间中的 k = 99 这样的情况将对应于第二个区间中的 k = 1099,这会产生另一个零位数。 k=7 将显示为 1007,产生另外两个零。

人们可以尝试用更简单的问题实例的解决方案来表达某些问题实例的解决方案。这种策略在计算机科学中被称为分而治之。如果在某种复杂程度可以解决问题实例,并且如果可以从更简单问题的解决方案中推断出更复杂问题的解决方案,那么它就可以工作。这自然会导致递归公式。

例如我们可以为 G 的受限版本制定解决方案,该版本仅适用于某些论点。我们称它为 g,它被定义为 9、99、999 等,并且对于这些参数将等于 G。 可以使用这个递归函数来计算:

# zeros for 1..n, where n = (10^k)-1: 0, 9, 99, 999, ..
def g(n)
  if n <= 9
    return 0
  end
  n2 = (n - 9) / 10
  return 10 * g(n2) + n2
end

请注意,此函数比蛮力方法快得多:要计算区间 [1, 10^9-1] 中的零,与问题中的 m 相当,它只需要 9 次调用,其复杂度是

O(log(n))

再次注意,这个 g 不是针对任意 n 定义的,仅针对 n = (10^k)-1。

g的推导

首先要找到函数 h(n) 的递归定义, 如果十进制表示具有前导零,则将 1 到 n = (10^k) - 1 的数字中的零计数。

示例:h(999) 计算数字表示的零位:

  • 001..009
  • 010..099
  • 100..999

结果将是 h(999) = 297。

使用 k = floor(log10(n+1)), k2 = k - 1, n2 = (10^k2) - 1 = (n-9)/10 函数 h 是

h(n) = 9 [k2 + h(n2)] + h(n2) + n2 = 9 k2 + 10 h(n2) + n2

初始条件 h(0) = 0。它允许将 g 表示为

g(n) = 9 [k2 + h(n2)] + g(n2)

初始条件 g(0) = 0。

从这两个定义中,我们也可以定义 h 和 g 之间的差异 d,同样是一个递归函数:

d(n) = h(n) - g(n) = h(n2) - g(n2) + n2 = d(n2) + n2

初始条件 d(0) = 0。尝试一些示例会导致几何级数,例如d(9999) = d(999) + 999 = d(99) + 99 + 999 = d(9) + 9 + 99 + 999 = 0 + 9 + 99 + 999 = (10^0)-1 + (10 ^1)-1 + (10^2)-1 + (10^3)​​-1 = (10^4 - 1)/(10-1) - 4。这给出了封闭形式

d(n) = n/9 - k

这允许我们仅用 g 来表达 g:

g(n) = 9 [k2 + h(n2)] + g(n2) = 9 [k2 + g(n2) + d(n2)] + g(n2) = 9 k2 + 9 d(n2 ) + 10 g(n2) = 9 k2 + n2 - 9 k2 + 10 g(n2) = 10 g(n2) + n2

G的推导

使用上述定义并将表示的 k 位命名为 q_k, q_k2, .., q2, q1 我们首先将 h 扩展为 H:

H(q_k q_k2..q_1) = q_k [k2 + h(n2)] + r (k2-kr) + H(q_kr..q_1) + n2

对于 q_1

注意附加定义 r = q_kr..q_1。要了解为什么需要它,请查看示例 H(901),其中对 H 的下一级调用是 H(1),这意味着数字字符串长度从 k=3 缩小到 kr=1,需要额外的填充r (k2-kr) 零位。

使用它,我们也可以将 g 扩展到 G:

G(q_k q_k2..q_1) = (q_k-1) [k2 + h(n2)] + k2 + r (k2-kr) + H(q_kr..q_1) + g(n2)

对于 q_1

注意:很可能可以像上面 g 的情况一样简化上述表达式。例如。试图仅用 G 来表达 G 而不是使用 h 和 H。我将来可能会这样做。以上已经足够实现快速归零计算了。

测试结果

recursive(1234567890, 2345678901) =
  987654304
expected:
  987654304
success

详情请参阅sourcelog

更新:我根据该竞赛中更详细的问题描述更改了源和日志(允许 0 作为输入,处理无效输入,第二个更大的示例)。

【讨论】:

    【解决方案2】:

    您可以使用标准方法找到 m = [1, M-1]n = [1, N],然后 [M, N] = n - m.

    标准方法很容易获得:Counting zeroes

    【讨论】:

    • 嘿我没看清楚这个程序..你能解释一下吗? @一民荣
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-06-12
    • 1970-01-01
    • 2016-05-05
    • 2022-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多