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.