简体   繁体   中英

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). 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). 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). 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).

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. 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):
j = 0
current == pairs[0].winner
current = pairs[0].loser = 5
return cycle_created(5,1,0)

cycle_created(5,1,0):
j = 0
...
j = 5
currrent == pairs[5].winner
current = pairs[5].loser = 6
return cycle_created(6,1,0)

cycle_created(6,1,0):
j = 0
...
j = 6
current == pairs[6].winner
current = pairs[6].loser = 4
return cycle_created(4,1,0)

cycle_created(4,1,0):
j = 0
...
j = 4
current == pairs[4].winner
current = 1
return cycle_created(1,1,0)

cycle_created(1,1,0):
1 == 1
cycle = 1
return 1

cycle_created(4,1,1):
return (1)

cycle_created(6,1,1):
return (1)

cycle_created(5,1,1):
return (1)

cycle_created(2,1,1):
return (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...

b) pairs[4].loser returns 0 , meaning the loser is id 0

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

d) id 2 won at locked[2][4]

e) Thus if you lock locked[5][0] as true you create a chain, so you shouldn't

If you want to look at actual working pseudocode, see this: https://stackoverflow.com/a/74867105/14714821

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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