【问题标题】:Speeding up algorithm that finds multiples in a given range加速在给定范围内查找倍数的算法
【发布时间】:2015-05-05 22:39:55
【问题描述】:

我不知道如何加快对给定范围内的倍数求和的算法。这是针对 codewars.com 上的问题 这里是问题的链接 codewars link

这是代码,我将在底部解释发生了什么

import itertools

def solution(number):
        return multiples(3, number) +  multiples(5, number) - multiples(15, number)

def multiples(m, count):

    l = 0
    for i in itertools.count(m, m):
        if i < count:
            l += i
        else:
            break
    return l

print solution(50000000) #takes 41.8 seconds
#one of the testers takes 50000000000000000000000000000000000000000 as input

# def multiples(m, count):
#     l = 0
#     for i in xrange(m,count ,m):
#         l += i
#     return l

所以基本上问题要求用户返回一个数字中所有 3 和 5 的倍数的总和。这是测试人员。

test.assert_equals(solution(10), 23)
test.assert_equals(solution(20), 78)
test.assert_equals(solution(100), 2318)
test.assert_equals(solution(200), 9168)
test.assert_equals(solution(1000), 233168)
test.assert_equals(solution(10000), 23331668)

我的程序得到正确答案没有问题。当输入很大时就会出现问题。当传入一个像 50000000 这样的数字时,需要 40 多秒才能返回答案。我被要求接受的输入之一是 500000000000000000000000000000000000000000,这是一个巨大的数字。这也是我使用itertools.count() 的原因我在第一次尝试中尝试使用xrange 但范围无法处理大于c 类型long 的数字。我知道问题最慢的部分是倍数方法......但它仍然比我第一次尝试使用列表理解并检查i % 3 == 0 or i % 5 == 0,有什么想法吗?

【问题讨论】:

  • 真正的优化将是一种不同的方法。你不需要遍历范围内的所有数字,这个问题有一个封闭形式的解决方案
  • 我在 google 上搜索了封闭形式,得到的定义是,“如果一个方程根据给定的函数和数学运算来解决给定的问题,那么它就是一个封闭形式的解——可接受的集合。例如,无限和通常不会被视为封闭形式。读完之后,我仍然对封闭形式的含义感到困惑。可以请教一下吗?
  • 通过接近形式我的意思是你可以想出一个简单的算术函数,给定一个数字 n 和一个范围 r 会给你在范围 [0. .r]
  • 我知道如何计算给定范围或数字内的倍数,我以前试过。我可以通过使用 ⌊b/n⌋ - ⌈a/n⌉ + 1 来计算一个数字中 3 和 5 的倍数,我在这里读到了 had2know.com/academics/…,我不知道如何应用它虽然因为问题是要求给定范围内所有倍数的总和,例如3、6、9 = 18,而 9 中有 3 个 3 的倍数。
  • 你会很高兴听到还有一个用于计算等差数列之和的封闭式公式 :) en.wikipedia.org/wiki/Arithmetic_progression#Sum

标签: python algorithm


【解决方案1】:

这个解决方案对于大数字应该更快。

def solution(number):
    number -= 1
    a, b, c = number // 3, number // 5, number // 15
    asum, bsum, csum = a*(a+1) // 2, b*(b+1) // 2, c*(c+1) // 2
    return 3*asum + 5*bsum - 15*csum

说明:

取从 1 到 n 的任意序列:

1, 2, 3, 4, ..., n

它的总和总是由公式n(n+1)/2 给出。如果您认为表达式 (1 + n) / 2 只是计算该特定数字序列的平均值或 Arithmetic mean 的快捷方式,则可以轻松证明这一点。因为average(S) = sum(S) / length(S),如果你取任意一个数列的平均值,再乘以数列的长度,就得到了数列的总和。

如果给定一个数 n,我们想要某个给定 k 到 n 的倍数之和,包括 n,我们想要求和:

k + 2k + 3k + 4k + ... xk

其中 xk 是小于或等于 n 的 k 的最大倍数。现在请注意,这个总和可以分解为:

k(1 + 2 + 3 + 4 + ... + x)

我们已经得到了 k,所以现在我们只需要找到 x。如果 x 被定义为你可以将 k 乘以得到小于或等于 n 的自然数的最大数,那么我们可以使用 Python 的整数除法得到数 x:

n // k == x

一旦我们找到 x,我们可以使用前面的公式找到任何给定 k 到给定 n 的倍数之和:

k(x(x+1)/2)

我们给定的三个 k 是 3、5 和 15。

我们在这一行找到我们的 x:

a, b, c = number // 3, number // 5, number // 15

在这一行中计算它们的倍数之和,最多为 n:

asum, bsum, csum = a*(a+1) // 2, b*(b+1) // 2, c*(c+1) // 2

最后,在这一行中将它们的总和乘以 k:

return 3*asum + 5*bsum - 15*csum

我们有我们的答案!

【讨论】:

  • 解决方案奏效了!能否请您解释一下它是如何工作的以及为什么效果如此之好?
  • @msanti 相关:Simple sum calculator
  • 谢谢大家!我现在看到了,哈哈,感谢 Shashanks 的精彩解释,并感谢 @yurib 和 Ashwini 的链接。
  • @msanti 好问题!让我知道是否需要更多解释。 :)
猜你喜欢
  • 1970-01-01
  • 2019-06-25
  • 2020-10-03
  • 2019-12-31
  • 2014-03-29
  • 2022-01-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多