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

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;
}

Related

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

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.

How to order a list of integers from greatest to least?

I have an assignment where I must use a structure to put in student information. I must then order the credit hours from greatest to least. I am focused on the integer ordering loop, I just can't figure out why my program is outputting incorrectly.
#include <stdlib.h>
#include <stdio.h>
struct Student {
char name[21];
int credits;
} s[99];
int main()
{
int students;
int tempCred = 0;
char tempName[21];
printf("How many students?: ");
scanf_s("%d", &students);
for (int i = 0; i < students; i++)
{
printf("\nStudent Name?: ");
scanf_s("%s", &s[i].name, 21);
printf("\nCredits Taken?: ");
scanf_s("%d", &s[i].credits);
}
for (int i = 0; i < students; i++) {
for (int j = 0; j < students; j++) {
if (s[j].credits > tempCred) {
tempCred = s[j].credits;
s[i].credits = s[j].credits;
s[j].credits = tempCred;
}
}
printf("\n%d", s[i].credits);
}
}
For example, if I were to enter 2,6, and 8 when asked for credit hours, the program would output '8,6,8'. I am not unfamiliar with sorting, but for some reason something isn't making sense when I look at this code. Could anyone help me order the integers from greatest to least? Thanks!
NOTE: I am aware there are better ways to do this, but my professor is making us use strictly C, no C++ at all. I just need help ordering the integers.
There are various techniques used for sorting. For instance the bubble sort, quick sort, insertion sort, etc. The simplest one is the bubble sort - but it's not the most efficient one.
In your program you have an array of structs. You've done the inserting part of the structs into the array and that's fine. The problem lies in the second part - the sorting. You have a for loop that starts at the very first element (i.e. 0) and goes all the way up to the last element (i.e. students-1). And nested inside this loop is another for loop - that also has the same range???
No, that's wrong. Instead replace the first and second for loops with this:
for (int i = 0 ; i < students-1 ; i++)
{
for (int j = i+1 ; j < students ; j++)
{
...
}
}
Here, the outer for loop begins with element 0 and goes up to the element before the last. The inner for loop starts with the next element to what the outer for loop stores (i.e. j = i + 1). So if i = 0, j = 1. And this loop goes all the way up to the last element of the array of structs.
Now, inside the inner for loop specify the condition. In your case you want them sorted in descending order (highest to lowest) of the credits.
for (int i = 0 ; i < students-1 ; i++)
{
for (int j = i+1 ; j < students ; j++)
{
if(s[j].credits > s[i].credits) // then swap the credits
{
tempCred = s[j].credits ;
s[j].credits = s[i].credits ;
s[i].credits = tempCred ;
}
}
}
Note that j is one greater that i. So if i = 0, j = 1, then the if statement reads
If the credits held in the struct in element 1 of the array is greater than the credits stored in the struct in element 0 of the array, then...
If the condition is met, the credits in these 2 structs are swapped.
This an implementation of the "bubble sort". See this for more techniques and explanations.
Finally, you can display the credits:
for(int index = 0 ; index < students ; index++)
{
printf("\n%d", s[index].credits) ;
}
Like a lot of people in the comments have said, use debugger. It'll help you trace the logic of your programs.
Like #Barmar said use the qsort() function from glibc.
Not only is easier than writting your own method but it is much faster at O(N log N) on average.

Implementing sorting algoritms on a struct [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
I am writing a program that calculates the winner of an election using the Tideman electorial system.
I have defined a struct called pair, in which two candidates are compared. The index of the candidate with the most votes is represented by winner and the candidate with the least votes by loser.
// Each pair has a winner, loser
typedef struct
{
int winner;
int loser;
}
pair;
these pairs are stored in a pairs array
pair pairs[MAX * (MAX - 1) / 2];
I am trying to sort this pairs array in decreasing order of strength of victory (defined as vote count for winner minus vote count for loser).
candidates are indexed into vote_count via their candidate index and the elements return their vote count. MAX pertains to the maximum number of candidates.
int vote_count[MAX]; // where i is the index of the candidate and the return value is the number of votes.
Here is my implementation of a selection sort:
int max_idx; // index of element with the highest strength of victory
// one by one move boundary of unsorterd subarray
for (int i = 0; i < array_size - 1; i++)
{
max_idx = i;
for (int j = i + 1; j < array_size; j++)
{
if (vote_count[pairs[j].winner] - vote_count[pairs[j].loser] > vote_count[pairs[i].winner] - vote_count[pairs[i].loser])
{
max_idx = j;
}
}
if (max_idx != i)
{
// swap the element with the highest strength of victory with the first element
swap(&pairs[max_idx], &pairs[i]);
}
}
return;
And here is my implementation of bubble sort:
for (int i = 0; i < array_size-1; i++)
{
for (int j = 0; j < array_size-i-1; j++)
{
if (vote_count[pairs[j].winner] - vote_count[pairs[j].loser] > vote_count[pairs[j+1].winner] - vote_count[pairs[j+1].loser])
{
swap(&pairs[j], &pairs[j+1]);
}
}
}
return;
Each call the swap function:
void swap(pair *xp, pair *yp)
{
pair temp = *xp;
*xp = *yp;
*yp = temp;
}
The vote_count array is filled during the call to another function, vote:
// Update ranks given a new vote
bool vote(int rank, string name, int ranks[])
{
// iterate through candidates
for (int i = 0; i < candidate_count; i++)
{
if (strcmp (name, candidates[i]) == 0) // if vote is for a valid candidate
{
// update rank array
ranks[rank] = i;
vote_count[i]++;
return true;
}
}
// if no candidate is found
return false;
}
Neither the selection sort or bubble sort is working for me, please let me know where i'm going wrong.
This does not exactly answer your question as asked, but you could create a comparator function and use qsort:
/* Vote count needs to be global or file static for this to work */
int vote_count[...];
...
int compare_pair(const void *p1, const void *p2)
{
int d1 = vote_count[((const pair *)p1)->winner] - vote_count[((const pair *)p1)->loser];
int d2 = vote_count[((const pair *)p2)->winner] - vote_count[((const pair *)p2)->loser];
return d2 - d1;
}
You could write this as a one-liner if you wanted, but it would be harder to read.
The sort then becomes
#include <stdlib.h>
...
qsort(pairs, MAX * (MAX - 1) / 2, sizeof(pair), compare_pair);

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.

Why isn't this program showing any number above 3?

Wrote this to find the prime numbers between 2 to 1000. But it stops after showing that 2 and 3 are prime numbers. I know I can find how to write a code for finding out prime numbers on the internet. But I really need to know what's going wrong here.
#include <stdio.h>
main() {
int i, j;
int ifPrime = 1;
for (i = 2; i < 1000; i++) {
for (j = 2; j < i; j++) {
if (i % j == 0) {
ifPrime = 0;
break;
}
}
if (ifPrime == 1) {
printf("%d is prime\n", i);
}
}
}
The line
int ifPrime=1;
must be inside the outer for loop. There it will be initialized for every i. This corresponds to the natural language words "to check whether a number i is prime, first assume it is. Then check if it is divisible". The code you had before said "to check whether the numbers 2 to 1000 are prime, first assume they are", and this wording was too broad.
The code should be:
int main()
{
for (int i = 2; i < 1000; i++)
{
int ifPrime = 1;
for (int j = 2; j < i; j++)
I replaced main with int main since that is required since 20 years. (You should not learn programming from such old books.)
I moved the int i and the int j into the for loops so that you cannot accidentally use these variables outside the scope where they have defined values.
To avoid this bug in the future, it's a good idea to extract the is_prime calculation into a separate function. Then you would have been forced to initialize the ifPrime in the correct place.
Another way of finding the cause of this bug is to step through the code using a debugger and ask yourself at every step: does it still make sense what the program is doing?
You are not setting ifPrime back to 1 after checking for the single number. So once you get a number that is non_prime, ifPrime is now 0 and hence if(ifPrime == 1) would never return true post that and hence you only see 2, 3 as prime
#include <stdio.h>
int main(void) {
for( int i=2;i<1000;i++)
{
int ifPrime = 1;
for(int j=2;j<i;j++)
{
if(i%j==0)
{
ifPrime=0;
break;
}
}
if(ifPrime==1)
{
printf("%d is prime\n",i);
}
}
return 0;
}

Resources