📜  算法中的时空权衡

📅  最后修改于: 2021-05-08 19:02:24             🧑  作者: Mango

在本文中,我们将讨论算法中的时空权衡。权衡是一种情况,一件事增加而另一件事减少。这是解决以下问题的一种方法:

  • 花费更少的时间并使用更多的空间,或者
  • 在很小的空间中花费大量时间。

最好的算法是有助于解决以下问题的算法,该问题需要较少的内存空间并且还需要较少的时间来生成输出。但是总的来说,并非总是能够同时达到这两个条件。最常见的条件是使用查找表的算法。这意味着可以写下一些有关每个可能值的问题的答案。解决此问题的一种方法是写下整个查找表,这将使您很快找到答案,但会占用大量空间。另一种方法是在不写下任何内容的情况下计算答案,这虽然占用很少的空间,但是可能会花费很长时间。因此,您拥有的时间效率更高的算法将节省空间。

时空权衡的类型

  • 压缩或未压缩的数据
  • 重新渲染或存储图像
  • 较小的代码或循环展开
  • 查找表或重新计算

压缩或未压缩的数据时空折衷可以应用于数据存储问题。如果存储的数据未压缩,则将占用更多空间,但所需时间更少。但是,如果将数据压缩存储,则需要较少的空间,但是会花费更多的时间来运行解压缩算法。在许多情况下,可以直接使用压缩数据。在压缩位图索引的情况下,使用压缩比不使用压缩要快得多。

重新渲染或存储图像在这种情况下,仅存储源并将其渲染为图像会占用更多空间,但所需时间更少,即,将图像存储在缓存中比重新渲染更快,但需要更多的内存空间。

较小的代码或循环展开较小的代码在内存中占用较少的空间,但是它需要很高的计算时间,这是在每次迭代结束时跳回到循环的开始所需要的。循环展开可以以增加二进制大小为代价来优化执行速度。它在内存中占用更多空间,但需要更少的计算时间。

查找表或重新计算在查找表中,实现可以包括整个表,这样可以减少计算时间,但会增加所需的内存量。它可以重新计算,即根据需要计算表条目,从而增加了计算时间,但减少了内存需求。

例如:用数学术语,斐波纳契数的序列F n由递归关系定义:

根据上述递归关系使用递归找到第N斐波那契项的简单解决方案。

下面是使用递归的实现:

C++
// C++ program to find Nth Fibonacci
// number using recursion
#include 
using namespace std;
 
// Function to find Nth Fibonacci term
int Fibonacci(int N)
{
    // Base Case
    if (N < 2)
        return N;
 
    // Recursively computing the term
    // using recurrence relation
    return Fibonacci(N - 1) + Fibonacci(N - 2);
}
 
// Driver Code
int main()
{
    int N = 5;
 
    // Function Call
    cout << Fibonacci(N);
 
    return 0;
}


Java
// Java program to find Nth Fibonacci
// number using recursion
class GFG {
 
    // Function to find Nth Fibonacci term
    static int Fibonacci(int N)
    {
        // Base Case
        if (N < 2)
            return N;
 
        // Recursively computing the term
        // using recurrence relation
        return Fibonacci(N - 1) + Fibonacci(N - 2);
    }
 
    // Driver Code
    public static void main(String[] args)
    {
        int N = 5;
 
        // Function Call
        System.out.print(Fibonacci(N));
    }
}
 
// This code is contributed by rutvik_56.


C#
// C# program to find Nth Fibonacci
// number using recursion
using System;
class GFG
{
 
  // Function to find Nth Fibonacci term
  static int Fibonacci(int N)
  {
 
    // Base Case
    if (N < 2)
      return N;
 
    // Recursively computing the term
    // using recurrence relation
    return Fibonacci(N - 1) + Fibonacci(N - 2);
  }
 
  // Driver Code
  public static void Main(string[] args)
  {
    int N = 5;
 
    // Function Call
    Console.Write(Fibonacci(N));
  }
}
 
// This code is contributed by pratham76.


C++
// C++ program to find Nth Fibonacci
// number using recursion
#include 
using namespace std;
 
// Function to find Nth Fibonacci term
int Fibonacci(int N)
{
    int f[N + 2];
    int i;
 
    // 0th and 1st number of the
    // series are 0 and 1
    f[0] = 0;
    f[1] = 1;
 
    // Iterate over the range [2, N]
    for (i = 2; i <= N; i++) {
 
        // Add the previous 2 numbers
        // in the series and store it
        f[i] = f[i - 1] + f[i - 2];
    }
 
    // Return Nth Fibonacci Number
    return f[N];
}
 
// Driver Code
int main()
{
    int N = 5;
 
    // Function Call
    cout << Fibonacci(N);
 
    return 0;
}


输出:
5

时间复杂度: O(2 N )
辅助空间: O(1)

说明:由于一次又一次地对同一子问题进行多次计算,因此上述实现的时间复杂度是指数级的。使用的辅助空间最小。但是我们的目标是减少方法的时间复杂性,即使它需要额外的空间。下面是讨论的优化方法。

高效方法:为了优化上述方法,其想法是使用动态编程通过记忆重叠的子问题来降低复杂性,如下面的递归树所示:

下面是上述方法的实现:

C++

// C++ program to find Nth Fibonacci
// number using recursion
#include 
using namespace std;
 
// Function to find Nth Fibonacci term
int Fibonacci(int N)
{
    int f[N + 2];
    int i;
 
    // 0th and 1st number of the
    // series are 0 and 1
    f[0] = 0;
    f[1] = 1;
 
    // Iterate over the range [2, N]
    for (i = 2; i <= N; i++) {
 
        // Add the previous 2 numbers
        // in the series and store it
        f[i] = f[i - 1] + f[i - 2];
    }
 
    // Return Nth Fibonacci Number
    return f[N];
}
 
// Driver Code
int main()
{
    int N = 5;
 
    // Function Call
    cout << Fibonacci(N);
 
    return 0;
}
输出:
5

时间复杂度: O(N)
辅助空间: O(N)

说明:上述实现的时间复杂度是线性的,方法是使用辅助空间来存储重叠的子问题状态,以便在需要时可以进一步使用它。