将一个数字的各个位数相加

107

如果我想找到一个数的数字之和,例如:

  • 输入:932
  • 输出:14,即 (9 + 3 + 2)

怎样最快地实现这个功能呢?

我的本能反应是:

sum(int(digit) for digit in str(number))

我在网上找到了这个:

sum(map(int, str(number)))

哪种方法最适合用于速度,还有其他更快的方法吗?

11个回答

135

你发布的两行代码都没问题,但你可以使用纯整数计算,这将是最有效的:

def sum_digits(n):
    s = 0
    while n:
        s += n % 10
        n //= 10
    return s

或使用 divmod 函数:

def sum_digits2(n):
    s = 0
    while n:
        n, remainder = divmod(n, 10)
        s += remainder
    return s

稍微快一点的方法是使用单个赋值语句:

def sum_digits3(n):
   r = 0
   while n:
       r, n = r + n % 10, n // 10
   return r

> %timeit sum_digits(n)
1000000 loops, best of 3: 574 ns per loop

> %timeit sum_digits2(n)
1000000 loops, best of 3: 716 ns per loop

> %timeit sum_digits3(n)
1000000 loops, best of 3: 479 ns per loop

> %timeit sum(map(int, str(n)))
1000000 loops, best of 3: 1.42 us per loop

> %timeit sum([int(digit) for digit in str(n)])
100000 loops, best of 3: 1.52 us per loop

> %timeit sum(int(digit) for digit in str(n))
100000 loops, best of 3: 2.04 us per loop

3
是的,在期望布尔值的地方,有些东西等同于False。请参见此处:https://docs.python.org/2/library/stdtypes.html#truth-value-testing - Pavel Anossov
1
有没有一种公式可以找到奇数整数序列的数字总和? - Anoop Toffy
6
在您的%timeit调用中,“n”的值是多少? - d8aninja
1
我认为第三段代码更快的原因不是缺少增强赋值,而是使用了多重赋值 - kotchwane
1
我认为你应该提到这仅适用于小整数更有效率。使用较大的整数,字符串转换最终将开始胜出 - 为每个除法创建bigint变得非常昂贵。 - wim
显示剩余6条评论

17

如果你想将数字的每一位相加,直到得到一个个位数(这是我最喜欢的9的倍数的特性之一),那么你可以这样做:

如果你希望保持数字的每一位相加,直到得到个位数(这是我最喜欢9的倍数的特点之一),你可以这样做:

def digital_root(n):
    x = sum(int(digit) for digit in str(n))
    if x < 10:
        return x
    else:
        return digital_root(x)

实际上这本身就相当快...

%timeit digital_root(12312658419614961365)

10000 loops, best of 3: 22.6 µs per loop

@RonaldoNascimento 我不知道,但它让我着迷。还有3秒钟。 - d8aninja
2
对于十进制数字的数字根,存在一个直接的公式:digital_root(n) = n-9*(n-1//9) - reschu
@reschu,更简单地说:n%9 - Toby Speight
2
对于将数字求和直到得到一位数,可以直接使用模9算术:(n - 1) % 9 + 1 - Aivar Paalberg
问题要求输入932时输出14。 - wim
显示剩余2条评论

9

我在一个解决问题挑战网站上发现了这个。不是我的,但它有效。

num = 0            # replace 0 with whatever number you want to sum up
print(sum([int(k) for k in str(num)]))

8
这可能会有所帮助。
def digit_sum(n):
    num_str = str(n)
    sum = 0
    for i in range(0, len(num_str)):
        sum += int(num_str[i])
    return sum

1
谢谢,这个对我解决问题很有帮助:检查给定的数字是否在将其各位数字相加后可以模0。 - Yannis Dran

4

在完成一些Codecademy的挑战时,我解决了这个问题:

def digit_sum(n):
    digits = []
    nstr = str(n)
    for x in nstr:
        digits.append(int(x))
    return sum(digits)

1
无论是使用数学运算还是字符串操作,都取决于输入数字的大小。对于小数字(长度少于20位),应使用除法和模运算:
def sum_digits_math(n):
    r = 0
    while n:
        r, n = r + n % 10, n // 10
    return r

对于长度大于30位的大数,使用字符串域:

def sum_digits_str_fast(n):
    d = str(n)
    return sum(int(s) * d.count(s) for s in "123456789")

在20到30位数字之间,sum(map(int, str(n)))是最快的。这是下面图表中紫色线条所代表的(点击这里放大查看)。

profile

随着输入数字越来越大,使用数学方法的性能会变得越来越差,但每种在字符串域中工作的方法似乎都会按照输入长度线性扩展。生成这些图表所使用的代码在这里,我在macOS上使用的是CPython 3.10.6。


0

这是一个没有任何循环或递归但只适用于非负整数的解决方案(Python3):

def sum_digits(n):
    if n > 0:
        s = (n-1) // 9    
        return n-9*s
    return 0

不起作用。问题要求输入932,输出14。 - wim

0

试试这个

    print(sum(list(map(int,input("Enter your number ")))))

0
一个十进制数可以表示为如下形式的一系列数字:

a × 10^p + b × 10^p-1 .. z × 10^0

因此,一个数的各位数字之和是每个位数系数的总和。
基于这个信息,可以按以下方式计算数字的各位数字之和:
import math

def add_digits(n):
    # Assume n >= 0, else we should take abs(n)
    if 0 <= n < 10:
        return n
    r = 0
    ndigits = int(math.log10(n))
    for p in range(ndigits, -1, -1):
        d, n = divmod(n, 10 ** p)
        r += d
    return r

这实际上是与接受答案中连续除以10的过程相反。考虑到与接受答案相比,此函数中存在额外的计算,因此不难发现此方法的性能较差:它大约慢3.5倍,而且比...慢两倍

sum(int(x) for x in str(n))

-1
为什么最高评价的答案比这个慢3.70倍
% echo; ( time (nice echo 33785139853861968123689586196851968365819658395186596815968159826259681256852169852986 \
 | mawk2 'gsub(//,($_)($_)($_))+gsub(//,($_))+1' | pvE0 \
 | mawk2 '
   
   function __(_,___,____,_____) {

                  ____=gsub("[^1-9]+","",_)~""
                ___=10
       while((+____<--___) && _) {
            _____+=___*gsub(___,"",_) 
       }
       return _____+length(_) } 

    BEGIN { FS=OFS=ORS
                RS="^$" 
    } END { 
            print __($!_) }' )| pvE9 ) | gcat -n | lgp3 ;

      in0:  173MiB 0:00:00 [1.69GiB/s] [1.69GiB/s] [<=>                                            ]
     out9: 11.0 B 0:00:09 [1.15 B/s] [1.15 B/s] [<=>                                               ]
      in0:  484MiB 0:00:00 [2.29GiB/s] [2.29GiB/s] [  <=>                                          ]
( nice echo  | mawk2 'gsub(//,($_)($_)($_))+gsub(//,($_))+1' | pvE 0.1 in0 | )  

 8.52s user 1.10s system 100% cpu 9.576 total
 1  2822068024



% echo; ( time ( nice echo 33785139853861968123689586196851968365819658395186596815968159826259681256852169852986 \
     \
     | mawk2 'gsub(//,($_)($_)($_))+gsub(//,($_))+1' | pvE0 \
     |  gtr -d '\n' \
     \
     |  python3 -c 'import math, os, sys;

        [ print(sum(int(digit) for digit in str(ln)), \
                                            end="\n") \
         \
         for ln in sys.stdin ]' )| pvE9 ) | gcat -n | lgp3 ;


      in0:  484MiB 0:00:00 [ 958MiB/s] [ 958MiB/s] [     <=>                                       ]
     out9: 11.0 B 0:00:35 [ 317miB/s] [ 317miB/s] [<=>                                             ]
( nice echo  | mawk2 'gsub(//,($_)($_)($_))+gsub(//,($_))+1' | pvE 0.1 in0 | )  

 35.22s user 0.62s system 101% cpu 35.447 total
     1  2822068024

这已经有点慷慨了。在这个2.82 GB的大型合成测试用例中,它要慢19.2倍

 % echo; ( time ( pvE0 < testcases_more108.txt  |  mawk2 'function __(_,___,____,_____) { ____=gsub("[^1-9]+","",_)~"";___=10; while((+____<--___) && _) { _____+=___*gsub(___,"",_) }; return _____+length(_) } BEGIN { FS=RS="^$"; CONVFMT=OFMT="%.20g" } END { print __($_) }'  ) | pvE9 ) |gcat -n | ggXy3  | lgp3; 

      in0:  284MiB 0:00:00 [2.77GiB/s] [2.77GiB/s] [=>                             ]  9% ETA 0:00:00
     out9: 11.0 B 0:00:11 [1016miB/s] [1016miB/s] [<=>                                             ]
      in0: 2.82GiB 0:00:00 [2.93GiB/s] [2.93GiB/s] [=============================>] 100%            
( pvE 0.1 in0 < testcases_more108.txt | mawk2 ; )

  8.75s user 2.36s system 100% cpu 11.100 total
     1  3031397722

% echo; ( time ( pvE0 < testcases_more108.txt  | gtr -d '\n' |  python3 -c 'import sys; [ print(sum(int(_) for _ in str(__))) for __ in sys.stdin ]' ) | pvE9 ) |gcat -n | ggXy3  | lgp3;  


      in0: 2.82GiB 0:00:02 [1.03GiB/s] [1.03GiB/s] [=============================>] 100%            
     out9: 11.0 B 0:03:32 [53.0miB/s] [53.0miB/s] [<=>                                             ]
( pvE 0.1 in0 < testcases_more108.txt | gtr -d '\n' | python3 -c ; )  

  211.47s user 3.02s system 100% cpu 3:32.69 total
     1  3031397722

—————————————————————

更新:该概念的本地Python3代码 - 即使是在我可怕的Python技能下,我也看到了4倍的加速:

% echo; ( time ( pvE0 < testcases_more108.txt  \
\
 |python3 -c 'import re, sys;

  print(sum([ sum(int(_)*re.subn(_,"",__)[1] 

     for _ in [r"1",r"2", r"3",r"4",
               r"5",r"6",r"7",r"8",r"9"])

 for __ in sys.stdin ]))' |pvE9))|gcat -n| ggXy3|lgp3   

      in0: 1.88MiB 0:00:00 [18.4MiB/s] [18.4MiB/s] [>                              ]  0% ETA 0:00:00
     out9: 0.00 B 0:00:51 [0.00 B/s] [0.00 B/s] [<=>                                               ]
      in0: 2.82GiB 0:00:51 [56.6MiB/s] [56.6MiB/s] [=============================>] 100%            
     out9: 11.0 B 0:00:51 [ 219miB/s] [ 219miB/s] [<=>                                             ]

( pvE 0.1 in0 < testcases_more108.txt | python3 -c  | pvE 0.1 out9; ) 



48.07s user 3.57s system 100% cpu 51.278 total
 1  3031397722

即使是较小的测试用例也实现了1.42倍的加速:

 echo; ( time (nice echo 33785139853861968123689586196851968365819658395186596815968159826259681256852169852986 \ 
 | mawk2 'gsub(//,($_)($_)$_)+gsub(//,$_)+1' ORS='' | pvE0 | python3 -c 'import re, sys; print(sum([ sum(int(_)*re.subn(_,"",__)[1] for _ in [r"1",r"2", r"3",r"4",r"5",r"6",r"7",r"8",r"9"]) for __ in sys.stdin ]))'  | pvE9  ))  |gcat -n | ggXy3 | lgp3 


      in0:  484MiB 0:00:00 [2.02GiB/s] [2.02GiB/s] [  <=>                                          ]
     out9: 11.0 B 0:00:24 [ 451miB/s] [ 451miB/s] [<=>                                             ]
( nice echo  | mawk2 'gsub(//,($_)($_)$_)+gsub(//,$_)+1' ORS='' | pvE 0.1 in0)

 20.04s user 5.10s system 100% cpu 24.988 total
   1    2822068024

1
因为你正在比较一种专门设计用于文本处理的领域特定语言和一种高级通用编程语言。这是无法相提并论的。 - wim
我已经更新了一个Python版本 - 请原谅我的可怕的Python代码 - 我无法让RE引擎循环整数,所以我不得不手动编写一个由9个整数组成的数组。 - RARE Kpop Manifesto
它之所以快,并不是因为我是某种Python大师,而是因为这是其中一种情况,当不需要进行算术运算时,“做算术运算”会有害。可以通过r"[Aa]" r"[Bb]"等扩展该列表,并直接总结十六进制数字。您还可以通过循环字节来推广该概念-它们的出现次数乘以字节序值,并且可以在二进制文件中获得字节值的“总和”(用例是什么,我不知道,但很容易推广)。 - RARE Kpop Manifesto
哦,是的,我们的输入量根本不在同一数量级上。字符串解决方案只有在非常大的输入情况下才具有优势 - 您的要点似乎正在测试从1到120位数字的所有长度。我的这两个测试用例分别为较小的测试用例约为5.075亿位数字(单个整数),较大的测试用例为30.3亿位数字。我们甚至没有在同一页上 - 难怪我们一直在互相说过去。 - RARE Kpop Manifesto
1
这段代码很难理解为Python答案,因为它包含了大量用于计时的外部shell代码。Python标准库中内置了计时功能:timeit模块允许以编程方式计时代码,也可以在命令行上计时短代码片段。此外,请注意您假设输入为字符串,而不是Python(任意大小)整数。 - Karl Knechtel
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接