我正在做一个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]
的东西,但当然不必在字符串和整数之间重新转回整数。我知道那里有数学关系,但我很难解读。
任何帮助都将不胜感激。
答案 0 :(得分:6)
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)
:1
和n
之间所有数字的总和,包括(答案)sum(d)
:1
和d
之间所有数字的总和(包括d=1..9, sum(d) = 0, 1, 3, 6, 10, 15, 21, 28, 36, 45
)。g(i)
:数字i
的数字总和。f(i,j)
:i
和j-1
之间所有整数的总和。g(m)
:数字m
的数字总和。h(d,p)
:0
和d*(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
前两位数字3
和40
出现在(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
,这是299
和0
之间所有数字的所有数字的总和。
首先考虑第一个数字的位数总和。 1
,2
和0-299
都是0*100 + 1*100 + 2*100 = sum(2) * 10^2
范围内100个数字的第一个数字。因此,第一个数字,总和,贡献
2
总数。我们现在为剩余的300
数字添加数字总和。 2
个数字在最后两个位置各有0-9
个数字。每个数字1/10th
都显示在2 * 300 = 600
个60
个数字中;即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}}