前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >用 Taichi 加速 Python:提速 100+ 倍!

用 Taichi 加速 Python:提速 100+ 倍!

作者头像
自学气象人
发布于 2022-11-02 02:33:39
发布于 2022-11-02 02:33:39
2K00
代码可运行
举报
文章被收录于专栏:自学气象人自学气象人
运行总次数:0
代码可运行

以下文章来源于太极图形 ,作者太极图形

Python 已经成为世界上最流行的编程语言,尤其在深度学习、数据科学等领域占据主导地位。但是由于其解释执行的属性,Python 较低的性能很影响它在计算密集(比如多重 for 循环)的场景下发挥作用,实在让人又爱又恨。如果你是一名经常需要使用 Python 进行密集计算的开发者,我相信你肯定会有下面的类似经历:

  • 我的 Python 程序里面有个很大的 for 循环,循环体里面全是密集的计算,跑起来好慢啊...
  • 我的程序里面只有一小部分计算是性能瓶颈,虽然可以用 C++ 改写然后用 ctypes 绑定一下,但是那样会很麻烦,还会有在别的机器上编译不了的风险。我希望所有的工作都能在一个 Python 脚本中完成!
  • 我之前是忠实的 C++/Fortran 用户,但是最近周围的同学用 Python 的越来越多,我也想试试 Python,但是无奈很多祖传代码用 Python 改写以后就会慢 100 多倍,我接受不了...
  • 我的工作中需要处理大量图片数据,而需要的图像处理功能 OpenCV 又不提供,只能自己手写两重 for 循环,在 Python 里面这么搞真是太痛苦了 ...

如果你有类似的烦恼,那真的值得了解一下 Taichi。我来简单介绍一下:Taichi 是一个嵌入在 Python 中的领域特定语言,其一大功能就是加速 Python,让 Python 代码跑得和 C++ 甚至 CUDA 一样快。Taichi 通过自己的编译器将被 @ti.kernel 修饰的函数编译到各种硬件上,包括 CPU 和 GPU,然后高性能执行。

(用户不用关心的)Taichi 运行原理:Python 代码被 Taichi 编译器编译到高性能二进制

由于 Taichi 开发者社区花了大量的精力优化 Taichi 在 Python 中的使用体验,所有的 Taichi 功能都可以在 import taichi as ti 以后使用,Taichi 本身也可以使用 pip 进行安装。当然,Taichi 也可以与常用的 Python 包(numpy、matplotlib、PyTorch 等)进行交互。

在这篇文章中,我们将通过三个计算例子来演示如何使用 Taichi 让你的 Python 轻松加速 > 50 倍。这三个例子是:1. 计算质数数目;2. 动态规划求解最长公共子序列;3. 求解反应-扩散方程。

🔗 https://github.com/taichi-dev/faster-python-with-taichi

计算素数个数

作为开胃小菜,我们先做一个小实验:计算小于给定正整数 的素数的个数。相信任何对 Python 有基础了解的人都不难写出类似下面这样的解法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
"""Count the number of primes in range [1, n].
"""

def is_prime(n: int):
    result = True
    for k in range(2, int(n ** 0.5) + 1):
        if n % k == 0:
            result = False
            break
    return result

def count_primes(n: int) -> int:
    count = 0
    for k in range(2, n):
        if is_prime(k):
            count += 1

    return count

print(count_primes(1000000))

这个方法的思路简单且粗暴:我们用一个函数 is_prime 来判断某个正整数 是不是素数,是素数则返回 1,不是则返回 0。这只要遍历检查从 2 到 之间是否有整数能够整除 即可。然后将小于 的全部整数依次代入此函数并统计结果。将上面的代码保存为 count_primes.py,在命令行运行:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
time python count_primes.py

在我的电脑上输出的运行结果是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
78498

real        0m2.235s
user        0m2.235s
sys        0m0.000s

耗时 2.235 秒。也许代码中 设置成一百万对你的电脑来说太轻松了,要不要把 改成一千万试试?我打赌不管你的电脑多么高端,你起码都要等个半分钟才能看到结果。

好了下面是魔法时刻:我们不修改上面的函数体,只 import 一个“库”,然后给两个函数分别加一个装饰器:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
"""Count the number of primes below a given bound.
"""
import taichi as ti
ti.init()

@ti.func
def is_prime(n: int):
    result = True
    for k in range(2, int(n ** 0.5) + 1):
        if n % k == 0:
            result = False
            break
    return result

@ti.kernel
def count_primes(n: int) -> int:
    count = 0
    for k in range(2, n):
        if is_prime(k):
            count += 1

    return count

print(count_primes(1000000))

仍然运行 time python count_primes.py 命令,输出的结果是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
78498

real        0m0.363s
user        0m0.546s
sys        0m0.179s

速度直接 x6! 而将 改成一千万的话,Taichi 的耗时只会增加到 0.8s 左右,而 Python 则需要大约 55 秒,Taichi 直接加速了 70 倍!不仅如此,我们还可以在 ti.init 中加上 ti.init(arch=ti.gpu) 参数,指定 Taichi 使用 GPU 来进行计算。在 GPU 上同样的计算 Taichi 只花了不到 0.45 秒,比 Python 足足快了 120 倍!你可以运行这里的代码亲身体会一下。

上面这个计算素数的例子使用的方法有点土,作为习题还可以,但在实际生产中就显得不那么实用了。我们接下来看一个实际中普遍使用的算法。

动态规划

动态规划(Dynamic Programming)是一类特别实用的算法,这类算法的哲学是以空间换时间,通过存储中间计算结果来减少重复计算量。我们这里选择一个求解最长公共子序列(Longest common subsequence, LCS)的例子 (算法导论的读者有木有)。

插播两个来自渊鸣的《算法导论》小故事:

  1. 笔者小时候买过一本《算法导论》,书中提到四位作者都来自“麻雀理工学院”。当时还很好奇:怎么会有学校叫这么奇怪的名字... 过了一阵才意识到自己可能成了盗版书籍的受害者。
  2. 10 年后,我还真的来到了麻省理工学院(MIT)读博士,一年后进行硕士论文答辩(MIT 叫做 Research Qualification Exam),我自然就带着 Taichi 的论文去了。答辩委员会里面有一位慈祥的教授,Charles E. Leiserson,嗯,就是《算法导论》的作者之一,“CLRS” 之中的 L。

言归正传。所谓子序列,就是一个序列的子集,但是保持它们在原序列中的顺序。比如说 [1, 2, 1][1, 2, 3, 1] 的子序列,而 [3, 2] 则不是。我们这里考虑对两条给定的序列,求出它们最长公共子序列的长度。最长公共子序列就是两个序列的所有公共子序列中最长的一条 (这个最长子序列未必唯一,但它的长度是唯一确定的)。

举个例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
a = [0, 1, 0, 2, 4, 3, 1, 2, 1]

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
b = [4, 0, 1, 4, 5, 3, 1, 2]

的最长公共子序列是

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
LCS(a, b) = [0, 1, 4, 3, 1, 2]

最长公共子序列有很多应用。比如大家日常使用的 Linux diff 命令和 git 工具(比较两个文件之间的相似度),还有生物信息学中判断两段基因的相似度(把数字换成 ACGT 就行),其中的实现都用到了 LCS。

动态规划计算 LCS 的想法是我们依次求解序列 a 的前 i 个元素和序列 b 的前 j 个元素的最长公共子序列的长度,通过让 ij 逐渐增加我们就逐步得出了最终的结果。我们用 f[i, j] 表示 LCS((prefix(a, i), prefix(b, j),其中 prefix(a, i) 表示序列 a 的前 i 个元素,即 a[0], a[1], ..., a[i - 1]。这样我们就得到递推式:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
f[i, j] = max(f[i - 1, j - 1] + (a[i - 1] == b[j - 1]),
              max(f[i - 1, j], f[i, j - 1]))

于是,一个 LCS 算法用 Python 可以很自然地书写为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
for i in range(1, len_a + 1):
    for j in range(1, len_b + 1):
        f[i, j] = max(f[i - 1, j - 1] + (a[i - 1] == b[j - 1]),
                      max(f[i - 1, j], f[i, j - 1]))

这里我们给出一个 Taichi 的加速实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import taichi as ti
import numpy as np

ti.init(arch=ti.cpu)

benchmark = True

N = 15000

f = ti.field(dtype=ti.i32, shape=(N + 1, N + 1))

if benchmark:
    a_numpy = np.random.randint(0, 100, N, dtype=np.int32)
    b_numpy = np.random.randint(0, 100, N, dtype=np.int32)
else:
    a_numpy = np.array([0, 1, 0, 2, 4, 3, 1, 2, 1], dtype=np.int32)
    b_numpy = np.array([4, 0, 1, 4, 5, 3, 1, 2], dtype=np.int32)


@ti.kernel
def compute_lcs(a: ti.types.ndarray(), b: ti.types.ndarray()) -> ti.i32:
    len_a, len_b = a.shape[0], b.shape[0]

    ti.loop_config(serialize=True) # 避免 Taichi 自动并行
    for i in range(1, len_a + 1):
        for j in range(1, len_b + 1):
            f[i, j] = max(f[i - 1, j - 1] + (a[i - 1] == b[j - 1]),
                          max(f[i - 1, j], f[i, j - 1]))

    return f[len_a, len_b]


print(compute_lcs(a_numpy, b_numpy))

将上面的代码保存为 lcs.py,然后在终端运行:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
time python lcs.py

得到的结果为(具体结果每次未必一致):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
2721

real        0m1.409s
user        0m1.112s
sys        0m0.549s

我们在代码中同时提供了分别使用 Taichi 和 Numpy 计算的版本,在我的电脑上对两个长度是 N=15000 的随机序列进行计算 Taichi 版本大约需要 0.9 秒,而 Python 则需要 476s,足足差了 500 多倍!大家可以运行一下体会 Taichi 相对 Numpy 那种飞一样的感觉。

当然,Numpy 主要针对的场景是以数组为基本单位的运算,遇到这种需要在数组内更细粒度进行计算的情况就比较无力了。而这正是 Taichi 能够发挥作用的地方。

反应 - 扩散方程

在大自然中我们常常会在动植物的表面见到一些有趣的图案,比如斑马身上的条纹,猎豹身上的斑点,河豚表面的花纹等等。

这些图案看起来是不规则的,但是又有一定的规律,并不完全随机。从进化的观点,这些图案是生物在长期演进和自然选择中逐渐形成的,但到底是什么规则决定了它们的形状一直是个有趣的问题。阿兰 . 图灵 (正是图灵机的发明人) 是最早注意到这一现象并尝试给出模型描述的人。他在论文 "The Chemical Basis of Morphogenesis" 中提出可以用两种化学物质 U, V 之间的相互作用来模拟图案的形成过程,其中物质 U 的角色类似被捕食者 (prey),物质 V 的角色类似捕食者 (predator)。它们之间的作用服从如下规则:

  1. 初始时空间中随机地分布了一些 U, V。
  2. 在每个时刻 1, 2, 3, ..., U, V 两种物质都向其邻域扩散。
  3. 当 U, V 相遇时,一定比例的 U, V 会合并转化为更多的 V (捕食者在捕食后数量会增加)
  4. 为了避免捕食者 V 的数量过多导致 U 的数量被消耗光,我们在每个时刻按照一定的比例 f 添加 U,同时按照一定的比例 k 移走 V。

于是整个过程可以用下面的反应 - 扩散方程描述:

\begin{align*} \frac{\mathrm{d}u}{\mathrm{d}t} &= D_u\Delta u - uv^2 + f(1-u)\\ \frac{\mathrm{d}v}{\mathrm{d}t} &= D_v\Delta v + uv^2 - (f+k)v. \end{align*}

这里关键的控制参数有四个,分别是 , 分别控制 U, V 的扩散速度, 代表 feed,控制 U 的添加量,而 代表 kill,控制移走 V 的比例。

为了在 Taichi 中模拟这一过程,我们将空间划分为网格,每个网格中 U, V 的浓度值用一个 vec2 来表示。注意拉普拉斯算子 的数值计算是需要访问当前网格周围的网格的,为了避免一边修改一边读取这种操作的发生,我们需要开辟两个形状为 的网格,每次用其中一个网格的值作为旧值,将更新后的浓度值写入另一个网格中,然后交换两个网格的角色。所以我们需要的数据结构应该是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
W, H = 800, 600
uv = ti.Vector.field(2, float, shape=(2, W, H)) 

初始时,我们假定网格中 U 的浓度处处是 1,然后随机选择 50 个点撒上 V:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import numpy as np

uv_grid = np.zeros((2, W, H, 2), dtype=np.float32)
uv_grid[0, :, :, 0] = 1.0
rand_rows = np.random.choice(range(W), 50)
rand_cols = np.random.choice(range(H), 50)
uv_grid[0, rand_rows, rand_cols, 1] = 1.0
uv.from_numpy(uv_grid)

实际的计算代码非常之简短:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@ti.kernel
def compute(phase: int):
    for i, j in ti.ndrange(W, H):
        cen = uv[phase, i, j]
        lapl = uv[phase, i + 1, j] + uv[phase, i, j + 1] + uv[phase, i - 1, j] + uv[phase, i, j - 1] - 4.0 * cen
        du = Du * lapl[0] - cen[0] * cen[1] * cen[1] + feed * (1 - cen[0])
        dv = Dv * lapl[1] + cen[0] * cen[1] * cen[1] - (feed + kill) * cen[1]
        val = cen + 0.5 * tm.vec2(du, dv)
        uv[1 - phase, i, j] = val

这里我们使用了取值为 0 或 1 的整数 phase 来控制使用 uv 的哪一层来作为旧的网格,并将更新的值写入 1-phase 对应的层中。

根据 V 的浓度进行染色,我们得到了如下的动画效果:

非常有趣的是,虽然 V 的初始浓度是随机设置的,但是最终得到的图案却具有相似性。

我们在代码中提供了基于 Taichi 和 Numba 的两份不同的实现,Taichi 的版本由于使用了 GPU 进行计算,计算的部分可以轻松达到 300+ fps,而 Numba 的版本计算部分虽然也是编译执行的,但由于是在 CPU 上计算的,只有大约 30fps 左右。大家可以亲自运行代码体会一下 Taichi 使用 GPU 加速的巨大优势。

总结

在这三个例子上 Taichi 都让程序有了大幅加速。主要的性能来自三点:

  1. Taichi 是编译性的,而 Python 是解释性的
  2. Taichi 能自动并行,而 Python 通常是单线程的
  3. Taichi 能在 GPU 上运行,而 Python 本身是在 CPU 上运行的

当然,加速 Python 还有很多其他工具,这里我们分析一下他们和 Taichi 的优劣。

与 Numpy/JAX/PyTorch/TensorFlow 比较:这几类工具都高度基于数组运算。计算的最小单位是数组,在 Data Science、Deep Learning 等领域是有明显的优势的。但是在科学计算领域,这样做导致灵活性缺失:比如说前面那个计算质数的程序,就比较难使用数组运算表示出来。Taichi 的优势就在于其灵活性,能够直接操纵循环的每一次迭代,以一种更细颗粒度进行对于计算的描述,类似 C++ 和 CUDA。

与 Cython 比较:使用 Cython 编写程序实现加速也是一种常见的选择。在 Numpy 和 Scipy 的官方代码中有不少模块都是使用 Cython 编写然后编译的。但按照 Cython 的要求书写代码会比较麻烦,会牺牲一些可读性。Cython 支持一定程度的并行计算,但不支持直接调用 GPU 进行计算。

与 Numba 比较:Numba 顾名思义,是非常适合针对 Numpy 进行加速的方案。当你的函数是针对 Numpy 的数组向量化的操作时,使用 Numba 将其编译以后执行可以大大加速。Taichi 相比 Numba 的优势还有:1. Taichi 支持各种灵活的数据类型,比如 struct, dataclass, quant, sparse 等等,你可以任意指定它们的内存排布,当数据量庞大时这个优势会非常明显。而 Numba 只有在针对 Numpy 的稠密数组时效果最佳。2. Taichi 可以调用不同的 GPU 后端进行计算,所以写大规模并行程序(如粒子仿真、渲染器等)这种操作对 Taichi 来说是小菜一碟。但你很难想象可以用 Numba 写一个还过得去的 (哪怕离线) 渲染器。

与 Pypy 比较:Pypy 是一个 Python 的 JIT 编译器,这个工具 2007 年就有了,和 Taichi 的解决方案有些类似,都是通过编译的方式加速 Python。Pypy 最大优势在于 Python 代码完全不用改变,就能通过 Pypy 加速。但是这也是 Pypy 加速比率比 Taichi 低的原因:因为 Pypy 需要在编译的同时保持 Python 所有的语言特性,所以能够进行的优化比较有限。而 Taichi 有一套自己的语法,虽然和 Python 很像但是也有自己的一些假设,这使得 Taichi 能够实现更大的加速。

与 ctypes 比较:ctypes 可以让用户在 Python 中调用 C 函数。C++、CUDA 编写的程序也可以用过 C 接口暴露给 Python 使用。但是,ctypes 会让工具链复杂化:为了写一段比较快的程序,用户需要同时掌握 C、Python、CMake、CUDA 等等语言,和本文描述的完全在 Python 中解决问题的方案比起来还是麻烦了一些。

总而言之,在科学计算任务上,Taichi 还是有自己独特的优势的,大家可以根据自己的需求选择最合适的工具。如果你需要在 Python 中实现类似 C/C++ 语言的性能,Taichi 不失为一个理想的选择!

最后,我们希望 Taichi 能够为你带来价值,也希望能够听到你对 Taichi 的反馈,欢迎给我们提交 issues,加入 Taichi 开发者社区。如果想一键体验 Taichi,只需要执行👇🏻

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
pip install -U taichi

并执行👇🏻

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ti gallery

就可以体验各种基于 Taichi 的高性能可视化 Demos,期待与大家相遇!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-10-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 自学气象人 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
数据结构和算法
10个算法:递归、排序、二分查找、搜索、哈希算法、贪心算法、分治算法、回溯算法、动态规划、字符串匹配算法。
分母为零
2019/12/04
5770
前端应该如何准备数据结构和算法?
据我了解,前端程序员有相当一部分不是科班出身,以至于对“数据结构”和“算法”的基础概念都不是很清晰,这直接导致很多人在看到有关这部分的内容就会望而却步。
ConardLi
2019/08/21
1K0
前端应该如何准备数据结构和算法?
02 复杂度分析_pythoner学习数据结构与算法系列
设计算法时,时间复杂度要比空间复杂度更容易出问题,所以一般情况一下我们只对时间复杂度进行研究。一般面试或者工作的时候没有特别说明的话,复杂度就是指时间复杂度。
诡途
2020/10/16
5500
02 复杂度分析_pythoner学习数据结构与算法系列
数据结构与算法笔记
数据结构与算法,是大学中计算机相关专业里的一门必修的基础课,当时学习的时候并不能列其中的知识点,毕业之后随着对计算机专业知识的了解加深,才意识到其重要性,今天我就来研究一番。
希里安
2023/10/30
2130
数据结构与算法笔记
聊聊数据结构和算法
其实笔者也是一样的,因为算法这个东西,平常真的实操的并不多,但是它又是面试必须问的知识点。因为它是程序员的基础知识。
35岁程序员那些事
2022/09/23
4160
数据结构与算法之美读书笔记
栈是一种操作受限的数据结构,只支持入栈和出栈操作。后进先出是它最大的特点。(特定的数据结构是对特定场景的抽象)
_春华秋实
2023/08/28
2950
算法笔记汇总精简版下载_算法与数据结构笔记
10个数据结构:数组、链表、栈、队列、散列表、二叉树、堆、跳表、图、Trie 树; 10个算法:递归、排序、二分查找、搜索、哈希算法、贪心算法、分治算法、回溯算法、动态 规划、字符串匹配算法。
全栈程序员站长
2022/09/20
9450
为啥学习数据结构和算法
基础知识就像是一座大楼的地基,它决定了我们的技术高度。而要想快速做出点事情,前提条件一定是基础能力过硬,“内功”要到位。
acc8226
2022/05/17
1710
Java面试考点4之数据结构
复杂度是衡量算法好坏的标准之一,我们需要掌握计算算法时间复杂度和空间复杂度的方法。计算时间复杂度的方法一般是找到执行次数最多的语句,然后计算语句执行次数的数量级,最后用大写 O 来表示结果。
马拉松程序员
2022/04/26
4580
Java面试考点4之数据结构
技术面试要了解的算法和数据结构知识
目录 在线练习 在线编程面试 数据结构 算法 贪心算法 位运算 复杂度分析 视频教程 面试宝典 计算机科学资讯 文件结构 在线练习 LeetCode Virtual Judge CareerCup HackerRank CodeFights Kattis HackerEarth Codility Code Forces Code Chef Sphere Online Judge – SPOJ 在线编程面试 Gainlo Refdash 数据结构 链表 链表
Albert陈凯
2018/04/04
1.3K0
技术面试要了解的算法和数据结构知识
常用的算法和数据结构 面试_数据结构与算法面试题80道
定义:最先发明的自平衡二叉查找树。在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下都是O(log n)。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。
全栈程序员站长
2022/09/23
7990
常用的算法和数据结构 面试_数据结构与算法面试题80道
【地铁上的面试题】--基础部分--数据结构与算法--动态规划和贪心算法
动态规划是一种解决多阶段决策问题的算法思想,它通过将问题划分为若干个子问题,并保存子问题的解来求解原问题的方法。动态规划的特点包括以下几个方面:
喵叔
2023/07/09
4480
01数据结构与算法总览_pythoner学习数据结构与算法系列
可以简单理解为: 当一个一维的链表的分叉有两个的时候, 它就变成了一个二维的数据结构,相当于树结构
诡途
2020/10/16
4100
01数据结构与算法总览_pythoner学习数据结构与算法系列
常见的算法分类方式
算法分类是一个广泛的主题,因为存在许多不同类型的算法,它们各自服务于不同的目的和领域。然而,以下是一些常见的算法分类方式:
jack.yang
2025/04/05
870
程序员必须掌握的算法有哪些?谈谈这这几年学过的算法
由于我之前一直强调数据结构以及算法学习的重要性,所以就有一些读者经常问我,数据结构与算法应该要学习到哪个程度呢?,说实话,这个问题我不知道要怎么回答你,主要取决于你想学习到哪些程度,不过针对这个问题,我稍微总结一下我学过的算法知识点,以及我觉得值得学习的算法。这些算法与数据结构的学习大多数是零散的,并没有一本把他们全部覆盖的书籍。下面是我觉得值得学习的一些算法以及数据结构,当然,我也会整理一些看过不错的文章给大家。大家也可以留言区补充。
帅地
2019/10/23
3.6K0
数据结构考研面试被问的问题_考研程序设计与数据结构
1. 顺序存储结构 ——把数据元素存放在地址连续的存储单元中,其数据间的逻辑关系和物理关系是一致的。
全栈程序员站长
2022/11/04
6820
数据结构考研面试被问的问题_考研程序设计与数据结构
数据结构与算法学习笔记
本文是王争老师的《算法与数据结构之美》的学习笔记,详细内容请看王争的专栏 。有不懂的地方指出来,我做修改。
全栈程序员站长
2022/07/23
7230
数据结构与算法学习笔记
重学前端(四)-数据结构与算法
最近在撸vue 和react的源码,虽然晦涩难懂,但是却发现新大陆,发现了数据结构和算法在前端的重要性,比如在react中,发现react的fiber树,对应的实际上是一个叫链表的数据结构,我们es6中新出的Map的数据结构其实就是对应字典的数据结构而Set对应的就是集合的数据结构,他是一个无序且唯一的数据结构。而在vue 中也是大量的用到栈和队列的数据结构,于是,遍寻资料,学习一番,记录如下,如有错误,请大佬指点!
用户7413032
2020/12/07
5760
数据结构与算法之基本概念
数据结构与算法是程序员内功体现的重要标准之一,且数据结构也应用在各个方面,业界更有程序=数据结构+算法这个等式存在。各个中间件开发者,架构师他们都在努力的优化中间件、项目结构以及算法提高运行效率和降低内存占用,在这里数据结构起到相当重要的作用。此外数据结构也蕴含一些面向对象的思想,故学好掌握数据结构对逻辑思维处理抽象能力有很大提升。
bigsai
2021/01/05
3590
一位算法工程师的自我修养
数据结构与算法 基本算法思想 动态规划 贪心算法 回溯算法 分治算法 枚举算法 算法基础 时间复杂度 空间复杂度 最大复杂度 平均复杂度 基础数据结构 数组 动态数组 树状数组 矩阵 栈与队列 栈 队列 阻塞队列 并发队列 双端队列 优先队列 堆 多级反馈队列 线性表 顺序表 链表 单链表 双向链表 循环链表 双向循环链表 跳跃表 并查集 哈希表(散列表) 散列函数 碰撞解决办法: 开放地址法 链地址法 再次哈希法 建立公共溢出区 布隆过滤器 位图 动态扩容 树 二叉树: 各种遍历,递归与非递归 二
攻城狮Chova
2022/01/22
4770
推荐阅读
相关推荐
数据结构和算法
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档