简体   繁体   English

C:递归函数调用返回值(Tideman CS50)

[英]C: Recursive Function Call Return Values (Tideman CS50)

I have a series of "nodes" and need to create a function that determines if adding a new "arrow" between nodes would create a loop (this is the Tideman problem from CS50).我有一系列“节点”,需要创建一个函数来确定在节点之间添加一个新的“箭头”是否会创建一个循环(这是 CS50 中的 Tideman 问题)。 I wrote a separate function to use recursion in order to achieve this;为了实现这一点,我写了一个单独的函数来使用递归; however, my recursive function produces no errors but does not work as I hoped.然而,我的递归函数没有产生任何错误,但没有像我希望的那样工作。 Below is a visual of the problem and my rationale.下面是问题的视觉效果和我的理由。

问题的视觉

My plan was to use an "if" condition to check if a cycle would be created, and if so use "continue" to skip that iteration of the for loop (theoretically not adding the figurative arrow).我的计划是使用“if”条件来检查是否会创建循环,如果是,则使用“continue”来跳过 for 循环的迭代(理论上不添加比喻箭头)。 However, I am finding that my recursive function never finds a cycle, meaning it is adding all arrows.但是,我发现我的递归函数从未找到循环,这意味着它正在添加所有箭头。

In the image above as an example, I am checking if I can add the arrow in purple from B to C (index 1 to 2).以上图为例,我正在检查是否可以将紫色箭头从 B 添加到 C(索引 1 到 2)。 I use recursion to check every node that C points to and end with a non-zero return value if the value of the current node is equal to the source of the original arrow in question (1 in this case).我使用递归来检查 C 指向的每个节点,如果当前节点的值等于所讨论的原始箭头的源(在本例中为 1),则以非零返回值结束。

From the example, I would expect that as the "current" node moves through the blue chain and reaches node B again, the "cycle" counter would become non-zero and the overall recursive function would return a non-zero value.从这个例子中,我预计当“当前”节点穿过蓝色链并再次到达节点 B 时,“循环”计数器将变为非零,并且整个递归函数将返回一个非零值。 However, this does not appear to be the case.然而,情况似乎并非如此。

I imagine my function is not very efficient (anticipate some incoming constructive criticism), just trying to understand how the recursion is supposed to work.我想我的功能不是很有效(预计会有一些建设性的批评),只是想了解递归应该如何工作。

Here is my function that calls the recursive function:这是我调用递归函数的函数:

// Lock pairs into the candidate graph in order, without creating cycles
void lock_pairs(void)
{
    // Initialize variables
    int cycle_start;
    int current;
    int cycle;

    // Loop through pairs to determine acceptable locks
    for (int i = 0; i < pair_count; i++)
    {
        // Set new cycle start and current node
        cycle_start = pairs[i].winner;
        current = pairs[i].loser;

        // Reset Boolean
        cycle = 0;

        // If a cycle would be created, skip edge
        if (cycle_created(current, cycle_start, cycle) > 0)
        {
            continue;
        }

        else
        {
            // Add edge by locking pairs
            locked[pairs[i].winner][pairs[i].loser] = true;
        }
    }

    return;

Here is my recursive function:这是我的递归函数:

// Recursively check candidate node to see if cycle would be created
int cycle_created(int current, int cycle_start, int cycle)
{
    // Reached start of cycle, exit function and don't add edge
    if (current == cycle_start)
    {
        cycle++;
        return cycle;
    }

    else
    {
        // Loop through all pairs
        for (int j = 0; j < pair_count; j++)
        {
            // New candidate branch to investigate
            if (current == pairs[j].winner)
            {
                // Set new branch to check
                current = pairs[j].loser;

                // Call function to check new branch
                return cycle_created(current, cycle_start, cycle);
            }
        }

        // No edge found where current node is the winner, can lock edge
        return cycle;
    }
}

Here is how I believe the order of recursion happens:这是我认为递归顺序发生的方式:

cycle_created(2,1,0):循环创建(2,1,0):
j = 0 j = 0
current == pairs[0].winner当前==对[0].winner
current = pairs[0].loser = 5当前 = pairs[0].loser = 5
return cycle_created(5,1,0)返回 cycle_created(5,1,0)

cycle_created(5,1,0):循环创建(5,1,0):
j = 0 j = 0
... ...
j = 5 j = 5
currrent == pairs[5].winner当前 == 对 [5].winner
current = pairs[5].loser = 6当前 = pairs[5].loser = 6
return cycle_created(6,1,0)返回 cycle_created(6,1,0)

cycle_created(6,1,0):循环创建(6,1,0):
j = 0 j = 0
... ...
j = 6 j = 6
current == pairs[6].winner当前 == 对 [6].winner
current = pairs[6].loser = 4当前 = pairs[6].loser = 4
return cycle_created(4,1,0)返回 cycle_created(4,1,0)

cycle_created(4,1,0):循环创建(4,1,0):
j = 0 j = 0
... ...
j = 4 j = 4
current == pairs[4].winner当前==对[4].winner
current = 1当前 = 1
return cycle_created(1,1,0)返回 cycle_created(1,1,0)

cycle_created(1,1,0):循环创建(1,1,0):
1 == 1 1 == 1
cycle = 1循环 = 1
return 1返回 1

cycle_created(4,1,1):循环创建(4,1,1):
return (1)返回 (1)

cycle_created(6,1,1):循环创建(6,1,1):
return (1)返回 (1)

cycle_created(5,1,1):循环创建(5,1,1):
return (1)返回 (1)

cycle_created(2,1,1):循环创建(2,1,1):
return (1)返回 (1)

Here is my relevant code so far:到目前为止,这是我的相关代码:

#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

// Max number of candidates
#define MAX 9

// preferences[i][j] is number of voters who prefer i over j
int preferences[MAX][MAX];

// locked[i][j] means i is locked in over j
bool locked[MAX][MAX];

// Each pair has a winner, loser
typedef struct
{
    int winner;
    int loser;
} pair;

// Array of candidates
string candidates[MAX];
pair pairs[MAX * (MAX - 1) / 2];

int pair_count;
int candidate_count;

// Function prototypes
bool vote(int rank, string name, int ranks[]);
void record_preferences(int ranks[]);
void add_pairs(void);
void sort_pairs(void);
void lock_pairs(void);
int cycle_created(int current, int cycle_start, int cycle);

int main(int argc, string argv[])
{
    // Check for invalid usage
    if (argc < 2)
    {
        printf("Usage: tideman [candidate ...]\n");
        return 1;
    }

    // Populate array of candidates
    candidate_count = argc - 1;
    if (candidate_count > MAX)
    {
        printf("Maximum number of candidates is %i\n", MAX);
        return 2;
    }
    for (int i = 0; i < candidate_count; i++)
    {
        candidates[i] = argv[i + 1];
    }

    // Clear graph of locked in pairs
    for (int i = 0; i < candidate_count; i++)
    {
        for (int j = 0; j < candidate_count; j++)
        {
            locked[i][j] = false;
        }
    }

    pair_count = 0;
    int voter_count = get_int("Number of voters: ");

    // Query for votes
    for (int i = 0; i < voter_count; i++)
    {
        // ranks[i] is voter's ith preference
        int ranks[candidate_count];

        // Query for each rank
        for (int j = 0; j < candidate_count; j++)
        {
            string name = get_string("Rank %i: ", j + 1);

            if (!vote(j, name, ranks))
            {
                printf("Invalid vote.\n");
                return 3;
            }
        }

        record_preferences(ranks);

        printf("\n");
    }

    add_pairs();
    sort_pairs();
    lock_pairs();
    print_winner();
    return 0;
}

// Update ranks given a new vote
bool vote(int rank, string name, int ranks[])
{
    // Loop through each candidate
    for (int i = 0; i < candidate_count; i++)
    {
        // Check if vote matches any existing candidates
        if (strcmp(candidates[i], name) == 0)
        {
            // Update ranks array to indicate rank of input candidate
            ranks[rank] = i;
            return true;
        }
    }

    // No candidate found
    return false;
}

// Update preferences given one voter's ranks
void record_preferences(int ranks[])
{
    // Loop through each candidate except final candidate (no preferences over others)
    for (int i = 0; i < candidate_count - 1; i++)
    {
        // Loop through candidates following the row candidate
        for (int j = i + 1; j < candidate_count; j++)
        {
            // Increment preferences array, since candidate i is preferred over candidate j
            preferences[ranks[i]][ranks[j]]++;
        }
    }

    return;
}

// Record pairs of candidates where one is preferred over the other
void add_pairs(void)
{
    // Loop through row candidates (i)
    for (int i = 0; i < candidate_count; i++)
    {
        // Loop through column candidates (j)
        for (int j = 0; j < candidate_count; j++)
        {
            // Check if more voters prefer candidate i over candidate j
            if (preferences[i][j] > preferences[j][i])
            {
                // Update winner and loser in pairs array with index as current pair count
                pairs[pair_count].winner = i;
                pairs[pair_count].loser = j;

                // Increment pair count total
                pair_count++;
            }
        }
    }

    return;
}

// Sort pairs in decreasing order by strength of victory
void sort_pairs(void)
{
    // Initialize temporary variable pair
    pair tmp;

    // Loop through each pair, except last pair
    for (int i = 0; i < pair_count - 1; i++)
    {
        // Loop through pair j following pair i
        for (int j = i + 1; j < pair_count; j++)
        {
            // Check if second pair vote count is greater than first pair vote count
            if (preferences[pairs[j].winner][pairs[j].loser] > preferences[pairs[i].winner][pairs[i].loser])
            {
                // Swap pairs
                tmp = pairs[i];
                pairs[i] = pairs[j];
                pairs[j] = tmp;
            }
        }
    }

    return;
}

// Lock pairs into the candidate graph in order, without creating cycles
void lock_pairs(void)
{
    // Initialize variables
    int cycle_start;
    int current;
    int cycle;

    // Loop through pairs to determine acceptable locks
    for (int i = 0; i < pair_count; i++)
    {
        // Set new cycle start and current node
        cycle_start = pairs[i].winner;
        current = pairs[i].loser;

        // Reset Boolean
        cycle = 0;

        // If a cycle would be created, skip edge
        if (cycle_created(current, cycle_start, cycle) > 0)
        {
            continue;
        }

        else
        {
            // Add edge by locking pairs
            locked[pairs[i].winner][pairs[i].loser] = true;
        }
    }

    return;
}

// Recursively check candidate node to see if cycle would be created
int cycle_created(int current, int cycle_start, int cycle)
{
    // Reached start of cycle, exit function and don't add edge
    if (current == cycle_start)
    {
        cycle = 1;
        return cycle;
    }

    else
    {
        // Loop through all pairs
        for (int j = 0; j < pair_count; j++)
        {
            // New candidate branch to investigate
            if (current == pairs[j].winner)
            {
                // Set new branch to check
                current = pairs[j].loser;

                // Call function to check new branch
                return cycle_created(current, cycle_start, cycle);
            }
        }

        // No edge found where current node is the winner, can lock edge
        return cycle;
    }
}

I think your approach is mistaken for the following reasons:我认为您的方法是错误的,原因如下:

You must not look for a complete cycle, but for chains.您不能寻找完整的循环,而要寻找链条。 A chain exists if a pairs loser won a locked pair vs a locked pair winner.如果一对失败者赢得锁定对锁定对赢家,则存在链。 For instance, here exists a chain:例如,这里存在一条链:

a) You want to lock pair 4 but... a) 你想锁定 pair 4 但是......

b) pairs[4].loser returns 0 , meaning the loser is id 0 b) pairs[4].loser返回0 ,意味着失败者是 id 0

c) id 0 won at locked[0][2] c) id 0locked[0][2]获胜

d) id 2 won at locked[2][4] d) id 2locked[2][4]中获胜

e) Thus if you lock locked[5][0] as true you create a chain, so you shouldn't e) 因此,如果你将locked[5][0]true ,你就创建了一个链,所以你不应该

If you want to look at actual working pseudocode, see this: https://stackoverflow.com/a/74867105/14714821如果您想查看实际工作的伪代码,请参阅: https ://stackoverflow.com/a/74867105/14714821

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM