博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
CS61A Efficiency 笔记
阅读量:5267 次
发布时间:2019-06-14

本文共 4321 字,大约阅读时间需要 14 分钟。

CS61A Spring 学习笔记

原文地址:

Measuring Efficiency

计算一个程序需要多长时间和多少的内存是很困难的。一个可行的方式就是计算一个事件比如一个函数被调用了多少次。

比如Fibonacci数列

>>> def fib(n):        if n == 0:            return 0        if n == 1:            return 1        return fib(n-2) + fib(n-1)

比如我们计算fib(6)

因为递归,所以每次计算都要重复调用更小的fib(x),导致很多冗余计算。
定义一个计算某个函数被调用了多少次的高阶函数。这个函数的返回一个与输出等效函数,还包含一个计数用的变量。

>>> def count(f):        def counted(*args):            counted.call_count += 1            return f(*args)        counted.call_count = 0        return counted

example 1:

def count(f):    def counted(*args):        counted.call_count += 1        return f(*args)    counted.call_count = 0    return counteddef test(n):    if n<=3:        return 1    else:          return test(n-2)+test(n-4)+1test = count(test)ret = test(8)c = test.call_count

example2:

def count(f):    def counted(*args):        counted.call_count += 1        return f(*args)    counted.call_count = 0    return counteddef fib(n):        if n == 0:            return 0        if n == 1:            return 1        return fib(n-2) + fib(n-1)fib = count(fib)ret = fib(6)c = fib.call_count

Space

想知道函数占用多少的空间。我们需要知道函数执行过程中如何使用,保留和回收内存。如果一个环境为正在执行的部分提供上下文,那么我们说这个环境是激活的。当函数最终调用了return,函数不再是激活的。

在下面的执行过程中,当执行完成后,相应的环境(frame)不再需要时会变成灰色,表示不再激活。当return的返回值好没有被执行,对应的那个frame始终是激活的。
这里定义了一个高阶函数去计算最多同时有多少个frame被激活。

def fib(n):    if n == 0:        return 0    if n == 1:        return 1    return fib(n-2) + fib(n-1)def count_frames(f):        def counted(*args):            counted.open_count += 1            counted.max_count = max(counted.max_count, counted.open_count)            result = f(*args)            counted.open_count -= 1            return result        counted.open_count = 0        counted.max_count = 0        return countedfib = count_frames(fib)fib(4)

Memoization

在树回归中,像上面求解斐波那契数列的时候,很多次重复的计算是没有必要的。使用记忆化就能够把曾经计算过的结果用cache保存起来,在遇到相同的参数时,只需按照参数在cache中索引相应的值即可。这样做能够大大提高运算的效率。

记忆化使用一个高阶函数来表达,它也可以作为一个装饰器(decorator)。

>>> def memo(f):        cache = {}        def memoized(n):            if n not in cache:                cache[n] = f(n)            return cache[n]        return memoized

计算的过程由下图可以观察到

fib_memo

def fib(n):    if n == 0:        return 0    if n == 1:        return 1    return fib(n-2) + fib(n-1)def count(f):        def counted(*args):            counted.call_count += 1            return f(*args)        counted.call_count = 0        return counted def memo(f):    cache = {}    def memoized(n):        if n not in cache:            cache[n] = f(n)        return cache[n]    return memoized       counted_fib = count(fib)fib  = memo(counted_fib)fib(5)counted_fib.call_countfib(6)counted_fib.call_count

Orders of Growth

影响函数执行时的space和time的因素很多,很难求得实际中使用了多少。为方便估算,把不同类型的计算按照不同的复杂度划分类别。

使用ΘΘ标注:
n 表示输入的规模
R(n)R(n)表示n为输入时需要的资源
我们称R(n)R(n)Θ(f(n))Θ(f(n))的计算数量级增长(order of growth),写作R(n)=Θ(f(n))R(n)=Θ(f(n)).
存在一个上界和一个下界k1k1,k2k2,使得

k1f(n)R(n)k2f(n)k1⋅f(n)≤R(n)≤k2⋅f(n)

Example: Exponentiation

使用递归计算 bnbn

>>> def exp(b, n):        if n == 0:            return 1        return b * exp(b, n-1)

这是一个线性递归过程,需要 Θ(n)Θ(n)步和 Θ(n)Θ(n) space.

另外一个计算方式:

>>> def exp_iter(b, n):        result = 1        for _ in range(n):            result = result * b        return result

使用连续乘方计算:

b2b4b8=bb=b2b2=b4b4b2=b⋅bb4=b2⋅b2b8=b4⋅b4
这时只需要计算3步,但是这种方法使用于幂是2的情况。改进这个连续乘方的方法使其适用于任何幂。
bn={
(b12n)2bbn1if n is evenif n is odd
bn={(b12n)2if n is evenb⋅bn−1if n is odd

>>> def square(x):        return x*x>>> def fast_exp(b, n):        if n == 0:            return 1        if n % 2 == 0:            return square(fast_exp(b, n//2))        else:            return b * fast_exp(b, n-1)>>> fast_exp(2, 100)1267650600228229401496703205376

这个快速的算法是计算的复杂度降为Θlog(n)Θlog(n),当n取值很大的时候,它跟Θ(n)Θ(n)的区别就很明显。如n=1000时,上面的算法计算只需要执行14次乘法,而不是1000次。

Growth Categories

常见计算复杂度的分类:

1) 常数项
这两个的复杂度是相同的。
Θ(n)Θ(n) = Θ(500n)Θ(500∗n)
2) 对数
对数的底的取值对复杂度没有影响
3) 嵌套

>>> def overlap(a, b):        count = 0        for item in a:            if item in b:                count += 1        return count

复杂度为Θ(mn)Θ(m⋅n), m 外循环的长度,n为内循环的长度。

4)低阶项
比如下面的例子,两个数列中一个相差为1的组合有多少?

def overlap(a, b):    count = 0    for item in a:        if item in b:            count += 1    return countdef one_more(a):    return overlap([x-1 for x in a], a)one_more([3, 14, 15, 9])

这里的复杂度为Θ(n+n2)Θ(n+n2),其中n为列表的长度

常见的分类:

order_growth

转载于:https://www.cnblogs.com/siucaan/p/9623155.html

你可能感兴趣的文章
json plist 互转
查看>>
需求分析
查看>>
JAVA反射使用手记
查看>>
GitHub 多人协作开发 三种方式:
查看>>
python3 调用 beautifulSoup 进行简单的网页处理
查看>>
java Dialog
查看>>
strxfrm - 转换字符串
查看>>
文本域添加编辑器
查看>>
Yum安装MySQL以及相关目录路径和修改目录
查看>>
java获取hostIp和hostName
查看>>
RxJava结合Retrofit和Volley简单比较
查看>>
iOS 企业版 安装失败 原因
查看>>
ThreadLocal 理解
查看>>
关于web服务器和数据库的各种说法(搜集到的)
查看>>
一个 forceLayout() 和 requestLayout() 的测试
查看>>
【转】使用js触发事件
查看>>
Linux系统(Centos)下安装nodejs并配置环境
查看>>
《TCP/IP 详解 卷一》读书笔记 -----第四章 ARP
查看>>
python-5:字典类型操作:增删查改
查看>>
CentOS6.4(linux)下编写C程序(hello world)步骤及注意事项
查看>>