鱼C论坛

 找回密码
 立即注册
查看: 2594|回复: 6

使用循环体来计算、直接调用多个运算函数来计算,哪种方式的效率高?

[复制链接]
发表于 2017-2-9 10:14:48 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x

  1. #自己编写的009讲中关于水仙花数的计算代码
  2. flag = 0
  3. print("计算出来的水仙花数为:")
  4. for i in range(100,1000) :
  5.     if i == ((i//100)**3+((i//10)%10)**3+(i%10)**3) :    #直接调用运算函数来计算,缺点是每循环一次,都要进行这么长的运算
  6.         flag = 1
  7.         print(i)
  8. if flag == 0 :
  9.     print("在此范围内未找到水仙花数")

复制代码


对比小甲鱼给出的参考答案,不同的地方即在上面给出注释了的那一行代码:我是采用一行长运算(包含多个运算函数)来完成的;小甲鱼是采用一个while循环(每次循环所调用的运算函数要少一些)来计算的。故此请问大家,哪种方式的运行效率要好一些?非常感谢!
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2017-2-9 11:21:29 | 显示全部楼层
在我的理解来说的话,如果你的运算函数更少的话,那么占用的资源也会更少,自然效率就会更高一点。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2017-2-9 15:31:02 | 显示全部楼层
其实我们可以采用python的timeit对2种代码进行简单的评估
我们首先写2个函数,分别实现2种方案
  1. In [1]: def foo1(a,b):
  2.     ...:     for n in range(a,b):
  3.     ...:         if n == (n//100)**3+((n//10)%10)**3+(n%10)**3 :
  4.     ...:             print(n)
  5.     ...:            

  6. In [2]: def foo2(a,b):
  7.    ...:     for n in range(a,b):
  8.    ...:         sum1 = 0
  9.    ...:         tmp = n
  10.    ...:         while tmp:
  11.    ...:             sum1 += (tmp%10)**3
  12.    ...:             tmp //= 10
  13.    ...:         if sum1 == n:
  14.    ...:             print(n)
  15.    ...:            

  16. In [3]: foo1(100,1000)
  17. 153
  18. 370
  19. 371
  20. 407

  21. In [4]: foo2(100,1000)
  22. 153
  23. 370
  24. 371
  25. 407
复制代码

好的,函数准备好了,接下来我们调用timeit看一下需要多少时间
  1. In [5]: %timeit foo1(100,1000)

  2. 1000 loops, best of 3: 1.97 ms per loop

  3. In [6]: %timeit foo2(100,1000)

  4. 1000 loops, best of 3: 2.2 ms per loop
复制代码

单次测试1000个循环,共3次测试,foo1()大约比foo2()快了那么0.3ms,那么foo1()是否比foo2()好呢?

在这个简单的问题中,答案是肯定的。因为根据算法的时间复杂度来说,foo1()的时间复杂度为O(n),而foo2()的时间复杂度为O(kn),k为位数,此处k=3。理论上我们不会特别关心k的大小,将O(kn)视为O(n),而当k接近n大小时(假设我们要查找10^100 ~ 10^100+1000内水仙花数!),问题的复杂度会上升为O(n^2)。

当然,这时候不可能采用foo1()这样的写法,手工将各个位数拆开来。因此,只能说在这个特定的问题中,foo1()还是比较取巧的。

编程中有句话叫:Keep your code dry! 意思就是你的代码要简练!不要去做那些吃力重复的劳动,foo1()虽然看起来行数较少,但事实上表达式难以让人一下子理解,并且有重复劳动之嫌,试想一下如果要求1~1,000,000内的水仙花数,你还要这么写么??

评分

参与人数 1荣誉 +1 鱼币 +1 贡献 +1 收起 理由
haskye + 1 + 1 + 1

查看全部评分

小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-2-9 16:08:26 | 显示全部楼层
DarkmasterSugar 发表于 2017-2-9 15:31
其实我们可以采用python的timeit对2种代码进行简单的评估
我们首先写2个函数,分别实现2种方案

多谢如此详细的解答,受教了
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-2-10 11:06:55 | 显示全部楼层
DarkmasterSugar 发表于 2017-2-9 15:31
其实我们可以采用python的timeit对2种代码进行简单的评估
我们首先写2个函数,分别实现2种方案

请教一下timeit的使用方法。

按照你的方式编写了foo1和foo2这两个函数。然后在Pathon shell里边输入计时函数,如下:

  1. import timeit
  2. t1 = timeit.Timer("foo1(100,1000)")
  3. t1.timeit()
复制代码


为何会打印如下错误信息:
Traceback (most recent call last):
  File "<pyshell#69>", line 1, in <module>
    t1.timeit()
  File "C:\Users\forpandan\AppData\Local\Programs\Python\Python36\lib\timeit.py", line 178, in timeit
    timing = self.inner(it, self.timer)
  File "<timeit-src>", line 6, in inner
NameError: name 'foo1' is not defined

foo1函数不是已经定义过了的么?非常感谢~~
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2017-2-10 13:14:57 | 显示全部楼层
haskye 发表于 2017-2-10 11:06
请教一下timeit的使用方法。

按照你的方式编写了foo1和foo2这两个函数。然后在Pathon shell里边输入计 ...

你这么好学!!!

timeit只适用于执行小代码片段的时间评测,有两种形式,一种以命令行模式运行(command-line mode),另一种就是调用函数形式(callable mode)
你现在在尝试的属于后一种,这种和命令行模式有比较大的区别,很多参数要自己给定

你可以查阅timeit模块的文档,我截取一部分如下
27.5.3. Command-Line Interface
When called as a program from the command line, the following form is used:
  1. python -m timeit [-n N] [-r N] [-u U] [-s S] [-t] [-c] [-h] [statement ...]
复制代码

The module defines three convenience functions and a public class:

timeit.timeit(stmt='pass', setup='pass', timer=<default timer>, number=1000000, globals=None)
Create a Timer instance with the given statement, setup code and timer function and run its timeit() method with number executions. The optional globals argument specifies a namespace in which to execute the code.

Changed in version 3.5: The optional globals parameter was added.
……

class timeit.Timer(stmt='pass', setup='pass', timer=<timer function>, globals=None)
Class for timing execution speed of small code snippets.
……


简单解释一下,stmt就是我们用来评测的代码段,setup用于初始化,这部分的语句只会在最开始被执行一次,默认为pass
timer则根据系统环境不同而不同,可以手工指定,但默认也是可以的,详细请参阅python文档
至于这个global,是python3.5引入的新内容,可以利用这个参数指定我们的命名空间

举例:
  1. def foo(x):
  2.     """Simple test function for illustration"""
  3.     return x


  4. foo(10)
  5. Out[2]: 10

  6. foo('hello')
  7. Out[3]: 'hello'
复制代码

我又随便弄了个函数foo(),就是个偷懒的示范><
现在我用timeit()函数来对foo()测试,2种方法
1. 在setup中给出初始导入
  1. In [4]: timeit.timeit("foo(10)",setup="from __main__ import foo")
  2. Out[4]: 0.12590337507851254
复制代码

2. 指定foo()所在的全局变量环境
  1. In [5]: timeit.timeit("foo('hello')",globals=globals())
  2. Out[5]: 0.10882844312273221
复制代码


咦,输出怎么只有这么点,这是什么鬼?
我前面说了,timeit函数调用与命令行模式是不一样的,你可以再试试命令行形式会怎么输出
值得一提的是,我用的是ipython交互环境,所以有%timeit这种便捷的工具,这里推荐一下ipython

以上

评分

参与人数 1荣誉 +3 鱼币 +3 贡献 +3 收起 理由
haskye + 3 + 3 + 3

查看全部评分

小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-2-10 16:11:04 | 显示全部楼层
DarkmasterSugar 发表于 2017-2-10 13:14
你这么好学!!!

timeit只适用于执行小代码片段的时间评测,有两种形式,一种以命令行模式运行(comma ...

嗯嗯,明白了。当时是有看Pathon的帮助文档,看到了globals参数的描述,但没太在意……在脚本中加上这个参数的赋值后就可以计算代码的运行时间了的。非常感谢~~
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2026-2-25 08:02

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表