是否有一个Ruby方法来获取整数的/十分之一/百分位?

时间:2014-06-13 15:03:52

标签: ruby

我正在做一个Ruby kata,要求我找到从1到N(包括两端)的所有数字的数字总和。

所以,如果我有这些输入,我会得到这些输出:

For N = 10 the sum is 1+2+3+4+5+6+7+8+9+(1+0) = 46

For N = 11 the sum is 1+2+3+4+5+6+7+8+9+(1+0)+(1+1) = 48

For N = 12 the sum is 1+2+3+4+5+6+7+8+9+(1+0)+(1+1) +(1+2)= 51

现在我知道需要做些什么。以下是我必须解决此问题的代码:

def solution(n)
  if n <= 9
    return n if n == 1
    solution(n-1) + n
  elsif n >= 10
    45 + (10..n) #How can I grab the ones,tenths, and hundreds?
  end
end

基本上一切都很好,直到我达到10岁。

我试图找到某种可以做到这一点的方法。我搜索了Fixnum和Integer,但我找不到任何可以帮助我的东西。我想要找到类似"string"[0]的东西,但当然不必在字符串和整数之间重新转回整数。我知道那里有数学关系,但我很难解读。

任何帮助都将不胜感激。

3 个答案:

答案 0 :(得分:6)

您可以使用modulo和整数division递归计算它:

def sum_digits(n)
  return n if n < 10
  (n % 10) + sum_digits(n / 10)
end

sum_digits(123)
# => 6

答案 1 :(得分:5)

初学者可能会这样做:

123.to_s.chars.map(&:to_i)
# => [1, 2, 3]

但是更有思想的人会这样做:

n, a = 123, []
until n.zero?
  n, r = n.divmod(10)
  a.unshift(r)
end
a
# => [1, 2, 3]

答案 2 :(得分:2)

我不是计算范围中每个数字的数字之和,而是将这些小计相加,而是使用组合方法计算总数。因此,它比直接枚举更有效。

<强>代码

SUM_ONES = 10.times.with_object([]) { |i,a| a << i*(i+1)/2 }
S = SUM_ONES[9]

def sum_digits_nbrs_up_to(n)
  pwr = n.to_s.size - 1
  tot = n.to_s.chars.map(&:to_i).reduce(:+)
  sum_leading_digits = 0
  pwr.downto(0).each do |p|
    pwr_term = 10**p
    leading_digit = n/pwr_term
    range_size = leading_digit * pwr_term
    tot += sum_leading_digits * range_size +
           sum_digits_to_pwr(leading_digit, p)
    sum_leading_digits += leading_digit
    n -= range_size
  end
  tot
end  

def sum_digits_to_pwr(d, p)
  case
  when d.zero? && p.zero?
    0
  when d.zero?
    10**(p-1) * S * d * p
  when p.zero?
    10**p * SUM_ONES[d-1]
  else
    10**p * SUM_ONES[d-1] + 10**(p-1) * S * d * p
  end
end

<强>实施例

sum_digits_nbrs_up_to(456)   #=>   4809
sum_digits_nbrs_up_to(2345)  #=>  32109
sum_digits_nbrs_up_to(43021) #=> 835759
sum_digits_nbrs_up_to(65827359463206357924639357824065821)
                             #=> 10243650329265398180347270847360769369

这些计算基本上都是即时的。我通过直接枚举验证了前三个示例的总计,使用@sawa的方法计算范围内每个数字的位数。

<强>解释

最好用一个例子来解释算法。假设n等于2345

我们首先定义以下功能:

  • t(n)1n之间所有数字的总和,包括(答案)
  • sum(d)1d之间所有数字的总和(包括d=1..9, sum(d) = 0, 1, 3, 6, 10, 15, 21, 28, 36, 45)。
  • g(i):数字i的数字总和。
  • f(i,j)ij-1之间所有整数的总和。
  • g(m):数字m的数字总和。
  • h(d,p)0d*(10^p)-1之间所有数字的总和(从下面导出)。

然后(我在下面解释):

t(2345) = f(0-1999)+f(2000-2299)+f(2300-2339)+f(2340-2344)+g(2345)

f(   0-1999) =                           h(2,3) =       h(2,3)
f(2000-2299) = 2       * (2299-2000+1) + h(3,2) = 600 + h(3,2)
f(2300-2339) = (2+3)   * (2339-2300+1) + h(4,1) = 200 + h(4,1)
f(2340-2344) = (2+3+4) * (2344-2340+1) + h(5,0) =  45 + h(5,0)
g(2345)      = 2+3+4+5                          =  14

所以

t(2345) = 859 + h(2,3) + h(3,2) + h(4,1) + h(5,0)

首先考虑f(2000-2299)。第一个数字2出现在(2000..2299)范围内的每个数字中;即,300次。其余三位数(根据定义)h(3,2)为总数:

f(2000-2299) = 2 * 300 + h(3,2)

f(2300-2339) 2前两位数字340出现在(2300..2339)范围内的所有h(4,1)个数字中,其余两位数字有效总共f(2300-2339) = 5 * 40 + h(4,1)

f(2340-2344)

对于,,前三位数字为“2 and 3 , are present in all four number in the range ``(2340-2344) 4 h(5,0),最后一位数为h(d,p)总数。

仍然需要派生一个用于计算h(3,2)的表达式。同样,最好用一个例子来解释。

考虑0,这是2990之间所有数字的所有数字的总和。

首先考虑第一个数字的位数总和。 120-299都是0*100 + 1*100 + 2*100 = sum(2) * 10^2 范围内100个数字的第一个数字。因此,第一个数字,总和,贡献

2

总数。我们现在为剩余的300数字添加数字总和。 2个数字在最后两个位置各有0-9个数字。每个数字1/10th都显示在2 * 300 = 60060个数字中;即2次。因此,所有300个数字中最后sum(9) * 2 * 300 / 10 = 45 * 2 * 30 = 2700. 个数字位置的所有数字之和等于:

h(d,p) = sum(d-1) * 10**p + sum(9) * d * p * 10**(p-1) if d >  0 and p >  0
       = sum(d-1) * 10**p                              if d >  0 and p == 0
       =                    sum(9) * d * p * 10**(p-1) if d == 0 and p >  0
       = 0                                             if d == 0 and p == 0

更一般地说,

h(2,3) = sum(1) * 10**3 + (45 * 2 * 3) * 10**2 =  1 * 1000 + 270 * 100 = 28000
h(3,2) = sum(2) * 10**2 + (45 * 3 * 2) * 10**1 =  3 *  100 + 270 *  10 =  3000
h(4,1) = sum(3) * 10**1 + (45 * 4 * 1) * 10**0 =  6 *   10 + 180 *   1 =   240
h(5,0) = sum(4) * 10**0                        = 10 *    1             =    10

将此应用于上述示例,我们有

t(2345) = 859 + 28000 + 3000 + 240 + 10 = 32109

因此

def sum_digits(n)
  a = []
  until n.zero?
    n, r = n.divmod(10)
    a.unshift(r)
  end
  a.reduce(:+)
end

def check_sum_digits_nbrs_up_to(n)
  (1..n).reduce(0) {|t,i| t + sum_digits(i) }
end

check_sum_digits_nbrs_up_to(2345) #=> 32109

上面的代码以直接的方式实现了这个算法。

我通过使用@sawa的代码确定范围中每个数字的数字总和,然后将这些数字相加来确认上述前三个例子的结果:

{{1}}