알고리즘에 관한 3주차 과제 3가지입니다!

Sort, Plurality, Runoff 로 구성되어 있고, 각각의 문제 해결 방법과 코드, 중요한 부분들을 정리해보겠습니다.

 

 

누가 Tideman 고르겠냐구요... ??? very..very..very 아주가 3개나 들어간

 

사실 쉬운 버전과 어려운 버전 중에 1주차에는 호기롭게 어려운 Credit을 골랐다가 된통 혼났더랬죠...

쉬운 버전이 쉬운 버전이 아님.. Credit에도 한 여섯 시간 이상은 투자하고 머리 싸맸던 것 같아요

그래서 자존심이고 공부 가치관이고 뭐고 12월 말 안에 일단 쉬운 버전으로 과제는 다 제출해놓자고 마음을 먹고.. 이후에 코딩이라는 것에 좀 익숙해졌을 때쯤 어려운 버전들을 차근차근 해 나가 보겠다는 마인드.. ㅎㅎㅎ

 

아무튼 시작!

 


 

1. Sort

 

: Analyze three sorting programs(merge sort/ selection sort/ bubble sort) to determine which algorithms they use.

 

→ cs50에서 미리 작성되어 제공된 distribution code files를 다운받아 reversed/ random/ sorted list (5000/10000/50000) 각각을 sort하는 데에 걸리는 시간을 측정, 비교하여 어떤 sort type인지 맞히는 과제

 

우선 시간을 측정할 때에는 (terminal window):

time ./sort1 reversed5000.txt

 

위의 형식으로 명령어를 입력하면, 시간을 측정해 준다. 

단순히 프로그램이 list를 sort해주고, 시간도 측정해 주기 때문에 데이터를 분석하는 일이 내 몫인데, 아래 표를 참고하면 도움이 된다.

 

  the worst case (O) the best case (Ω)
merge sort O(n log(n)) Ω(n log(n))
selection sort O(n^2) Ω(n^2)
bubble sort O(n^2) Ω(n)

 

  • sort 1 : 다른 방법들보다 대체적으로 시간이 오래 걸리며, 50000개의 경우 reversed와 sorted list의 시간이 차이가 많이 난다      
                 → O와 Ω의 차이가 큰 BUBBLE SORT
  • sort 2 : 가장 적은 시간이 걸리며, O와 Ω의 차이가 거의 없다
                → MERGE SORT
  • sort 3 : 중간 정도의 시간이 걸리며, O와 Ω의 차이가 거의 없다
                → SELECTION SORT

 

 


2. Plurality

 

: In the plurality vote, every voter gets to vote for one candidate. At the end of the election, whichever candidate has the greatest number of votes is declared the winner of the election.

Implement a program that runs a plurality election.

 

2번째 과제도 이미 cs50에 의해 일부 작성되어 있는 distribution code를 다운받아 빈칸을 채우는 형태였습니다! 

오리지널 코드는 다음과 같습니다 :

 

#define MAX 9 

: candidate candidates[MAX] 에서 사용함으로써, 변수 candidates의 최대 숫자를 9로 제한한다는 뜻

 

★ if (argc < 2)

: command-line argument에 input이 적어도 하나 있어야 한다는 뜻

 

 

 

★ candidate_count = argc - 1

: 하나를 차지하는 './plurality' 를 빼고 센 숫자가 candidate_count에 들어간다는 뜻

 

★ for loop 안의 candidates[i].name = argv[i + 1];

: i가 0에서 후보자 수만큼 증가하는 루프 안에서, './plurality'를 뺀 첫 번째 후보자의 이름부터 candidates[i].name에 들어간다는 뜻

 

 

 

 

TODO #1

voter들이 투표할 때마다 각각의 후보자의 total vote 수를 업데이트 시켜야 함.

 

 

for (int i = 0; i < candidate_count; i++)
{
	if (strcmp(name, candidates[i].name) == 0)
    {
    	candidates[i].vote ++;
        return true;
    }
}
return false;

 

★ String끼리 비교할 때는 함수 strcmp(  ,  ) == 0 사용

 

 

 

TODO #2

가장 많은 표를 받은 사람을 찾아 print 하기 (sorting 사용할 필요 x)

void print_winner(void)
{
	int highest_vote = 0;
    for (int i = 0; i < candidate_count; i++)
    {
    	if (candidates[i].votes > highest_vote)
        {
        	highest_vote = candidate[i].votes;
        }
    }
    
    for(int i = 0; i < candidate_count; i++)
    {
		if(candidates[i].votes == highest_vote)
        {
    		printf("%s\n", candidate[i].name);
        }
   }
}

 

// Find the maximum number of votes

→ candidates[i].votes를 iterate 하기 위한 for loop를 만들고, 그 속에 highest_vote와 각 후보자의 투표 수를 비교해서 큰 수를 highest_vote에 넣어 업데이트 시킨다. 

 

// Print the candidate with maximum votes

→ candidates[i].votes를 iterate 하기 위한 for loop를 만들고, 그 속에 각 후보자의 투표 수가 highest_vote와 동일하다면 해당 후보자의 이름인 candidates[i].name을 프린트하는 함수를 사용하여 문제를 해결한다.

 

 

terminal window


 

3. Runoff

 

: In an instant runoff election, voters can rank as many candidates as they wish. If any candidate has a majority (more than 50%) of the first preference votes, that candidate is declared the winner of the election. The candidate who received the fewest number of votes is eliminated from the election, and anyone who originally chose that candidate as their first preference now has their second preference considered, and so on.

 

간략히 말해서 2번째 과제와 비슷한 선거 시스템을 만드는데, 앞의 plurality와 달리 투표 순서에 따라 선호도가 만들어져 반영됩니다. 50% 이상의 지지율을 받은 후보는 바로 그 선거의 우승자가 되지만, 그렇지 않을 경우 가장 적은 표를 받은 선거는 투표에서 제거되고, 두 번째로 선호도가 높은 후보가 선거 결과에 반영됩니다. 이 방식의 장점은 투표자들의 선호를 더 잘 반영한다는 장점을 가지고 있습니다.

 

 

이번에도 사전에 cs50에 의해 작성되어 다운받은 distribution code는 다음과 같습니다 :

#include <cs50.h>
#include <stdio.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
return false;
}

// Tabulate votes for non-eliminated candidates
void tabulate(void)
{
// TODO
return;
}

// Print the winner of the election, if there is one
bool print_winner(void)
{
// TODO
return false;
}

// Return the minimum number of votes any remaining candidate has
int find_min(void)
{
// TODO
return 0;
}

// Return true if the election is tied between all candidates, false otherwise
bool is_tie(int min)
{
// TODO
return false;
}

// Eliminate the candidate (or candidates) in last place
void eliminate(int min)
{
// TODO
return;
}

 

참 긴데요.. 하나하나 뜯어보면서 같이 과제를 해결해보아요 ^~^

 

 

 

2번째 과제와 다른 부분이라고 한다면 minimum votes수를 가진 후보를 제거한다는 점에서

candidate struct 안에 bool eliminated; 가 추가되었습니다!

 

 

투표자들이 투표함에 따라서 각각에 따른 Rank를 매기고, vote 형식에 맞지 않을 경우 'Invalid vote.'를 프린트하는 부분입니다.

 

ⓐ outer loop (int i)

: 각각의 투표자들을 iterate 하는 loop

ⓑ inner loop (int j)

: 각각의 투표자들의 각각의 rank를 iterate 하는 loop

ⓒ prompt name

string name = get_string("Rank %i:", j + 1);

: j + 1 인 이유는 rank가 0이 아니라 1에서부터 시작하기 때문

 

ⓓ record vote

if (!vote(i, j, name))

: vote라는 함수를 불러 invalid 할 경우 해당 문구를 프린트

 


 

그리고 여기서부터 작성해야 하는 과제 코드 시짝!!

 

1) Vote function

bool vote(int voter, int rank, string name)
{
	 //TODO
}

: VOTE function 에서는 투표를 통해 받은 이름과 후보자의 이름이 동일할 경우, preferences array를 투표자와 rank를 반영하도록 update 해야합니다.

 

이 부분이 제일 어려웠음 무슨 말인지...???

for (int i = 0; i < candidate_count; i ++)
{
	if (strcmp (name, candidates[i].name) == 0)
    {
    	preferences[voter][rank] = i;
        return true;
    }
}
return false;

 

① 모든 후보자를 포괄하도록 후보자 수만큼 loop를 돌려주고,

② 투표를 통해 받은 이름(name)과 후보자 이름(candidates[i].name)이 동일할 경우를 strcmp 함수로 조건문을 적어주기

③ 같을 경우, preferences[voter][rank] 배열에 랭크 i를 배치해주기

 

→ 이로써, 각 투표자들의 각 이름별로 rank가 형성된 preferences[] 가 생겨나게 됨!

 

 

 

 

2) Tabulate function

void tabulate(void)
{
	//TODO
}

: Tabulate function에서는 제거되지 않은 후보자들에 대하여 각 후보자들이 받은 투표 수 만큼 votes 수를 업데이트 해야합니다.

 

for (int i = 0; i < voter_count; i++)
{
	for (int j = 0; j < candidate_count; j++)
    {
		if (!candidate[preferences[i][j]].eliminated) 
        {
        	candidate[preferences[i][j]].votes ++;
            break;
        }
    }
}

 

① if 조건문

if (!candidate[preferences[i][j]].eliminated) : candidate[i].eliminated 대신에 이렇게 쓰인 것은 각 rank로 업데이트 된 선호도에 따라서 candidate를 더 세분화해서 봐야 하기 때문입니다.

 

 

 

 

3) Print_winner function

 

bool print_winner(void)
{
	//TODO
}

: Print_winner function에서는 만약 한 후보자의 득표수가 과반수를 넘긴 경우 이름을 프린트하고, 아무도 이긴 사람이 없다면 false로 마무리 해야합니다.

 

for (int i = 0; i < candidate_count; i++)
{
	if (candidate[i].votes > (voter_count / 2))
    {
    	printf("%s\n", candidates[i].name);
        return true;
    }
}
return false;

 

① if 조건문

candidate[i].votes 즉 후보자가 얻은 득표수가 (voter_count / 2) 즉 전체 투표자수 / 2 = 과반수를 넘길 때를 말하는 조건문

 

 

 

 

 

4) Find_min function

int find_min(void)
{
	//TODO
}

: find_min function에서는 여전히 선거에 참여하는 후보자들 중 가장 적은 득표수의 후보를 찾습니다.

 

int min = candidates[0].votes;

for (int i = 0; i < candidate_count; i++)
{
	if(!candidates[i].eliminated && candidates[i].votes < min)
    {
    	min = candidates[i].votes;
    }
}
return min;

 

① int min = candidates[0].votes;

: min에 첫 번째 후보자의 투표 수 값을 배정

② if 조건문

: 각 후보자가 제거되지 않았을 경우(!candidates[i].eliminated), 그 후보자의 득표수가 min보다 작을 때

③ min에 새로운 값 배정

: min = candidates[i].votes

 

 

 

 

 

5) is_tie function

bool is_tie(int min)
{
	//TODO
}

: is_tie function에서는 선거에 남아있는 후보자들의 득표수가 min으로 모두 같을 때 true를 return함

 

for (int i = 0; i < candidate_count; i++)
{
	if(!candidates[i].eliminated && candidates[i].votes != min)
    {
    	return false;
    }
}
return true;

 

여기서도 true일 경우의 조건문을 찾느라 고생고생하다가, 관점을 바꾸어 false일 경우를 정의하고, 그것이 아닐 때 return true;를 하게 되니 더 쉬웠습니다. (cs50 duck 덕분에 ㅎ..)

 

★ 후보자가 제거되지 않고(1), 득표 수가 하나라도 min과 동일하지 않을 때(2) → return false;

(1) !candidates[i].eliminated

(2) candidates[i].votes != min

 

 

 

 

 

6) Eliminate function

void eliminate(int min)
{
	//TODO
}

: Eliminate function에서는 min 득표수를 가지고 있는 후보자를 제거합니다.

 

for (int i = 0; i < candidate_count; i++)
{
	if (candidates[i].votes == min)
    {
    	candidates[i].eliminated = true;
    }
}
return;

 

이건 비교적 간단한 코드입니다!

주의할점은 candidates[i].eliminated = true; 에서 등호를 두 개가 아닌 하나 적는 것!

 

* = : 값을 할당한다

* == : 두 값이 같다

 


 

 

휴~ 여기까지 무사히 Week0부터 Week3까지 한 달치를 끝냈네요!

갈수록 Pset이 어려워지고 더 길어져서 무섭습니다만 차근차근 파고들어가보면 그렇게 못 할 것도 아닌 것 같기도 하고.. 모르겠어요

ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

지레 겁먹기보다는 할 수 있다고 생각하고 덤비는 게 코딩을 대하는 좋은 자세 같아요.

컴퓨터 짜식이.... 나는 인간인데..... 

 

그래도 1/3을 마무리하는 이 시점이 아주 뿌듯 ! 행복 ! 합니다 ㅎㅎ 

시작할 때부터 원했던 정말 끈기를 길러주는 코딩 녀석.. 한 번 시작하니 오기가 생겨 정말 올해 안에 끝내고 싶네요 ^~^

다음 주도 화이팅! ♥

'CS50' 카테고리의 다른 글

[CS50] Week 4 Section  (1) 2024.10.10
[CS50] Week 4 Review  (4) 2024.10.09
[CS50] Week 3 Section  (2) 2024.10.03
[CS50] Week 3 Review  (1) 2024.10.01
[CS50] Pset 2 : Scrabble, Readability, Caesar  (1) 2024.09.29

+ Recent posts