文章目录
前言
CS50是由美国哈佛大学和耶鲁大学联合开发的计算机基础公选课。该日志作者是正在学习课程的初学者,特作此日志记录并总结学习上的收获。成果代码是通过CS50IDE的编译功能make
、执行功能./
和CS50课程配套练习的检验来完成的,因网络原因无法使用check50
或者提交功能进行更加专业的正午检验,所有代码均有可以探讨和指正的地方,还请经验丰富的先行者不吝赐教。
本日志可以作为学习交流的媒介,但如果您是和我一样的CS50课程初学者,该日志可能无法帮助到您,CS50的官网讲解和配套的Walkthrough会有更大的帮助。
启动这篇文章时没有学习完所有内容和填充所有课程的日志,因日志的个人性质,会陆续更新至学习结束。知识产权声明
本篇文章涉及到哈佛大学CS50课程的成果讨论,如侵犯到您的利益,请及时联系我做修改或删除的处理。
Week 0 Scratch
Week 1 C
Week 2 Arrays
Week 3 Algorithms
Lab 3
Sort
Problem Set 3
Plurality
概述
Plurality在Collins词典的翻译是
If a candidate, political party, or idea has the support of a plurality of people, they have more support than any other candidate, party, or idea.
简单来说意思就是在投票中以小服大的问题——在投票中得票数多的候选人(candidate)胜出。经过课程和指示学习,以下是成功运行的代码展示。
CS50课程作业已经给了学生distribution code,要求学生完成的部分仅为自定义功能函数bool vote(string name)
和void print_winner(void)
。
#include <cs50.h>
#include <stdio.h>
#include <string.h>
// Max number of candidates
#define MAX 9
// Candidates have name and vote count
typedef struct
{
string name;
int votes;
}
candidate;
// Array of candidates
candidate candidates[MAX];
// Number of candidates
int candidate_count;
// Function prototypes
bool vote(string name);
void print_winner(void);
int main(int argc, string argv[])
{
// Check for invalid usage
if (argc < 2)
{
printf("Usage: plurality [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].name = argv[i + 1];
candidates[i].votes = 0;
}
int voter_count = get_int("Number of voters: ");
// Loop over all voters
for (int i = 0; i < voter_count; i++)
{
string name = get_string("Vote: ");
// Check for invalid vote
if (!vote(name))
{
printf("Invalid vote.\n");
}
}
// Display winner of election
print_winner();
}
// Update vote totals given a new vote
bool vote(string name)
{
// TODO
// If string name belongs to candidates[].name, implement candidates[].votes++
for (int i = 0; i < candidate_count; i++)
{
if (strcmp(candidates[i].name, name) == 0)
{
candidates[i].votes++;
return true;
}
}
return false;
}
// Print the winner (or winners) of the election
void print_winner(void)
{
// TODO
int most_votes = candidates[0].votes;
for (int i = 1; i < candidate_count; i++)
{
if (candidates[i].votes > most_votes)
{
most_votes = candidates[i].votes;
}
}
for (int i = 0; i < candidate_count; i++)
{
if (candidates[i].votes == most_votes)
{
printf("%s\n", candidates[i].name);
}
}
// Start loop
return;
}
vote(string name)
bool vote(string name)
{
// TODO
// If string name belongs to candidates[].name, implement candidates[].votes++
for (int i = 0; i < candidate_count; i++)
{
if (strcmp(candidates[i].name, name) == 0)
{
candidates[i].votes++;
return true;
}
}
return false;
}
print_winner(void)
:
void print_winner(void) {
// TODO
int most_votes = candidates[0].votes;
for (int i = 1; i < candidate_count; i++)
{
if (candidates[i].votes > most_votes)
{
most_votes = candidates[i].votes;
}
}
for (int i = 0; i < candidate_count; i++)
{
if (candidates[i].votes == most_votes)
{
printf("%s\n", candidates[i].name);
}
}
// Start loop
return; }
Runoff
概述
Runoff实现的功能是在一定程度上弥补Plurality选票制度的不足,比其更有公平性。Runoff通过每一个voter的投票偏好(perferences rank)进行多次淘汰。
其中要注意的是在CS50的课程中,简化了问题的复杂程度——即不会考虑偏好比候选人数少的情况。
完成代码展示
CS50课程作业已经给了学生distribution code,要求学生完成的部分仅为预定义功能函数vote(int voter, int rank, string name)
、tabulate(void)
、print_winner(void)
、find_min(void)
、is_tie(int min)
和eliminate(int min)
。
#include <cs50.h>
#include <stdio.h>
#include <string.h>
// Max voters and candidates
#define MAX_VOTERS 100
#define MAX_CANDIDATES 9
// preferences[i][j] is jth preference for voter i
int preferences[MAX_VOTERS][MAX_CANDIDATES];
// Candidates have name, vote count, eliminated status
typedef struct
{
string name;
int votes;
bool eliminated;
}
candidate;
// Array of candidates
candidate candidates[MAX_CANDIDATES];
// Numbers of voters and candidates
int voter_count;
int candidate_count;
// Function prototypes
bool vote(int voter, int rank, string name);
void tabulate(void);
bool print_winner(void);
int find_min(void);
bool is_tie(int min);
void eliminate(int min);
int main(int argc, string argv[])
{
// Check for invalid usage
if (argc < 2)
{
printf("Usage: runoff [candidate ...]\n");
return 1;
}
// Populate array of candidates
candidate_count = argc - 1;
if (candidate_count > MAX_CANDIDATES)
{
printf("Maximum number of candidates is %i\n", MAX_CANDIDATES);
return 2;
}
for (int i = 0; i < candidate_count; i++)
{
candidates[i].name = argv[i + 1];
candidates[i].votes = 0;
candidates[i].eliminated = false;
}
voter_count = get_int("Number of voters: ");
if (voter_count > MAX_VOTERS)
{
printf("Maximum number of voters is %i\n", MAX_VOTERS);
return 3;
}
// Keep querying for votes
for (int i = 0; i < voter_count; i++)
{
// Query for each rank
for (int j = 0; j < candidate_count; j++)
{
string name = get_string("Rank %i: ", j + 1);
// Record vote, unless it's invalid
if (!vote(i, j, name))
{
printf("Invalid vote.\n");
return 4;
}
}
printf("\n");
}
// Keep holding runoffs until winner exists
while (true)
{
// Calculate votes given remaining candidates
tabulate();
// Check if election has been won
bool won = print_winner();
if (won)
{
break;
}
// Eliminate last-place candidates
int min = find_min();
bool tie = is_tie(min);
// If tie, everyone wins
if (tie)
{
for (int i = 0; i < candidate_count; i++)
{
if (!candidates[i].eliminated)
{
printf("%s\n", candidates[i].name);
}
}
break;
}
// Eliminate anyone with minimum number of votes
eliminate(min);
// Reset vote counts back to zero
for (int i = 0; i < candidate_count; i++)
{
candidates[i].votes = 0;
}
}
return 0;
}
// Record preference if vote is valid
bool vote(int voter, int rank, string name)
{
// TODO
for (int i = 0; i < candidate_count; i++)
{
if (strcmp(candidates[i].name, name) == 0)
{
preferences[voter][rank] = i;
return true;
}
}
return false;
}
// Tabulate votes for non-eliminated candidates
void tabulate(void)
{
// TODO
for (int i = 0; i < voter_count; i++)
{
for (int j = 0; j < candidate_count; j++)
{
if (candidates[preferences[i][j]].eliminated == false)
{
candidates[preferences[i][j]].votes++;
break;
}
}
}
return;
}
// Print the winner of the election, if there is one
bool print_winner(void)
{
// TODO
for (int i = 0; i < candidate_count; i++)
{
if (candidates[i].votes > (voter_count / 2))
{
printf("%s\n", candidates[i].name);
return true;
}
}
return false;
}
// Return the minimum number of votes any remaining candidate has
int find_min(void)
{
// TODO
for (int i = 0, j = 0, min_votes = 0; i < candidate_count; i++)
{
if (candidates[i].eliminated == false)
{
j++;
if (j == 1)
{
min_votes = candidates[i].votes;
}
if (candidates[i].votes <= min_votes)
{
min_votes = candidates[i].votes;
}
}
if (i == candidate_count - 1)
{
return min_votes;
}
}
return 0;
}
// Return true if the election is tied between all candidates, false otherwise
bool is_tie(int min)
{
// TODO
int j = 0;
int n = 0;
for (int i = 0; i < candidate_count; i++)
{
if (candidates[i].eliminated == false)
{
n++;
if (candidates[i].votes == min)
{
j++;
}
}
}
if (n == j)
{
return true;
}
return false;
}
// Eliminate the candidate (or candidates) in last place
void eliminate(int min)
{
// TODO
for (int i = 0; i < candidate_count; i++)
{
if (candidates[i].eliminated == false)
{
if (candidates[i].votes == min)
{
candidates[i].eliminated = true;
}
}
}
return;
}
学生完成部分
各个预定义函数为学生通过代码功能提示创作,Runoff中的自定义函数的功能和灵感是作者在CS50及其练习配套讲解Walkthrough的指导下完成的。
bool vote(int voter, int rank, string name)
首先了解到vote函数的类型是bool(布尔型变量),是逻辑型变量的定义符。就目前的学习深度而言,可以了解到vote函数返回的值是真或者假,即true或者false。
vote
函数需要实现的功能有二:
- 判断用户投票是否有效,即判断输入的名称是否在候选人中;
- 记录每一个(第
voter
个)投票人(voter)的投票中每一个喜好排名(rank
)所对应的名字(name
)。
实现代码如下。
bool vote(int voter, int rank, string name)
{
// TODO
for (int i = 0; i < candidate_count; i++)
{
if (strcmp(candidates[i].name, name) == 0)
{
preferences[voter][rank] = i;
return true;
}
}
return false;
}
值得注意的是
if (strcmp(candidates[i].name, name) == 0)
使用到了strcmp
,所以需要我们在头文件说明中添加#include <string.h>
进行调用。
void tabulate(void)
tabulate
函数的功能即为每一轮投票计数,在功能中使用了if (candidates[i].eliminated)
判断第i
个候选人(candidate
)是否参与计票,如果不计票,则将该投票人的这一票传递给该投票人的第二喜好。
实现代码如下。
void tabulate(void)
{
// TODO
for (int i = 0; i < voter_count; i++)
{
for (int j = 0; j < candidate_count; j++)
{
if (candidates[preferences[i][j]].eliminated == false)
{
candidates[preferences[i][j]].votes++;
break;
}
}
}
return;
}
新的学习:break
这次编程在代码中使用了break
,可以有效跳出当前循环体。在这里可以避免计票所有输入的偏好。
bool print_winner(void)
功能同函数类型及名称一样,返回真假并打印出胜选人。
实现代码如下。
bool print_winner(void)
{
// TODO
for (int i = 0; i < candidate_count; i++)
{
if (candidates[i].votes > (voter_count / 2))
{
printf("%s\n", candidates[i].name);
return true;
}
}
return false;
}
int find_min(void)
find_min(void)
需要找到当前轮次的最小值,并返回最小值。
实现代码如下。
int find_min(void)
{
// TODO
for (int i = 0, j = 0, min_votes = 0; i < candidate_count; i++)
{
if (candidates[i].eliminated == false)
{
j++;
if (j == 1)
{
min_votes = candidates[i].votes;
}
if (candidates[i].votes <= min_votes)
{
min_votes = candidates[i].votes;
}
}
if (i == candidate_count - 1)
{
return min_votes;
}
}
return 0;
}
bool is_tie(int min)
bool is_tie(int min)
需要判断当前轮次的所有未被淘汰的候选人(candidates[i].eliminated = false
)得票数是否相同,即平局(tie)。
实现代码如下。
bool is_tie(int min)
{
// TODO
int j = 0;
int n = 0;
for (int i = 0; i < candidate_count; i++)
{
if (candidates[i].eliminated == false)
{
n++;
if (candidates[i].votes == min)
{
j++;
}
}
}
if (n == j)
{
return true;
}
return false;
}
void eliminate(int min)
void eliminate(int min)
解决的是淘汰问题,机制很简单,就是把当前轮次得票数最少的候选人的candidates[i].eliminated
的值由false
变为true
,实现代码如下。
void eliminate(int min)
{
// TODO
for (int i = 0; i < candidate_count; i++)
{
if (candidates[i].eliminated == false)
{
if (candidates[i].votes == min)
{
candidates[i].eliminated = true;
}
}
}
return;
}