2021-04-17

文章目录


前言

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函数需要实现的功能有二:

  1. 判断用户投票是否有效,即判断输入的名称是否在候选人中;
  2. 记录每一个(第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;
}

总结

上一篇:[ 回溯 ]组合总和II


下一篇:python-shutil学习