菜单
×
   ❮   
HTML CSS JAVASCRIPT SQL PYTHON JAVA PHP HOW TO W3.CSS C C++ C# BOOTSTRAP REACT MYSQL JQUERY EXCEL XML DJANGO NUMPY PANDAS NODEJS R TYPESCRIPT ANGULAR GIT POSTGRESQL MONGODB ASP AI GO KOTLIN SASS VUE DSA GEN AI SCIPY AWS CYBERSECURITY DATA SCIENCE
     ❯   

记忆化


记忆化

记忆化是一种将结果存储起来以避免重复计算的技术。

当使用记忆化来改进递归算法时,由于其从主问题开始并将其分解为更小子问题的过程,因此称为“自顶向下”方法。

记忆化用于 动态规划


使用记忆化查找第 \(n\) 个斐波那契数

第 \(n\) 个斐波那契数可以使用递归来查找。阅读 本页面 了解其具体做法。

此实现的弊端在于,当尝试查找更高的斐波那契数时,计算次数和递归调用次数会“爆炸性”增长,因为相同的计算会一遍又一遍地重复。

示例

使用递归查找第 6 个斐波那契数

def F(n):
    print('Computing F('+str(n)+')')
    if n <= 1:
        return n
    else:
        return F(n - 1) + F(n - 2)

print('F(6) = ',F(6))
运行示例 »

从上面的示例运行结果可以看出,即使只是查找第 6 个斐波那契数,也有 25 次计算,其中许多计算是重复的。

但是,使用记忆化可以更有效地通过递归查找第 \(n\) 个斐波那契数。

我们通过创建一个数组 memo 来存储斐波那契数,以便可以通过 memo[n] 找到第 \(n\) 个斐波那契数。只有当斐波那契数不存在于 memo 数组中时,我们才会进行计算。

示例

使用递归查找第 6 个斐波那契数,但使用记忆化来避免不必要的递归调用

def F(n):
    if memo[n] != None: # Already computed
        return memo[n]
    else: # Computation needed
        print('Computing F('+str(n)+')')
        if n <= 1:
            memo[n] = n
        else:
            memo[n] = F(n - 1) + F(n - 2)
        return memo[n] 

memo = [None]*7
print('F(6) = ',F(6))
print('memo = ',memo)
运行示例 »

从上面的示例运行结果可以看出,记忆化对于减少计算次数非常有帮助。

计算次数从初始代码的 25 次减少到使用记忆化的最后一个示例中的 7 次,而且随着我们想要查找的斐波那契数的高度增加,使用记忆化的好处也会快速增加。

查找第 30 个斐波那契数在初始代码中需要 2,692,537 次计算,但在使用记忆化实现的算法中,仅需要 31 次计算!

通过运行下面的代码可以得到这个结果。

示例

查看查找第 30 个斐波那契数时,使用记忆化和不使用记忆化的计算次数差异

computation_count = 0
def F(n):
    global computation_count
    computation_count += 1
    if n <= 1:
        return n
    else:
        return F(n - 1) + F(n - 2)
        
computation_count_mem = 0
def F_mem(n):
    if memo[n] != None: # Already computed
        return memo[n]
    else: # Computation needed
        global computation_count_mem
        computation_count_mem += 1
        if n <= 1:
            memo[n] = n
        else:
            memo[n] = F_mem(n - 1) + F_mem(n - 2)
        return memo[n] 

print('F(30) = ',F(30))
print(f'Number of computations: {computation_count}')
print('\nUsing memoization:')
memo = [None]*31
print('F(30) = ',F_mem(30))
print(f'Number of computations with memoiztion: {computation_count_mem}')
运行示例 »

AVL 树中的记忆化

有关 AVL 树的更多详细信息,请参阅 本页面

AVL 树是一种自平衡二叉树。

每次向 AVL 树插入或删除节点时,都必须计算所有祖先节点的平衡因子,利用左右子树的高度来判断是否需要进行旋转以恢复平衡。

为了避免计算每个节点的树高(一直递归到叶子节点)来计算平衡因子,每个节点都存储了其子树的高度。

示例

class TreeNode:
    def __init__(self, data):
        self.data = data
        self.left = None
        self.right = None
        self.height = 1
运行示例 »

这意味着,要查找节点的平衡因子,只需用已存储的右子节点高度减去已存储的左子节点高度即可,无需其他计算。

在 AVL 树中存储节点高度是一种记忆化的形式,因为值被存储起来以避免重新计算。在 AVL 树中,当高度像这样存储时,它被称为**增强属性**。

增强属性是元素的属性,不一定非要存储,但为了提高操作效率而存储。

节点高度当然必须在某个时候计算,但这仅在使用 回溯 时进行,并且仅在绝对需要时才进行。



×

联系销售

如果您想将 W3Schools 服务用于教育机构、团队或企业,请发送电子邮件给我们
sales@w3schools.com

报告错误

如果您想报告错误,或想提出建议,请发送电子邮件给我们
help@w3schools.com

W3Schools 经过优化,旨在方便学习和培训。示例可能经过简化,以提高阅读和学习体验。教程、参考资料和示例会不断审查,以避免错误,但我们无法保证所有内容的完全正确性。使用 W3Schools 即表示您已阅读并接受我们的使用条款Cookie 和隐私政策

版权所有 1999-2024 Refsnes Data。保留所有权利。W3Schools 由 W3.CSS 提供支持