简体   繁体   中英

Logical flaw in CS50 Problem Set 3 - Tideman?

I am currently taking CS50, an online-based introductory module to coding by Harvard University. Whilst writing my code for a problem called "Tideman", I had many difficulties with the lock_pairs function, as I am very new to coding. A description of the problem can be found here: https://cs50.harvard.edu/x/2020/psets/3/tideman/ .

With some help from @TomKarzes, I managed to refine my code slightly, but for some reason I am now unable to fulfil one of the question's requirements ie my code is not working as it should now, but I am quite positive it worked a few days ago. Even when I use my original code (without Tom's input), it still does not work anymore, so I am really confused (like... was I seeing things?).

The function which I have issues with is:

bool iscycle(int index, bool visited[])
{
    if (visited[index])
    {
        return true;
    }
    visited[index] = true;
    for (int i = 0; i < candidate_count; i++)
    {
        if (locked[index][i] && iscycle(i, visited))
        {
            return true;
        }
    }
    return false;
}

// Lock pairs into the candidate graph in order, without creating cycles
void lock_pairs(void)
{
    for (int i = 0; i < pair_count; i++)
    {
        locked[pairs[i].winner][pairs[i].loser] = true;
        bool visited[candidate_count];
        for (int j = 0; j < candidate_count; j++)
        {
            visited[j] = false;
        }
        if (iscycle(i, visited))
        {
            locked[pairs[i].winner][pairs[i].loser] = false;
        }
    }
    return;
}

My entire code (including the function above) is as follows:

#include <cs50.h>
#include <stdio.h>
#include <string.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);
void print_winner(void);

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[])
{
    for (int i = 0; i < candidate_count; i++)
    {
        if (strcmp(candidates[i], name) == 0)
        {
            ranks[rank] = i;
            return true;
        }
    }
    return false;
}

// Update preferences given one voter's ranks
void record_preferences(int ranks[])
{
    for (int i = 0; i < candidate_count; i++)
    {
        for (int j = 0; j < candidate_count; j++)
        {
            if (i < j)
            {
                preferences[ranks[i]][ranks[j]]++;
            }
        }
    }
    return;
}

// Record pairs of candidates where one is preferred over the other
void add_pairs(void)
{
    for (int i = 0; i < candidate_count; i++)
    {
        for (int j = i + 1; j < candidate_count; j++)
        {
            if (preferences[i][j] > preferences[j][i])
            {
                pairs[pair_count].winner = i;
                pairs[pair_count].loser = j;
                pair_count++;
            }
            else if (preferences[i][j] < preferences[j][i])
            {
                pairs[pair_count].winner = j;
                pairs[pair_count].loser = i;
                pair_count++;
            }
        }
    }
    return;
}

// Sort pairs in decreasing order by strength of victory
void sort_pairs(void)
{
    int swapcounter = -1;
    pair toswap;
    do
    {
        swapcounter = 0;
        for (int i = 0; i < pair_count - 1; i++)
        if (preferences[pairs[i].winner][pairs[i].loser] < preferences[pairs[i + 1].winner][pairs[i + 1].loser])
        {
            toswap = pairs[i];
            pairs[i] = pairs[i + 1];
            pairs[i + 1] = toswap;
            swapcounter++;
        }
    }
    while (swapcounter != 0);
    return;
}

bool iscycle(int index, bool visited[])
{
    if (visited[index])
    {
        return true;
    }
    visited[index] = true;
    for (int i = 0; i < candidate_count; i++)
    {
        if (locked[index][i] && iscycle(i, visited))
        {
            return true;
        }
    }
    return false;
}

// Lock pairs into the candidate graph in order, without creating cycles
void lock_pairs(void)
{
    for (int i = 0; i < pair_count; i++)
    {
        locked[pairs[i].winner][pairs[i].loser] = true;
        bool visited[candidate_count];
        for (int j = 0; j < candidate_count; j++)
        {
            visited[j] = false;
        }
        if (iscycle(i, visited))
        {
            locked[pairs[i].winner][pairs[i].loser] = false;
        }
    }
    return;
}

// Print the winner of the election
void print_winner(void)
{
    for (int i = 0; i < candidate_count; i++)
    {
        for (int j = 0; j < candidate_count; j++)
        {
            if (locked[j][i] == false)
            {
                printf("%s\n", candidates[i]);
            }
        }
    }
    return;
}

When I run it through Check50, a built-in checker to see if my code has fulfilled the question's requirements, this is what I get:

:) tideman.c exists
:) tideman compiles
:) vote returns true when given name of candidate
:) vote returns false when given name of invalid candidate
:) vote correctly sets rank for first preference
:) vote correctly sets rank for all preferences
:) record_preferences correctly sets preferences for first voter
:) record_preferences correctly sets preferences for all voters
:) add_pairs generates correct pair count when no ties
:) add_pairs generates correct pair count when ties exist
:) add_pairs fills pairs array with winning pairs
:) add_pairs does not fill pairs array with losing pairs
:) sort_pairs sorts pairs of candidates by margin of victory
:) lock_pairs locks all pairs when no cycles
:( lock_pairs skips final pair if it creates cycle
    lock_pairs did not correctly lock all non-cyclical pairs
:) lock_pairs skips middle pair if it creates a cycle
:) print_winner prints winner of election when one candidate wins over all others
:) print_winner prints winner of election when some pairs are tied

I cannot wrap my head around where the flaw in my logic is.

PS If anyone can take the time to explain to me, in terms of efficiency and perhaps "design of code", when recursion is preferred to iteration and vice versa, that would be greatly appreciated as well!

I was struggling with this function to. Basicly in this recursion function you must have return TRUE if one of the vinners will be equal to loser, than all circle is locked. But you are checking visited array instead, I also was going by "visited" path, and it took me some time to look another way:)

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