计算图中路径的递归函数的复杂性
Posted
技术标签:
【中文标题】计算图中路径的递归函数的复杂性【英文标题】:Complexity of a recursive function that counts paths in graph 【发布时间】:2015-02-19 21:10:24 【问题描述】:我找到了一个有向图的函数,它对于其中的顶点“u”和“v”,它计算从“u”到“v”的所有可能步行,步行上正好有 k 个边。 代码和算法来自here。所以,
// C++ program to count walks from u to v with exactly k edges
#include <iostream>
using namespace std;
// Number of vertices in the graph
#define V 4
// A naive recursive function to count walks from u to v with k edges
int countwalks(int graph[][V], int u, int v, int k)
// Base cases
if (k == 0 && u == v) return 1;
if (k == 1 && graph[u][v]) return 1;
if (k <= 0) return 0;
// Initialize result
int count = 0;
// Go to all adjacents of u and recur
for (int i = 0; i < V; i++)
if (graph[u][i]) // Check if is adjacent of u
count += countwalks(graph, i, v, k-1);
return count;
我试图找出并证明这个算法的复杂性。根据帖子:
"上述函数的最坏情况时间复杂度为O(V^k) 其中V 是给定图中的顶点数。我们可以简单分析 通过绘制递归树来计算时间复杂度。最坏的情况发生在 完整的图表。在最坏的情况下,递归树的每个内部节点 正好有 n 个孩子。”
但是,我找不到导致我可以分析的树的递归,以证明该算法是O(V^k)
。另外,我认为最好的情况是Theta(1)
。真的吗?一般情况下呢?
【问题讨论】:
P(k+1) = VP(k)。我错过了什么吗?是的,最好的情况是 Θ(1)。而且我不知道如何谈论平均情况(具有 V(V-1)/2 边的随机图?)。 【参考方案1】:对于完整的图,每个节点都连接到其他节点,因此您的 for
循环将进行 |V|
递归调用。这将在每次递归调用时发生,直到 k
变为 1,所以总共有 O(|V|^k)
递归调用。
你可以这样表达:
T(V, k) = |V|*T(V, k - 1)
= |V|*|V|*T(V, k - 2)
= |V|^2*|V|*T(V, k - 3)
= ...
总是T(V, _)
,因为一个节点可以被多次访问。
当前三个 if 条件之一在第一次调用期间触发时,最好的情况确实是 O(1)
。
我不确定的平均情况,但我认为它应该仍然很糟糕。考虑一个链表图和一个巨大的k
:您将多次遍历相同的边以使k
为0 或1。随着您添加更多路径,这种情况会逐渐变得更糟。
【讨论】:
【参考方案2】:一般的“平均情况”分析在这样的问题中有点不适,因为您可以选择如何定义“随机”图。一种方法是说每个可能的边都以某个概率 p 出现,0
【讨论】:
以上是关于计算图中路径的递归函数的复杂性的主要内容,如果未能解决你的问题,请参考以下文章