【问题标题】:Why is one function of divisor >3000% faster than the other?为什么除数的一个函数比另一个函数快 > 3000%?
【发布时间】:2022-01-09 10:14:30
【问题描述】:

我在https://www.practicepython.org/ 上做一些简单的Python 练习时,我在https://www.practicepython.org/exercise/2014/02/26/04-divisors.html 上做这个任务。

我想测试不同的方法来编写答案,我在评论字段中看到了引起我兴趣的解决方案;两个代码看起来相似,但一个比另一个快 4000%!

代码:

import time

start = time.time()

print("Fast code:")

#the slow code
def divisor(x):
    divisors = []
    for i in range(1, int(x)):
        if x % i == 0:
            divisors.append(i)
            divisors.sort()
    print(divisors)


divisor(50000000)
end = time.time()
print("Time elapsed: ")
print(end - start)

print("Fast code:")

start = time.time()

#the fast code!
def divisor2(x):
    divisors = []
    for i in range(1, int(x**.5)):
        if x % i == 0:
            divisors.append(i)
            divisors.append(x//i)
            divisors.sort()
    print(divisors)
divisor2(50000000)
end = time.time()
print("Time elapsed: ")
print(end - start)

经过的时间(慢代码):

3.1739981174468994

经过的时间(快速代码):

0.0010004043579101562

谁能告诉我这是怎么可能的,所以也许任何人都可以为他们的工具带带回家一些宝贵的更快的编码技能?

【问题讨论】:

  • 你明白xsquare root of x的区别吗?
  • 这两个版本也比必要的慢,因为它们在每个循环迭代中都排序。如果它们只在最后排序会更好。
  • 对于初学者来说,一个是迭代直到x,另一个是迭代直到int(x**0.5)...对于像50000000这样的数字,所以第一个迭代是50000000 ,而第二个是 7071 迭代...
  • 这两个函数也被“破坏”了——它们都应该将范围末尾增加 1 并使用集合而不是列表。尝试使用16,您将分别得到[1, 2, 4, 8][1, 2, 8, 16]。通过我建议的修复,它们都产生[1, 2, 4, 8, 16](在对集合进行一次排序之后)。
  • @juanpa.arrivillaga 是的,用于除数,否则4 将出现两次16

标签: python function performance time


【解决方案1】:

x**.5 是 x 的二分之一次方,是平方根。

都使用x = 50,000,000,第一个使用:

for i in range(1, int(x)):

五千万次循环

第二个:

for i in range(1, int(x**.5)):

七千次循环

在计算除​​数时可以这样做是因为它们形成了一个矩形的两侧:

 __
|  |
|  |
|__|

如果您知道区域(目标,5000 万),并且您知道一侧(柜台i),那么您可以计算另一侧。两边相同的地方是平方根,它就像一个枢轴,如果一个比那个短,那么另一个必须更长,并平衡它。也就是说,100/2 = 50 给你 2 和 50。

因此,您可以数到平方根以找到所有简短答案,然后计算出每个答案的另一面(平衡),然后您就有了所有答案。 100/10 = 10 给你 10 作为除数,并告诉你,如果你一直数到 100/50 = 2,你已经从另一边看到了,得到了 50。

这就是为什么第二个代码有:

        divisors.append(i)
        divisors.append(x//i)

在只测试一个之后,将两个值添加到列表中。

【讨论】:

  • 很好很简单的解释。我很欣赏这个。谢谢你,TessellatingHeckler!
【解决方案2】:

经典 O(n) 与 O(n^0.5)

第一个是做 50000000 次操作

第二个是执行 7000 次操作(少 7000 倍)

那么时差呢?快3000倍?有些东西没有加起来。

这里!

divisors.sort()

您在找到每个除数对后执行此操作,这是一项昂贵的操作。只需将其向左移动 2 个缩进级别(与 print 相同的级别),即可看到大约 7000 倍或更高的性能。

为什么会这样?

好吧,如果您发现 15 可以被 3 整除,那么为什么要检查每隔一个数字然后再检查 5,因为您知道如果 15 可以被 3 整除,那么它也可以被 15/3 整除。此外,任何数字都不能被任何大于其平方根的数字整除,其中结果也大于平方根。因此,长时间运行循环是没有意义的——它不会找到解决方案。由于 50000000 绝对不能被任何大于 7071.067811865475 的数字整除,这将导致另一个大于 7071.067811865475 的数字,如果我们将找到的每个除数的除法结果相加,检查 7072 和更大的数字会浪费大量时间。

【讨论】:

  • 我认为这种措辞可能会令人困惑。肯定有比平方根大的数字除以输入。问题是我们已经找到了它们,因为我们将ix // i 添加到列表中,因此无需检查平方根。
  • 是的 - 没有数字等于两个大于平方根的数字的乘积。但是 50000000 可以被 25000000 >> 7000 整除。
  • 我确实尝试了 .sort() 方法的 2 向左缩进(带打印)。这对我的 PyCharm 的处理时间没有任何影响。即使我在更高的除数上测试也不行。我尝试了 5000000000000 没有任何变化。原文:divisors.sort() print(divisors) Time elapsed: 0.16904497146606445 2 indent to left: divisors.sort() print(divisors) Time elapsed: 0.17499947547912598 我试了几次,他们似乎都准时了。也许是一些疯狂的高数字,但我看不到改变 .sort() 位置的价值。我错过了什么吗?
  • 确实如此。我高估了它将执行的排序数量(它拥有的除数数量)。为什么这个比例不是 1:7000 而只是 1:3000 是一个有趣的好奇心。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-19
  • 2012-12-29
  • 2014-03-06
  • 2015-07-10
  • 1970-01-01
相关资源
最近更新 更多