【问题标题】:Problem with in-degrees in Kahn's algorithm implementation in cC 中 Kahn 算法实现中的入度问题
【发布时间】:2020-05-27 16:37:32
【问题描述】:

我正在尝试在 C 中为卡恩算法制作一个串行代码。我基于它的伪代码如下:

S ← Set of all nodes with no incoming edge 

while S is non-empty do     
    remove a node n from S     
    add n to tail of L
    for each node m with an edge e from n to m do
         remove edge e from the graph
         if m has no other incoming edges then
             insert m into S 

if graph has edges then
    return error   (graph has at least one cycle) 
else      
    return L       (a topologically sorted order)

我的代码读取了一个 .mtx 文件,该文件显示了图中节点的邻接关系,就是这个:

7 11
7 8
3 8
11 2
11 9
11 10
8 9
3 10

我使用一个名为 indegree 的 int 数组来保存每个节点 i 的入度。例如,如果节点 5 的入度值为 3,则:indegree[5]=3。每当 i 被读取为文件中每一行的第二个数字时,值 indegree[i] 就会增加 1。这表示有一条边指向节点 i。 这是我的代码:

#include <stdio.h>
#include <stdlib.h>

//struct for queues L and S
struct Queue
{
    int front, rear, size;
    unsigned capacity;
    int* array;
};

struct Queue* createQueue(unsigned capacity)
{
    struct Queue* queue = (struct Queue*) malloc(sizeof(struct Queue));
    queue->capacity = capacity;
    queue->front = queue->size = 0;  
    queue->rear = capacity - 1;  
    queue->array = (int*) malloc(queue->capacity * sizeof(int));
    return queue;
}

int isFull(struct Queue* queue)
{  return (queue->size == queue->capacity);  }

int isEmpty(struct Queue* queue)
{  return (queue->size == 0); }


//inserting element
void enqueue(struct Queue* queue, int item)
{
    if (isFull(queue))
        return;
    queue->rear = (queue->rear + 1)%queue->capacity;
    queue->array[queue->rear] = item;
    queue->size = queue->size + 1;
    printf("%d enqueued to queue\n", item);
}

//dequeueing element
int dequeue(struct Queue* queue)
{
    if (isEmpty(queue))
        return -1;
    int item = queue->array[queue->front];
    queue->front = (queue->front + 1)%queue->capacity;
    queue->size = queue->size - 1;
    return item;
}

//print queue
void print_queue(struct Queue* queue)
{
    int j;

    for(j=queue->front; j < queue->rear; j++)
{
printf("I put this number: %d in list L", queue->array[j]);
printf("\n");
}
}

int main()

{
FILE *fp; //filestream
int K,j,i,k;
int N=7;
char line[50];

fp = fopen("/home/konst/Documents/Project_Parallel/ibm32.mtx", "r");

//array that holds the in-degrees for each node i. For example if node 5 has an in-degree value of 3 then: indegree[5]=3
int indegree[N];

//when in .mtx file, it reads only the second number in each line, in this case i, and increases by one the value of indegree[i] (since there is an edge from node j to node i)
while(fgets(line,50,fp)!=NULL)  
    {  
sscanf(line, "%*d %d", &i);
printf("%d\n" , i);  //I inserted this print to check if the .mtx file is read correctly(it is!)
indegree[i] ++;
printf("Node : %d with indegree value: %d\n",i,indegree[i]);  //I inserted this print to check the indegree value that is incremented in each iteration 
    }


struct Queue* L = createQueue(N);  //N number of nodes
struct Queue* S = createQueue(N);  //N number of nodes

for (i=0; i<N; i++)
{

 if (indegree[i] == 0) //if this is true then node i doesnt have any incoming edges therefore I enqueue it in S
 {
printf("I found a value that has indegree[%d]=0 and put it in S queue\n", i);
enqueue(S, i);
 }

}

while (isEmpty(S) != 0) 
{
i = dequeue(S); 
enqueue(L,i);

while(fgets(line,50,fp)!=NULL)  
{  

sscanf(line, "%d%d",&j, &k); 
if (j == i)
{
indegree[k]--; 
break; 
}
}
if (indegree[k] == 0) 
enqueue(S,k);


}

if (isFull(L) !=0)
{
return -1; 
}
else
{
print_queue(L); 
}

fclose(fp);

}

现在,问题来了。当我运行我的文件(编译器是 gcc)时,我得到的结果是这个:

11
Node : 12 with indegree value: -1
11
Node : 12 with indegree value: -1
8
Node : 8 with indegree value: 1
8
Node : 8 with indegree value: 2
2
Node : 2 with indegree value: 482383745
9
Node : 9 with indegree value: 1
10
Node : 10 with indegree value: -908997711
9
Node : 9 with indegree value: 2
10
Node : 10 with indegree value: -908997710
-1 enqueued to queue
-1 enqueued to queue
-1 enqueued to queue
-1 enqueued to queue
-1 enqueued to queue
-1 enqueued to queue
-1 enqueued to queue

现在第一个奇怪的事情是,即使节点 11 被读取,节点 12 也会打印在“节点:x,入度值:x”消息中。在某些节点中,这些奇怪的数字不断被打印为 indegree 的值。但是对于节点 8 和 9,这可以正常工作吗?!

任何帮助将不胜感激!

【问题讨论】:

    标签: c arrays algorithm topological-sort


    【解决方案1】:

    首先,缩进你的代码。

    其次,您需要初始化indegree 的值。这就是为什么您打印的某些学位有偏差。

    第三,你的N 太小了。您将其设置为 7,但在示例 .mtx 文件中,您一直到 11。对于该文件,您需要 N 为 12。

    第四,如果您提供的输出是程序输出的所有内容,那么您不是在读取 .mtx 文件,因为您为读取的每一行打印一行。

    第五,为什么要在这里打破循环:

    if (j == i)
    {
    indegree[k]--; 
    break; 
    }
    

    这意味着您一次不会向队列中添加多个节点。这有意义吗?

    第六,至少可以说,您缺乏实施。 您在循环的每次迭代中迭代邻接列表。 这给了我们O(N^3) 的最坏情况,此时卡恩的算法实际上是线性的。

    第七,我认为.mtx 的第一行与其他行是分开的。 我认为这是说图中有 7 个 egdes 和 11 个节点。 但是,您将其视为对边缘的描述。 如果我的假设是正确的,那么此信息可用于动态确定N,而不是像您那样对其进行硬编码。 然后将N 设置为第一行的第二个数字。

    八,您需要重置文件指针。 因此,当您再次尝试读取文件时,什么都不会发生。 我还建议不要多次阅读该文件。 而是将值存储在数组中。

    第九,很高兴看到您在最后调用 fclose,但如果您的图表包含一个循环,那么您返回 -1 并且不要调用 fclose

    关于下面的评论:

    为了阐明第四点,如果这是您的程序输出的所有内容,那么

    11
    Node : 12 with indegree value: -1
    11
    Node : 12 with indegree value: -1
    8
    Node : 8 with indegree value: 1
    8
    Node : 8 with indegree value: 2
    2
    Node : 2 with indegree value: 482383745
    9
    Node : 9 with indegree value: 1
    10
    Node : 10 with indegree value: -908997711
    9
    Node : 9 with indegree value: 2
    10
    Node : 10 with indegree value: -908997710
    -1 enqueued to queue
    -1 enqueued to queue
    -1 enqueued to queue
    -1 enqueued to queue
    -1 enqueued to queue
    -1 enqueued to queue
    -1 enqueued to queue
    

    那么这个循环永远不会运行:

    while(fgets(line,50,fp)!=NULL)  
        {  
    sscanf(line, "%*d %d", &i);
    printf("%d\n" , i);  //I inserted this print to check if the .mtx file is read correctly(it is!)
    indegree[i] ++;
    printf("Node : %d with indegree value: %d\n",i,indegree[i]);  //I inserted this print to check the indegree value that is incremented in each iteration 
        }
    

    我会简单地用 for 循环初始化 indegree

    对于N,您需要它至少比输入中最大节点的名称大一。 如果某些节点没有出现在文件中,就像您提供的示例一样,那么它们仍然是您的图表的一部分,不应(通常)被忽略。 例如,如果您正在实施 Dijkstra 的图搜索算法,那么您可以忽略这些节点,但对于 Kahn,您需要将它们放在拓扑顺序中的某个位置(但是,它们的位置并不重要)。 如果您要忽略孤立的节点,则必须谨慎行事。 像你那样做会导致你指向indegree 之外,因为它的大小为 7,但有些节点比这大。 您必须将它们的值映射到 0、1、...、6 以适合数组。 这并不难,可以通过排序(对数线性时间)或使用哈希映射(线性时间)来完成。 重申一下,这在 Kahn 算法中并不理想,因为您需要按照拓扑顺序考虑所有节点的位置,因此只需输入 N = 12 并忽略节点 0。 您还可以设置N = 11 并减少.mtx 文件中的所有值。

    【讨论】:

    • 感谢您的回复!我的 .mtx 文件不包含节点 1、2、4、5。我的印象是 N 代表图中的节点数。如果不包括以前的节点,我还应该将 N 设置为 12 吗?其次,我尝试通过编写 indegree[n] = {0} 来用零初始化 indegree 数组(我假设这就是你的意思?),但我得到了一个错误。您对 .mtx 文件的第一行通常说明边和节点的数量是正确的,但是我没有将其包含在我的文件中,以便更轻松地读取文件。你能进一步解释你的第四点吗?
    猜你喜欢
    • 2022-11-26
    • 2012-11-25
    • 2021-04-21
    • 2022-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-27
    相关资源
    最近更新 更多