CS50 plurality - unable to print if there are too many winners - c

In plurality prblm, I managed to update votes for each candidate, my code can print the one winner, but still stuck if they are many winners .
help by hints or clues, not the whole solution.
Thanks in advance.
void print_winner(void)
{
int v = 0; //maximum number of votes
string w; //winner of the election
for (int i = 0; i < candidate_count; i++)
{
if (v <= candidates[i].votes)
{
v = candidates[i].votes;
}
}
for (int j = 0; j < candidate_count; j++)
{
if (candidates[j].votes == v)
{
w = candidates[j].name;
}
}
printf("%s\n", w);
return;
}

Change your second for loop to this:
for (int j = 0; j < candidate_count; j++)
{
if (candidates[j].votes == v)
{
w = candidates[j].name;
printf("%s\n", w);
}
}

You need to separate it into two for loops, if you have them together the first loop will run only through the first candidate with more than 1 vote and print it. Since it has not checked the others. That loops must finish and go through the whole set to actually find the max number of votes.

I am also on the same problem with you for around 2 or 3 days now. But I think I found out how to print if there are multiple winners.
First, I have a variable to store the highest votes and initialized it to 0. Then I tried comparing the candidates[i].votes against the current highest vote.
Then, I assigned the new value of the highest vote with the candidates vote until the loop finds someone who is higher than the previous one.
Lastly, I looped again through all the candidates and checks each candidates votes if they are equal to the highest vote. If yes, then their names should be printed. This solved my problem in printing 2 or more winners.

Related

Cs50 plurality problem - Does not work in only 1 check

I made my code for the problem set 3 "plurality". When I check it, all answers are green, but there's only a problem with the "bob" answer
Here's the code, I include only the part with the function I implemented.
It's about a voting. In this function (after counting the votes for each candidate) you need to check who got the most votes and print the names. The function should take into consideration multiple winners. when I check the answer with the program given by cs50 all answers are red, only one answer marked as Bob but it doesn't explain what's the problem (in the previous problem set it did)
// Print the winner (or winners) of the election
void print_winner(void)
{
// Compare votes to select to biggest
for (int i = 0; i < (candidate_count -1); i++)
{
for (int j = 0; j < (candidate_count - 2); j++)
{
// swap bigger number and name to last index
if (candidates[i].votes > candidates[candidate_count - 1].votes)
{
candidates[candidate_count - 1].votes = candidates[i].votes;
candidates[candidate_count - 1].name = candidates[i].name;
}
}
}
// Print every candidates that have same votes as votes of last index
for (int i = 0; i < candidate_count; i++)
{
if (candidates[i].votes == candidates[candidate_count - 1].votes)
{
printf("%s\n", candidates[i].name);
}
}
return;
}
What's the problem?

check50 on plurality insists that it doesn't work yet everytime i try with my own input it does work

The program in question gets candidate names and voters choices as input and the output is the name of the winner or winners(in case there is a tie between 2 or mre candidates).
whenever I try using my own input it works fine, be it a single winner or multiple.
yet check50 disagrees. when I use the debugger it says in the end something about segmentation fault but I can't see where in the code is the error.
I'm having a problem understanding why check50 insists the program isn't fully correct.
The part of the code which the problem is in is:
void print_winner(void)
{
int c = 0;
int d[(candidate_count - 1)];
int e = 0;
for (int i = 1; i < candidate_count; i++)
{
if (candidates[c].votes < candidates[i].votes)
{
c = i;
}
else if (candidates[c].votes == candidates[i].votes)
{
d[e] = i;
e++;
}
}
printf("%s\n", candidates[c].name);
for(int i = 0; i < (e + 1); i++)
{
if(candidates[c].votes == candidates[(d[i])].votes)
{
printf("%s\n", candidates[d[i]].name);
}
}
return;
}
the results are:
:) plurality.c exists
:) plurality compiles
:) vote returns true when given name of first candidate
:) vote returns true when given name of middle candidate
:) vote returns true when given name of last candidate
:) vote returns false when given name of invalid candidate
:) vote produces correct counts when all votes are zero
:) vote produces correct counts after some have already voted
:) vote leaves vote counts unchanged when voting for invalid candidate
:) print_winner identifies Alice as winner of election
:) print_winner identifies Bob as winner of election
:) print_winner identifies Charlie as winner of election
:) print_winner prints multiple winners in case of tie
:( print_winner prints all names when all candidates are tied
print_winner function did not print all three winners of election
From:
d[e] = i;
e++;
we see that e is one greater than the index of the last element set in d.
Then:
for(int i = 0; i < (e + 1); i++)
{
if(candidates[c].votes == candidates[(d[i])].votes)
executes an iteration in which i is e and so d[i] is d[e], which accesses an element beyond the last element set in d. This value is uninitialized and may behave as if it has a very large positive or negative value (thus causing a segment fault as the expression tries to access memory out of bounds) or a small value that results in accessing some unwanted element in candidates (thus printing an incorrect name), or it may have some other undesired behavior in your program.
Change the for statement to:
for (int i = 0; i < e; i++)
Segmentation fault happens when the program tries to access an invalid memory address, e.g. if you try to access a[10] of a vector int a[10]. Note that indexing in C is zero-based, which means 10 is the 11th element. And it is happening in some parts of your code:
int d[candidate_count - 1];
...
for (int i = 1; i < candidate_count; i++)
{
if (candidates[c].votes < candidates[i].votes)
...
When i = candidate_count - 1 in last iteration, the index i is out of the limits of the vector which is has size candidate_count-1. You can try something like:
int d[candidate_count];
...
for (int i = 0; i < candidate_count; i++)
{
if (candidates[c].votes < candidates[i].votes)
...
See this Wikipedia article about segfault

cs50 tideman (converting information from one array into another larger one)

I have completed and submitted the easier version of the problem set for the week I'm on, I'm just interested in finishing the harder version for the learning experience, but I'm stuck on what seems like the crux of it. Basically you are supposed to implement a voting system that records all the ranks of each of the voters. So for instance let's say you have 3 candidates (A, B, and C). If the first voter chooses candidate C as their first choice, and A as their second, (B as their last), the orginal array you would have looks like this [2,0,1] signifying that the the third candidate is the first choice, the first candidate is the second choice, and the second candidate is the third choice. The code I've used for this is the following:
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;
for( int j = 0; j < candidate_count; j++)
{
printf("%i\n", ranks[j]);
}
return true;
}
}
return false;
}
But then we are asked to convert it into a 2d array that compares each of the candidates in basically a 1vs1 battle. So in our ABC example, the array should convert from [2,0,1] to [[0,1,0],[0,0,0],[1,1,0]]
where the first array represents the first candidate and how they did against the 2nd and 3rd respectively, etc. (the first location in the first array should always be 0 because you cant compare the candidate to how many votes they had against themself, same with the middle spot in the second array and the last spot in the last array). In other words array[i][j] represents how candidate i did against candidate j. You are meant to work this function out using the array returned from the vote function.
I know that it will involve another nested loop, possibly 3 layers. I need a point in the right direction. I've made a bunch of different tweaks to a function that looks like this but I know they've all been wrong because I'm resorting to trial and error rather than logic because the logic has defeated me at this point. Maybe this forum isn't really meant for logical help but I'd still like to work this out on my own without being given the answer. Anyway here's the latest version of the function that I've been helplessly tinkering with.
void record_preferences(int ranks[])
{
printf("\n");
for (int i = 0; i < candidate_count; i++)
{
for (int j = 0; j < candidate_count; j++)
{
for (int k = 0; k < candidate_count; k++)
{
if((ranks[k] > i && ranks[k] < j) || (ranks[k] < i && ranks[k] > j))
{
preferences[i][j] += 1;
}
}
printf("%i\n", preferences[i][j]);
}
}
return;
}
Keep in mind I've already gotten my grade for this week and I don't plan on submitting this work so it's not cheating even if you do straight up tell me the answer, but I'd prefer if you didn't. I know a lot of people are of the belief that you need to work the logic out yourself otherwise you're not really learning, which I kind of get, but at the same time you can only bang your head against a wall so many times before seeking help.
Thanks.
Edit: here is a link to a more clear explanation. The function in question starts around the 7:30 mark. https://www.youtube.com/watch?v=kb83NwyYI68&t=492s&ab_channel=CS50
Using the term vote instead of ranks for consistency with the video. It is also clearer as we also need to deal with the plural form votes.
The solution is a little long but the key insight is function rank() which returns the position of candidate for a given vote. We call the rank() for candidate i and j and incrementpreferences[i][j] if i ranks higher (<) than j.
Introduced enum candidates for readability (used candidate names from the video just to have a 2nd test case; comment out definition of QUESTION). This will also give you a compile error if you misspell a candidate name.
The code assumes that all candidates are ranked. It's straight forward to decouple number of candidates and the number of candidates that are being ranked.
#include <stdio.h>
#define LEN(a) (sizeof(a) / sizeof(*a))
enum candidates {
Alice,
Bob,
Charlie
};
void preferences_print(size_t candidates_len, unsigned preferences[candidates_len][candidates_len]) {
for(size_t i = 0; i < candidates_len; i++) {
for(size_t j = 0; j < candidates_len; j++) {
printf("%u%s", preferences[i][j], j + 1 < candidates_len ? ", " : "");
}
printf("\n");
}
}
unsigned rank(size_t candidates_len, unsigned vote[candidates_len], enum candidates candidate) {
for(size_t i = 0; i < candidates_len; i++) {
if(vote[i] == candidate) return i;
}
printf("Error: skipping invalid cadidate %d", candidate);
return 0;
}
void votes_to_preferences(size_t votes_len, size_t candidates_len, enum candidates votes[votes_len][candidates_len], unsigned preferences[candidates_len][candidates_len]) {
for(size_t v = 0; v < votes_len; v++) {
for(size_t i = 0; i < candidates_len; i++) {
for(size_t j = 0; j < candidates_len; j++) {
preferences[i][j] +=
rank(candidates_len, votes[v], i) <
rank(candidates_len, votes[v], j);
}
}
}
}
int main() {
enum candidates votes[][3] = {
#define QUESTION
#ifdef QUESTION
{ Charlie, Alice, Bob },
#else // VIDEO
{ Alice, Charlie, Bob },
{ Alice, Charlie, Bob },
{ Charlie, Alice, Bob },
{ Bob, Alice, Charlie }
#endif
};
unsigned preferences[LEN(*votes)][LEN(*votes)] = { { 0 } };
votes_to_preferences(LEN(votes), LEN(*votes), votes, preferences);
preferences_print(LEN(*votes), preferences);
return 0;
}

CS50 pset3: Plurality. Print_winner function incorrectly printing multiple candidates regardless of input when running the program

CS50's pset3 plurality requires you to print the winners of the "election" based on the number of votes they receive. Votes are input by the user.
My program's working fine up until I have to print the winners. I've been at it for a few days trying different methods but can't get my head around it.
My code currently outputs multiple candidates regardless of the input. I'm guessing that the variables "most_votes" and "election_winner" are retaining the assigned value from the top of the code.
How can I rewrite this to print a single winner if they receive the most votes and multiple winners if multiple candidates receive an equal number of votes?
Thanks for any help!
Here's my code:
// Print the winner (or winners) of the election
void print_winner(void)
{
int most_votes = candidates[0].votes;
string election_winner = candidates[0].name;
// Loop over each candidate
for (int i = 1; i < candidate_count; i++)
{
// Count how many votes each candidate received and determine who
// received the most votes
if (most_votes < candidates[i].votes)
{
most_votes = candidates[i].votes;
election_winner = candidates[i].name;
}
}
// Loop over candidates a second time
for (int j = 1; j < candidate_count; j++)
{
// Count how many votes each candidate received
// Compare number of votes of each candidate
// Check if mutiple candidates have the most votes
if (most_votes == candidates[j].votes && election_winner != candidates[j].name)
{
// Print all candidates with the most votes
printf("%s\n", candidates[j].name);
}
}
// Print the name of winner with most votes
printf("%s\n", election_winner);
return;
}
You do not need (and shouldn't have) the variable election_winner, if there can be multiple winners.
In the first loop only determine the value for most_votes.
Loop over the candidates a second time (starting from 0, not 1 as you are currently doing). If the vote count of the candidate equals most_votes, then you know that the candidate is a winner, and you may print the name out, or whatever else is required.
Thanks for the help guys I was able to figure it out. Simplified it as #vmt suggested and worked!
Changing the variable "most_votes" to "0" instead of "candidates[0].votes" made it easier to figure out.
// Print the winner (or winners) of the election
void print_winner(void)
{
int most_votes = 0;
// Loop over each candidate
for (int i = 0; i < candidate_count; i++)
{
// Count which candidate has the most votes and assign them most_votes
if (most_votes < candidates[i].votes)
{
most_votes = candidates[i].votes;
}
}
// Loop again over candidates
for (int j = 0; j < candidate_count; j++)
{
// Check if mutiple candidates have the most votes
if (most_votes == candidates[j].votes)
{
// Print all candidates with the most votes
printf("%s\n", candidates[j].name);
}
}
return;
}

find the n largest elements in an array

I have an array and I need to find n tie cases in the array for example {1,2,3,3} I need the program to return both 3's
void print_winner(void)
{
// TODO
string arr[9];
string name = "";
int largest = 0;
for (int i = 0; i < voter_count; i++)
{
if (largest<candidates[i].votes)
{
largest = candidates[i].votes;
name = candidates[i].name;
}
}
// arr[0] = name;
printf("%s\n", name);
return;
}
in this code candidates is a struct with two attributes: name and votes
I need the program to print the name with the highest number of votes even if there is a 3-way tie
I was thinking I would traverse the list find the largest int and then remove that int and traverse the list again to see if any elements equal the largest element from the original list and if so add the name to the array and in the end print all the name(s)
An approach in programming that is often good is to divide the problem up and solve its separate parts.
In this case, one way of setting up the problem is to print the names of all those that have the highest score. But that problem is somewhat complex.
An alternative way of setting up the problem would be as follow:
Find the maximum score.
After having found the maximum score, print the names of all those that have the highest score.
Each of these sub-problems are easier, and should together solve the problem.
I much prefer teaching others how to fish, so I don't want to spoil or ruin your chances for learning and improving and becoming awesome by implementing the solution for you in code. You are more than welcome to ask for clarification, however, I very much like to help :).
I think u just need to loop the array again after you find the candidate with max votes, to look for if there is another candidate or more with same no. of votes.No need to remove records.
Until you passed every vote count, you do not know the largest vote count.
The currently largest's name would be needed to be corrected when a really larger largest is found. So do:
void print_winner_1(void)
{
// globals: candidates: array
// voter_count: size of array
int largest = 0;
for (int i = 0; i < voter_count; i++)
{
if (largest < candidates[i].votes)
{
largest = candidates[i].votes;
}
}
for (int i = 0; i < voter_count; i++)
{
if (largest == candidates[i].votes)
{
printf("%s\n", candidates[i].name);
}
}
}
The above could store a largest_first_i for a small speed improvement.
Collecting intermediary results in full:
void print_winner_2(void)
{
// globals: candidates: array
// voter_count: size of array
string names[9]; // Tie names.
int name_count = 0;
int largest = 0;
for (int i = 0; i < voter_count; i++)
{
if (largest < candidates[i].votes)
{
name_count = 0; // Reset list of ties.
largest = candidates[i].votes;
}
if (largest == candidates[i].votes)
{
if (name_count == 9) { // More than 9 temporary (!) ties.
print_winner_1();
return;
}
names[name_count++] = candidates[i].name;
}
}
for (int i = 0; i < name_count; i++)
{
printf("%s\n", names[i]);
}
}
I did one with two full loops, and one collecting the ties immediately.
The second solution is prone to overflow of the result array (if there are more than 9 candidates), say in the case of [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 23 23] the first intermediary tie would overflow for largest == 0.
Also the second need not be faster, as you need to store into names and for every increase of largest. Almost a case of premature optimization.

Resources