保存计算值而不是多次重新计算的术语是啥?

Posted

技术标签:

【中文标题】保存计算值而不是多次重新计算的术语是啥?【英文标题】:What's the term for saving values of calculations instead of recalculating multiple times?保存计算值而不是多次重新计算的术语是什么? 【发布时间】:2015-11-08 00:48:42 【问题描述】:

如果你有这样的代码(用 java 编写,但适用于任何类似的语言):

public static void main(String[] args) 
    int total = 0;
    for (int i = 0; i < 50; i++)
        total += i * doStuff(i % 2); // multiplies i times doStuff(remainder of i / 2)


public static int doStuff(int i) 
    // Lots of complicated calculations

您可以看到还有改进的余地。 doStuff(i % 2) 仅返回两个不同的值 - 一个用于偶数的 doStuff(0),另一个用于奇数的 doStuff(1)。因此,您每次通过说doStuff(i % 2) 来重新计算这些值都会浪费大量的计算时间/能力。你可以这样改进:

public static void main(String[] args) 
    int total = 0;
    boolean[] alreadyCalculated = new boolean[2];
    int[] results = new int[2];
    for (int i = 0; i < 50; i++) 
        if (!alreadyCalculated[i % 2]) 
            results[i % 2] = doStuff(i % 2);
            alreadyCalculated[i % 2] = true;
        
        total += i * results[i % 2];
    

现在它访问一个存储的值,而不是每次都重新计算。保持这样的数组可能看起来很愚蠢,但对于从i = 0, i &lt; 500 循环并且您每次都检查i % 32 或其他情况的情况,数组是一种优雅的方法。

这种代码优化有术语吗?我想详细了解它的不同形式和约定,但我缺乏简明的描述。

【问题讨论】:

【参考方案1】:

这种代码优化有术语吗?

是的,有:

在计算中,memoization 是一种优化技术,主要用于通过存储昂贵的函数调用的结果并在再次出现相同的输入时返回缓存的结果来加速计算机程序。

https://en.wikipedia.org/wiki/Memoization

【讨论】:

【参考方案2】:

Common-subexpression-elimination (CSE) 与此有关。这种情况是这种情况与从循环中提升循环不变计算的结合。

我同意 CBroe 的观点,您可以将这种特定形式的缓存记忆称为缓存记忆,尤其是您使用笨重的 alreadyCalculated 数组实现它的方式。您可以优化它,因为您知道哪些调用将是新值,哪些将是重复的。通常,为了所有调用者的利益,您会在被调用函数中使用静态数组来实现记忆化。理想情况下,您可以使用一个标记值来标记尚未计算结果的条目,而不是为此维护一个单独的数组。或者对于一组稀疏的输入值,只需使用哈希(而不是例如具有 2^32 个条目的数组)。

您也可以避免在主循环中使用if

public class Optim

  public static int doStuff(int i)  return (i+5) << 1; 
  public static void main(String[] args)
  

    int total = 0;
    int results[] = new int[2];

    // more interesting if we pretend the loop count isn't known to be > 1, so avoiding calling doStuff(1) for n=1 is useful.
    // otherwise you'd just do int[] results =  doStuff(0), doStuff(1) ;

    int n = 50;

    for (int i = 0 ; i < Math.min(n, 2) ; i++) 
        results[i] = doStuff(i);
        total += i * results[i];
    
    for (int i = 2; i < n; i++)  // runs zero times if n < 2
        total += i * results[i % 2];
    
    System.out.print(total);
  

当然,在这种情况下,我们可以进一步优化。 sum(0..n) = n * (n+1) / 2,所以我们可以使用它来获得一个封闭形式(非循环)的解决方案,以doStuff(0)(偶数项之和)和doStuff(1)(奇数项之和)的形式。所以我们只需要两个 doStuff() 的结果,每个结果一次,避免任何需要记忆。

【讨论】:

以上是关于保存计算值而不是多次重新计算的术语是啥?的主要内容,如果未能解决你的问题,请参考以下文章

Spark partitionBy |按列值而不是 columnName=value 保存

使用值而不是占位符保存到文件中

Cakephp:保存数组值而不是整数

在选择选项 laravel 上保存名称值而不是 id

指针的Boost序列化保存指针的十六进制值而不是对象的内容

在 ag 网格下拉列表中,如何在选择后显示名称并在保存集值而不是名称上显示。?