简体   繁体   English

查找整个序列的数字总和的有效方法

[英]Efficient way to find sum of digits of an entire sequence

In the following code snippet I am finding the sum of digits of all odd number between the interval [a,b] 在下面的代码片段中,我找到了区间[a,b]之间所有奇数的位数之和

def SumOfDigits(a, b):
    s = 0
    if a%2 == 0:
        a+=1
    if b%2 == 0:
        b-=1   
    for k in range(a,b+1,2):
        s+= sum(int(i) for i in list(str(k)))
    return s

Is there an efficient way to accomplish the same? 有没有一种有效的方法来实现同样的目标? Any patterns, which is leading to a clear cut formula. 任何图案,这导致明确的公式。

I did search in https://oeis.org 我在https://oeis.org上搜索过

Avoid all the overhead of conversion to and from strings and work directly with the numbers themselves: 避免转换到字符串和从字符串转换的所有开销,并直接使用数字本身:

def SumOfDigits(a, b):
    result = 0
    for i in range(a + (not a % 2), b + 1, 2):
        while i:
            result += i % 10
            i //= 10
    return result

Of course there is a pattern. 当然有一种模式。 Let's look at the problem of summing up the digitsums of all – odd and even – numbers between a and b inclusively. 让我们来看看总结ab之间所有奇数和偶数的数字的问题。

For example: 17 to 33 例如:17到33

17  18  19    20  21  22  23  24  25  26  27  28  29    30  31  32  33

The middle section gives you the sum of all digits from 0 to 9 (45) plus ten times 2. The left section is 7 + 8 + 9 plus three times 1 and the right the sum of 0 + 1 + 2 + 3 plus four times 3. 中间部分为您提供从0到9(45)加10次的所有数字的总和2.左侧部分是7 + 8 + 9加3次1,右侧是0 + 1 + 2 + 3加4的总和次3。

The middle section can comprise several blocks of ten, for example if you calculate the range between 17 and 63, you get 40 times 45 plus ten simes the the digitsums from 2 to 5. 中间部分可以包含几个十个块,例如,如果你计算17到63之间的范围,你可以得到40倍45加10个simes,其中digitums从2到5。

This gives you a recursive algorithm: 这给你一个递归算法:

def ssum(n):
    return n * (n + 1) // 2

def dsum(a, b):
    res = 0

    if a == b:
        while a:
            res += a % 10
            a //= 10

    elif a < b:
        aa = a // 10
        bb = b // 10

        ra = a % 10
        rb = b % 10

        if aa == bb:
            res += ssum(rb) - ssum(ra - 1)
            res += (rb - ra + 1) * dsum(aa, bb)

        else:
            if ra > 0:
                res += 45 - ssum(ra - 1)
                res += (10 - ra) * dsum(aa, aa)
                aa += 1

            if rb < 9:
                res += ssum(rb)
                res += (rb + 1) * dsum(bb, bb)
                bb -= 1

            if aa <= bb:
                res += 45 * (bb - aa + 1)
                res += 10 * dsum(aa, bb) 

    return res

Now let's extend this to include only odd numbers. 现在让我们将其扩展为仅包含奇数。 Adkust a so that it is even and b so that it is odd. Adkust a ,它是偶数和b所以它是奇怪的。 Your sum of digit sums now runs over pairs of even and odd numbers, where even + 1 == odd . 你的数字和的总和现在在偶数和奇数对上运行,其中even + 1 == odd That means that the digitsum of the odd number id one more than the even number, because all except the last digits are the same and the last odd digit is one more than the even digit. 这意味着奇数的数字id比偶数更多,因为除了最后的数字之外的所有数字都是相同的,并且最后的奇数数字比偶数数字多一个。

Therefore: 因此:

dsum(a, b) == oddsum + evensum

and: 和:

oddsum - evensum == (b - a + 1) // 2

The function to sum the digitsums of all odd numbers is then: 然后,将所有奇数的位数相加的函数为:

def oddsum(a, b):
    if a % 2: a -= 1
    if b % 2 == 0: b -= 1

    d = (b - a + 1) // 2

    return (dsum(a, b) + d) // 2

When I looked at your comment about OEIS, I've noticed that the algorithm can probably be simplified by writing a function to sum the digits from all numbers from zero to n and then calculate the difference dsum(b) - dsum(a) . 当我查看你关于OEIS的评论时,我注意到可以通过编写一个函数来简化算法,将所有数字中的数字从0加到n ,然后计算差值dsum(b) - dsum(a) There are probably more opportunities for optimisation. 可能有更多的优化机会。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM