数据结构 — 堆

python中直接使用heapq建立 小根堆

http://139.9.1.231/index.php/2022/02/22/python3heapq/

堆简介

堆是一棵树,其每个节点都有一个键值,且每个节点的键值都大于等于/小于等于其父亲的键值。

每个节点的键值都大于等于其父亲键值的堆叫做小根堆,否则叫做大根堆。STL 中的 priority_queue 其实就是一个大根堆。

(小根)堆主要支持的操作有:插入一个数、查询最小值、删除最小值、合并两个堆、减小一个元素的值。

一些功能强大的堆(可并堆)还能(高效地)支持 merge 等操作。

一些功能更强大的堆还支持可持久化,也就是对任意历史版本进行查询或者操作,产生新的版本。

二叉堆

结构

从二叉堆的结构说起,它是一棵二叉树,并且是完全二叉树,每个结点中存有一个元素(或者说,有个权值)。

堆性质:父亲的权值不小于儿子的权值(大根堆)。同样的,我们可以定义小根堆。本文以大根堆为例。

由堆性质,树根存的是最大值(getmax 操作就解决了)。

插入操作

插入操作是指向二叉堆中插入一个元素,要保证插入后也是一棵完全二叉树。

最简单的方法就是,最下一层最右边的叶子之后插入。

如果最下一层已满,就新增一层。

插入之后可能会不满足堆性质?

向上调整:如果这个结点的权值大于它父亲的权值,就交换,重复此过程直到不满足或者到根。

可以证明,插入之后向上调整后,没有其他结点会不满足堆性质。

向上调整的时间复杂度是  的。

二叉堆的插入操作

删除操作

删除操作指删除堆中最大的元素,即删除根结点。

但是如果直接删除,则变成了两个堆,难以处理。

所以不妨考虑插入操作的逆过程,设法将根结点移到最后一个结点,然后直接删掉。

然而实际上不好做,我们通常采用的方法是,把根结点和最后一个结点直接交换。

于是直接删掉(在最后一个结点处的)根结点,但是新的根结点可能不满足堆性质……

向下调整:在该结点的儿子中,找一个最大的,与该结点交换,重复此过程直到底层。

可以证明,删除并向下调整后,没有其他结点不满足堆性质。

减小某个点的权值

很显然,直接修改后,向上调整一次即可

参考代码:


void up(int x) {
  while (x > 1 && h[x] > h[x / 2]) {
    swap(h[x], h[x / 2]);
    x /= 2;
  }
}

void down(int x) {
  while (x * 2 <= n) {
    t = x * 2;
    if (t + 1 <= n && h[t + 1] > h[t]) t++;
    if (h[t] <= h[x]) break;
    std::swap(h[x], h[t]);
    x = t;
  }
}

建堆

方法一:使用 decreasekey(即,向上调整)¶
从根开始,按 BFS 序进行。


void build_heap_1() {
  for (i = 1; i <= n; i++) up(i);
}
方法二:使用向下调整¶
这时换一种思路,从叶子开始,逐个向下调整


void build_heap_2() {
  for (i = n; i >= 1; i--) down(i);
}

Python中collections.defaultdict()使用

1、以一个例子开始:统计一个列表里各单词重复次数

words = ['hello', 'world', 'nice', 'world']
counter = dict()
for kw in words:
    counter[kw] += 1

这样写肯定会报错的,因为各词的个数都没有初始值,引发KeyError

2、改进一下:加入if判断

words = ['hello', 'world', 'nice', 'world']
counter = dict()
for kw in words:
    if kw in words:
        counter[kw] += 1
    else:
        counter[kw] = 0

3、再改进:使用setdefault()方法设置默认值

words = ['hello', 'world', 'nice', 'world']
counter = dict()
for kw in words:
    counter.setdefault(kw, 0)
    counter[kw] += 1

setdefault(),需提供两个参数,第一个参数是键值,第二个参数是默认值,每次调用都有一个返回值,如果字典中不存在该键则返回默认值,如果存在该键则返回该值,利用返回值可再次修改代码。

4、接着改进
一种特殊类型的字典本身就保存了默认值defaultdict(),defaultdict类的初始化函数接受一个类型作为参数,当所访问的键不存在的时候,可以实例化一个值作为默认值。

from collections import defaultdict
dd = defaultdict(list)
defaultdict(<type 'list'>, {})
#给它赋值,同时也是读取
dd['hh']
defaultdict(<type 'list'>, {'hh': []})
dd['hh'].append('haha')
defaultdict(<type 'list'>, {'hh': ['haha']})

该类除了接受类型名称作为初始化函数的参数之外,还可以使用任何不带参数的可调用函数,到时该函数的返回结果作为默认值,这样使得默认值的取值更加灵活。

>>> from collections import defaultdict
>>> def zero():
...     return 0
...
>>> dd = defaultdict(zero)
>>> dd
defaultdict(<function zero at 0xb7ed2684>, {})
>>> dd['foo']
0
>>> dd
defaultdict(<function zero at 0xb7ed2684>, {'foo': 0})

最终代码:

from collections import defaultdict
words = ['hello', 'world', 'nice', 'world']
#使用lambda来定义简单的函数:函数返回值就是默认key的value值
counter = defaultdict(lambda: 0) 
for kw in words:
    counter[kw] += 1

python 字典树、前缀匹配

在python中可以用字典来实现字典树这一结构。

class Trie:

    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.root = {}
        self.isEnd = '#'

    def insert(self, word: str) -> None:
        """
        Inserts a word into the trie.
        """
        node = self.root
        for char in word:
            if char not in node:
                node[char] = {}  #如果没有这一字母则新建一个字典
            node = node[char]
        node[self.isEnd] = '#'   #加上结束符

    def search(self, word: str) -> bool:
        """
        Returns if the word is in the trie.
        """
        node = self.root
        for char in word:
            if char not in node:
                return False
            node = node[char]
        return self.isEnd in node  #如果没有# 说明只是前缀


    def startsWith(self, prefix: str) -> bool:
        """
        Returns if there is any word in the trie that starts with the given prefix.
        """
        node = self.root
        for char in prefix:
            if char not in node:
                return False
            node = node[char]
        return True



# Your Trie object will be instantiated and called as such:
# obj = Trie()
# obj.insert(word)
# param_2 = obj.search(word)
# param_3 = obj.startsWith(prefix)

2.采用defaultdict创建trie



from collections import defaultdict
from functools import reduce
TrieNode = lambda: defaultdict(TrieNode)
class Trie:
    def __init__(self):
        self.trie = TrieNode()
    def insert(self, word):
        reduce(dict.__getitem__, word, self.trie)['end'] = True
    def search(self, word):
        return reduce(lambda d,k: d[k] if k in d else TrieNode(), word, self.trie).get('end', False)
    def startsWith(self, word):
        return bool(reduce(lambda d,k: d[k] if k in d else TrieNode(), word, self.trie).keys())

collections,解释是数据类型容器模块。这里面有一个collections.defaultdict()经常被用到

这里的defaultdict(function_factory)构建的是一个类似dictionary的对象,其中keys的值,自行确定赋值,但是values的类型,是function_factory的类实例,而且具有默认值。比如default(int)则创建一个类似dictionary对象,里面任何的values都是int的实例,而且就算是一个不存在的keyd[key] 也有一个默认值,这个默认值是int()的默认值0.

python 二分查找 :bisect模块

bisect模块的源码:

"""Bisection algorithms."""

def insort_right(a, x, lo=0, hi=None):

    if lo < 0:
        raise ValueError('lo must be non-negative')
    if hi is None:
        hi = len(a)
    while lo < hi:
        mid = (lo+hi)//2
        if x < a[mid]: hi = mid
        else: lo = mid+1
    a.insert(lo, x)

insort = insort_right   # backward compatibility

def bisect_right(a, x, lo=0, hi=None):

    if lo < 0:
        raise ValueError('lo must be non-negative')
    if hi is None:
        hi = len(a)
    while lo < hi:
        mid = (lo+hi)//2
        if x < a[mid]: hi = mid
        else: lo = mid+1
    return lo

bisect = bisect_right   # backward compatibility

def insort_left(a, x, lo=0, hi=None):

    if lo < 0:
        raise ValueError('lo must be non-negative')
    if hi is None:
        hi = len(a)
    while lo < hi:
        mid = (lo+hi)//2
        if a[mid] < x: lo = mid+1
        else: hi = mid
    a.insert(lo, x)


def bisect_left(a, x, lo=0, hi=None):

    if lo < 0:
        raise ValueError('lo must be non-negative')
    if hi is None:
        hi = len(a)
    while lo < hi:
        mid = (lo+hi)//2
        if a[mid] < x: lo = mid+1
        else: hi = mid
    return lo

# Overwrite above definitions with a fast C implementation
try:
    from _bisect import *
except ImportError:
    pass

方法介绍

bisect模块采用经典的二分算法查找元素。模块提供下面几个方法:

bisect.bisect_left(a, x, lo=0, hi=len(a)):返回待插入值应该放置的位置下标,如果 a 中有和x相同的元素,就在返回该元素左边的下标,即在左边插入

定位x在序列a中的插入点,并保持原来的有序状态不变。参数lo和hi用于指定查找区间。如果x已经存在于a中,那么插入点在已存在元素的左边。函数的返回值是列表的整数下标。

bisect.bisect_right(a, x, lo=0, hi=len(a))

和上面的区别是插入点在右边。函数的返回值依然是一个列表下标整数。

bisect.bisect(a, x, lo=0, hi=len(a))

等同于bisect_right()。

注意,前面这三个方法都是获取插入位置,也就是列表的某个下标的,并不实际插入。但下面三个方法实际插入元素,没有返回值。

bisect.insort_left(a, x, lo=0, hi=len(a))

将x插入有序序列a内,并保持有序状态。相当于a.insert(bisect.bisect_left(a, x, lo, hi), x)。碰到相同的元素则插入到元素的左边。这个操作通常是 O(log n) 的时间复杂度。

bisect.insort_right(a, x, lo=0, hi=len(a))

同上,只不过碰到相同的元素则插入到元素的右边。

bisect.insort(a, x, lo=0, hi=len(a))

等同于insort_right()。

python 全局变量 global 和 nonlocal

两个关键词都用于允许在一个局部作用域中使用外层的变量。

  • global 表示将变量声明为全局变量
  • nonlocal 表示将变量声明为外层变量(外层函数的局部变量,而且不能是全局变量)
  • python引用变量的顺序如下:
  1. 当前作用域局部变量
  2. 外层作用域变量
  3. 当前模块中的全局变量
  4. python内置变量

比如:

a=1
def test(x):
   global x
   a=x
因为a外层变量是全局变量,所以要加global


def test2():
  a=1
  def test(x)
     nonlocal x
     a=x
因为a外层变量不是是全局变量,也是一个外层的局部变量,所以要加nonlocal

python 在新建一个变量时,默认作用域为当前局部作用域。如果不声明global or nonglocal,就认为是一个局部变量。

内部函数,不修改外层变量可以访问外层变量。

a = 1
def fun():
    print(a) # 在函数内部找不到对 a 的定义,则去外层查询。输出1。
fun()

内部函数,修改同名外层变量,则python会认为是定义了一个新的局部变量,与外层变量无关了。

a = 1
def fun():
a = 2 # 声明了一个局部变量,与外面等于1的那个a没有关系了
print(a) # 输出2
fun()
print(a) # 输出1

在内部函数修改同名全局变量之前调用变量名称,则引发Unbound-LocalError

a = 1
def fun():
    print(a) # 先引用
    a = 2 # 再修改
fun()

如果我们想要强制访问外层变量 a,便可以使用 global 关键字

a = 1
def fun():
    global a # a为全局变量
    print(a) # 输出1
    a = 2 # 改变的是全局变量,因此出了这个局部作用域,仍然有效
fun()
print(a) # 输出2
注意,这个时候,不要将 global 换为 nonlocal 关键字,不然会报错:

这是因为 nonlocal 表示外层变量,但是一旦外层变量是全局变量,则只能用 global。如果将这段代码全部放到一个函数中,则可以使用 nonlocal :

def outer_fun():
a = 1
def fun():
nonlocal a # a为外层变量
print(a) # 输出1
a = 2
fun()
print(a) #输出2
outer_fun()

注意:可以发现,之前的将 global 换为 nonlocal 的报错,和这次的将 nonlocal 改为 global 的报错,错误类型是不同的。前者是 nonlocal a 这一行报错,后者是 print(a) 这一行报错。也就是说,在使用 nonlocal a 之前,必须保证外层的确已经定义过 a 了,但是在 global a 的时候,可以允许全局变量中还没有定义过a,可以留在后面定义。比如将 print(a) 之前加上对a的定义,便不会出错:

def outer_fun():
a = 1
def fun():
global a # a为全局变量,与上面等于1的 a 没有关系
a = 3 # 定义全局变量
print(a) # 输出3
a = 2
fun()
print(a) #输出1,局部变量
outer_fun()
print(a) # 输出2,全局变量

python 进制转换

十六进制 到 十进制

使用 int() 函数 ,第一个参数是字符串 ‘0Xff’ ,第二个参数是说明,这个字符串是几进制的数。 转化的结果是一个十进制数。

int(‘0xf’,16)
15
二进制 到 十进制

int(‘10100111110’,2)
1342
八进制 到 十进制

int(’17’,8)
15
其实可以看到,不管 几进制数 转换成 十进制数 ,都是用 int() 函数 。之后后面的 第二个参数 写清楚 前面字符串 是 几进制数就可以 。注意一定要合法。 比如2进制数就不能出现2这样的字符。


十进制 转 十六进制

hex(1033)
‘0x409’

二进制 转 十六进制

就是 二进制先转成 十进制, 再转成 十六进制。

hex(int(‘101010’,2))
‘0x2a’
八进制到 十六进制

就是 八进制先转成 十进制, 再转成 十六进制。

hex(int(’17’,8))

‘0xf’

十进制转二进制

bin(10)
‘0b1010’
十六进制转 二进制

十六进制->十进制->二进制

bin(int(‘ff’,16))
‘0b11111111’
八进制 到 二进制

八进制先到十进制,再到二进制

bin(int(’17’,8))
‘0b1111’


二进制 到 八进制

oct(0b1010)
‘012’
十进制到八进制

oct(11)
‘013’
十六进制到八进制

oct(0xf)
‘017’
可见oct 函数 可将 任意进制的数 转换成 8进制的。

python中的取整:

  1. 向上取整:math.ceil()
  2. 向下取整:math.floor()、整除”//”
  3. 四舍五入:round()——奇数向远离0取整,偶数去尾取整;或言之:奇数进位,偶数去尾
  4. 向0取整:int()
  • 向上取整:math.ceil()
import math

math.ceil(-0.5)
>>> 0
 
math.ceil(-0.9)
>>> 0

math.ceil(0.3)
>>> 1

  • 向下取整:math.floor()、整除”//”
  • 
    math.floor(-0.3)
    >>> -1
     
    math.floor(0.9)
    >>> 0
    
    
    (-1) // 2  # -0.5
    >>> -1
     
    (-3) // 2  # -1.5
    >>> -2
     
    1 // 2    # 0.5 
    >>> 0
     
    3 // 2    # 1.5
    

    int()

    int(-0.5)>>> 0 int(-0.9)>>> 0 int(0.5)>>> 0 int(0.9)>>> 0一句话总结:int()函数是“向0取整”,取整方向总是让结果比小数的绝对值更小

  • round()
  • 
    round(-2.5)
    >>> -2
     
    round(-1.5)
    >>> -2
     
    round(-0.5)
    >>> 0
     
    round(0.5)
    >>> 0
     
    round(1.5)
    >>> 2
     
    

    python 中的序列

    python中序列是最基本的数据结构,他是一块用于存放多个值的连续内存空间。python中内置了5个常用的序列结构,分别是列表、元组、集合、字典和字符串

    1、序列:存放多个值的连续内存空间。

    序列包括:列表[ ],元组 ( ),字典{a:b},集合set={ a,b},字符串:”asdc”

    其中:集合和字典不支持索引、切片、相加和相乘操作。

    序列索引:正序索引,从0开始到n-1,倒序索引:从-(n-1)开始,到-1结束

    切片:name[start:end:step]

    相加:name1+name2

    乘法: name*2 表示name+name

    空序列: [None]

    检测某个元素是否是序列的成员: x in sequence

    计算序列的长度、最大小值: len( )\、max( )、 min( )

    其他内置函数:

    list()将序列转换为list
    str()将序列转换为字符串
    sum()计算元素和
    sorted()排序
    reversed()将原序列元素反向
    enumerate()将序列组合为一个索引序列

    python 中查看数据类型:

    内置函数isinstance(object, (type1,type2…))
    isinstance(‘content’, str)
    返回True or False

    使用内置函数type(object)
    print(type(1))
    print(type(‘content’))

    2、列表 list

    列表中的元素可以是任意各种类型的组合。

    list(seqence ) 将序列转换为列表

    listname=[a,v,bd,d]

    访问序列元素 :

    for i in list:

    for index, item in enumerate(list):

    enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。

    enumerate(sequence, [start=0])
    sequence -- 一个序列、迭代器或其他支持迭代对象。
    start -- 下标起始位置。

    添加元素: list.append(“x”)

    添加多个元素: list.extend(seqence)

    根据元素值删除元素: list.remove(value)

    统计指定元素出现次数:list.count(obj)

    获取指定元素首次出现的下标:list.index(obj)

    统计元素和:sum(iterable[,start])

    start表示统计结果需要在 加上start值。 start — 指定相加的参数,如果没有设置这个值,默认为0。

    排序: list.sort(key=None,reverse=False)

    • key — 主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序。如设置key=str.lower,表示在排序时候不区分字母大小写。
    • reverse — 排序规则,reverse = True 降序, reverse = False 升序(默认)。
    # 获取列表的第二个元素
    def takeSecond(elem):
        return elem[1]
     
    # 列表
    random = [(2, 2), (3, 4), (4, 1), (1, 3)]
     
    # 指定第二个元素排序
    random.sort(key=takeSecond)

    个人理解:首先将list元素送到key定义的函数中 ,然后再把返回值进行排序。

    排序:使用内置的sorted函数: sorted(iterable,key=None,reverse=False)

    列表推导式:

    list=[expression for x in range]

    range是 range ()函数生成的range对象

    从x中筛选满足codition的x

    list = [expression for x in range if condition]

    二维列表:[[ ],[ ],[ ]]

    使用for循环创建:

    for i in range(4):   
       arr.append([])
       for j in range(5):
          arr[i].append[j]
    
    

    列表推导式创建:

    arr = [[j for j in range(6)] for i in range(4)]

    3、元组:不可变的列表,定义以后不可修改,只能重新定义

    A=(1,2,3,4)

    tuple 元组:不可修改,在python中,元组使用一对小括号将所有元素括起来,但小括号并不是必须的只需要将一组值用逗号分隔开,python就可以视为元组 B=1,2,3,4,5 也是元组

    创建元组 tuple(data)

    题外话:print(“ddd”,end=” “) //表示不换行输出

    因为元组不可修改,因此需要对元组中某个元素修改时需要重新定义元组:

    A=(“W”,”Q”)

    A=(“w”,”q”)

    元组推导式:和list类似,将[ ]变为( ),并在最外面还要嵌套一层tuple( ):

    x= (i for i in range) //这句生成的结果不是一个元组,而是一个生成器对象,因此需要千年套一层tuple()

    tuple(x)就是元组

    4、字典 dict( ) == c++中的Map

    dictionary={‘key’:’value’}

    创建字典:

    dictionary = dict(zip(list1,list2))

    zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。

    dictionary = dict(‘key1’=’value1′,’key2’=’value2’,….)

    创建值为空的字典: dictionary = dict.fromkeys(list)

    获取指定键的值:

    dictionary.get(key[,default])

    遍历字典:

    dictionary.item() 返回“键-值”列表

    for item  in  dictionary.item(): 获得每一个键值对元组:(key,value)
    for key,value  in  dictionary.item(): 获得每一个键-值

    字典推导式:

    dictionary = {i:j for i,j in zip(list1,list2)}

    5、 集合 set():{ }集合中元素不重复,用于自动去重

    创建set :x = {1,2,3 }

    y=set(iteration)

    创建空集合 z =set( )

    集合的增删:

    setname.add(element) //添加元素

    setname.remove(element) //删除 元素

    setname.pop() //删除元素

    setname.clear() //清空set

    集合的交并差补运算: & | – ^

    实例: c = set1&set2

    6、字符串str()

    拼接字符串:+

    转换字符串 str(num)

    计算字符串长度 len(str)

    计算字符串所占字节数: len(str.encode())

    字符串编解码 str.encode() str.decode()

    截取字符串 string[start:end:step] (前闭后开)

    分割字符str.split(seq,maxsplit)

      seq — 分隔符,默认为所有的空字符,包括空格、换行(\n)、制表符(\t)等。

    maxsplit 分割次数。默认为 -1, 即分隔所有。

    合并字符:string.jion(iterable) // string 合并符,用于合并时候的分隔符

    检索字符串

    str.count(sub[,start,end]) //统计sub出现的次数

    str.find (sub[,start,end])// 检索是否存在sub,不存在返回-1,否则返回首次出现的位置

    str.index (sub[,start,end])// 检索是否存在sub , 返回首次出现的位置 ,如不存在会报异常

    str.startwith(prefix[,start,end]) //判断是否以prefix开头,返回true or flase

    str.endwith(prefix[,start,end]) //判断是否以prefix结尾,返回true or flase

    大小写转换: str.lower() str.upper()

    去除字符串中的空格和特殊字符

    str.strip([chars]) // chars :要去除的字符,默认是去除空格、制表符\t,回车符\r 换行符\n

    str.lstrip([chars])

    str.rstrip([chars])

    图像金字塔pyrUp和pyrDown

    函数作用:
    图像金字塔中的向上和向下采样分别通过 pyrUp 和 pyrDown 实现。这里的向上向下采样,是针对图像的尺寸而言的(和金字塔的方向相反),向上就是图像尺寸加倍,向下就是图像尺寸减半。
    对于 pyrUp,图像首先在每个维度上扩大为原来的两倍,新增的行和列(偶数行和列)以 0 填充。然后用指定的滤波器进行卷积(实际上是一个在每个维度上都扩大为原来两倍的过滤器)去估计”丢失“像素的近似值。
    对于 pyrDown,先用高斯核对图像进行卷积,然后删除所有偶数行和偶数列,新的到的图像面积就会变成源图像的四分之一。
    注意:pyrUp 和 pyrDown 不是互逆的。

    通常,我们过去使用的是恒定大小的图像。但是在某些情况下,我们需要使用不同分辨率的(相同)图像。例如,当在图像中搜索某些东西(例如人脸)时,我们不确定对象将以多大的尺寸显示在图像中。在这种情况下,我们将需要创建一组具有不同分辨率的相同图像,并在所有图像中搜索对象。这些具有不同分辨率的图像集称为“图像金字塔”(因为当它们堆叠在底部时,最高分辨率的图像位于顶部,最低分辨率的图像位于顶部时,看起来像金字塔)。

    有两种图像金字塔。1)高斯金字塔**和2)**拉普拉斯金字塔

    高斯金字塔中的较高级别(低分辨率)是通过删除较低级别(较高分辨率)图像中的连续行和列而形成的。然后,较高级别的每个像素由基础级别的5个像素的贡献与高斯权重形成。通过这样做,M×NM×N图像变成M/2×N/2M/2×N/2图像。因此面积减少到原始面积的四分之一。它称为Octave。当我们在金字塔中越靠上时(即分辨率下降),这种模式就会继续。同样,在扩展时,每个级别的面积变为4倍。我们可以使用**cv.pyrDown**()和**cv.pyrUp**()函数找到高斯金字塔。

    img = cv.imread('messi5.jpg') 
    lower_reso = cv.pyrDown(higher_reso)

    衡量代码运行时间性能:

    %time 和 %timeit

    对于计时有两个十分有用的魔法命令:%%time 和 %timeit. 如果你有些代码运行地十分缓慢,而你想确定是否问题出在这里,这两个命令将会非常方便。

    1.%%time 将会给出cell的代码运行一次所花费的时间。

    %%time
    import time
    for _ in range(1000):
    time.sleep(0.01)# sleep for 0.01 seconds

    output:
    CPU times: user 196 ms, sys: 21.4 ms, total: 217 ms
    Wall time: 11.6 s
    注:window 下好像只能显示 “Wall time”, Ubuntu16.4可以正常显示,其他系统未进行测试。。。

    2.%time 将会给出当前行的代码运行一次所花费的时间。

    import numpy
    %time numpy.random.normal(size=1000)

    output:
    Wall time: 1e+03 µs
    3.%timeit 使用Python的timeit模块,它将会执行一个语句100,000次(默认情况下),然后给出运行最快3次的平均值。

    import numpy
    %timeit numpy.random.normal(size=100)

    output:
    12.8 µs ± 1.25 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)