计算图中路径的递归函数的复杂性
Complexity of a recursive function that counts paths in graph
我找到了一个用于有向图的函数,对于其中的顶点“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;
}
我试图找到并证明这个算法的复杂性。根据 post:
"The worst case time complexity of the above function is O(V^k) where V
is the number of vertices in the given graph. We can simply analyze
the time complexity by drawing recursion tree. The worst occurs for a
complete graph. In worst case, every internal node of recursion tree
would have exactly n children."
但是,我找不到导致我可以分析以证明该算法是 O(V^k)
的树的递归。另外,我认为最好的情况是 Theta(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, _)
因为一个节点可以被多次访问
最好的情况确实是 O(1)
,即在第一次调用期间触发前三个 if 条件之一。
平均情况我不确定,但我认为应该还是很糟糕。考虑一个链表图和一个巨大的 k
:为了使 k
变为 0 或 1,您将多次遍历相同的边。随着路径的增加,情况会变得越来越糟。
一般 "average case" 分析在像这样的问题中有点不适,因为您可以选择如何定义 "random" 图。一种方法是说对于 0 <= p <= 1,每个可能的边都以某种概率 p 出现,然后尝试根据 p 分析平均情况 运行 时间。但是还有其他方法可以定义随机图。平均案例分析通常也很困难。但是,如果你用 "average" 定义你的意思(即随机图是什么意思)那么有人可能会尝试一下。
我找到了一个用于有向图的函数,对于其中的顶点“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;
}
我试图找到并证明这个算法的复杂性。根据 post:
"The worst case time complexity of the above function is O(V^k) where V is the number of vertices in the given graph. We can simply analyze the time complexity by drawing recursion tree. The worst occurs for a complete graph. In worst case, every internal node of recursion tree would have exactly n children."
但是,我找不到导致我可以分析以证明该算法是 O(V^k)
的树的递归。另外,我认为最好的情况是 Theta(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, _)
因为一个节点可以被多次访问
最好的情况确实是 O(1)
,即在第一次调用期间触发前三个 if 条件之一。
平均情况我不确定,但我认为应该还是很糟糕。考虑一个链表图和一个巨大的 k
:为了使 k
变为 0 或 1,您将多次遍历相同的边。随着路径的增加,情况会变得越来越糟。
一般 "average case" 分析在像这样的问题中有点不适,因为您可以选择如何定义 "random" 图。一种方法是说对于 0 <= p <= 1,每个可能的边都以某种概率 p 出现,然后尝试根据 p 分析平均情况 运行 时间。但是还有其他方法可以定义随机图。平均案例分析通常也很困难。但是,如果你用 "average" 定义你的意思(即随机图是什么意思)那么有人可能会尝试一下。