Data structures are forms of organization in memory.
1. Stacks and Queues
Queues : one form of abstract data structure (enqueued, dequeued)
Stacks (push, pop)
FIFO
First in first out
LIFO
Last in first out
typedef struct
{
person people[CAPACITY];
int size;
}
stack;
- Limitation of the code above : unable to grow as items are added to it.
2. Resizing Arrays
There's an array :
In memory, there are other values being stored by other programs, functions, and variables.
If you want to store a fourth value '4' in our array.
You can allocate a new area of memory of size 4 and move the old array to a new one.
This new area of memory would be populated with garbage values
However, it's bad designed - Every time we add a number, we have to copy the array item by item.
It would be nice if we were able to put the '4' somewhere else in memory. (this would no longer be an array though because it's not contiguous)
list.c
#include<stdio.h>intmain(void){
//List of size 3int list[3];
//Initialize list with numbers
list[0] = 1;
list[1] = 2;
list[2] = 3;
//Print listfor (int i = 0; i < 3; i++)
{
printf("%i\n", list[i]);
}
}
We can leverage our understanding of pointers to create a better design in this code:
#include <stdio.h>#include <stdlib.h>
int main(void)
{
//List of size 3
int *list = malloc(3 * sizeof(int));
if (list == NULL)
{
return 1;
}
//Initialize list of size 3 with numbers
list[0] = 1;
list[1] = 2;
list[2] = 3;
//List of size 4
int *tmp = malloc(4 * sizeof(int));
if (tmp == NULL)
{
free(list);
return 1;
}
//Copy list of size 3 into list of size 4
for (int i = 0; i < 3; i++)
{
tmp[i] = list[i];
}
//Add number to list of size 4
tmp[3] = 4;
//Free list of size 3
free(list);
//Remember list of size 4
list = tmp;
//Print list
for (int i = 0; i < 4; i++)
{
printf("%i\n", list[i]);
}
//Free list
free(list);
return 0;
}
Notice that the 'tmp' list was created.
free(list) : since the block of memory that list points to is no longer used.
3. Linked Lists
three useful primitives
struct
a data type that you can define yourself
.
dot notation allows you to access variables inside that structure
*
is used to declare a pointer or dereference a variable
-> operator : goes to an address and looks inside of a structure
A linked list is one of the most powerful data structures within C. It allows you to include values that are located at varying areas of memory. They allow you to dynamically grow and shrink the list as you desire.
Three values stored at three different areas of memory :
We could utilize more memory to keep track of where the next item is :
We would keep one more element in memory, a pointer, that keeps track of the first item in the list.
These boxes are called nodes.
A node contains both an item (int number) & a pointer called next.
For example, a word 'Toad' would be stored as follows:
Toadette and Tom would be stored as:
5주차에서는 Queues, Linked lists, Trees, Dictionaries, 그리고 Tries 까지 다양한 data structure의 개념과 활용 방법에 대해서 알아보았다. 컴퓨터의 메모리를 정리할 때 어떤 data structure을 사용하느냐에 따라 속도와 메모리가 차지하는 크기가 달라지기 때문에 중요한 영향을 끼친다. 저번주에 이어서 보이지 않는 메모리 영역을 다루고, pointer의 개념이 아직 미숙해서 처음으로 강의를 다시 뒤로 돌려 반복해서 듣기도 한 내용들이다. 아직까지 완벽하게 이해했다고 자신할 수는 없지만, Section과 Pset5를 듣고 고민하는 과정에서 더 큰 이해를 할 수 있길!! 오늘도 수고 많았다 안뇨옹
ㅎㅎ 사실 논 건 아니고 매일 시간 내서 문제를 풀려고 노력은 했지만 너무 어려워서 한참 걸렸어요..ㅠㅠ
뒤로 갈수록 배운 거에서 나오는 게 아니고 응용을 요구합니다 !!!
약간.. 개념 배우고 심화 문제 나오는 수능같은 그런 느낌
진짜로 literally 손으로 머리 싸매고 낑낑댐..
어쨋든 어제 두세시간을 쏟고 마지막 과제를 제출하고 나서야 문풀 리뷰를 할 수 있게 되었네용
리뷰하면서 못 다한 이해를 해보도록 하겠습니다 ^~^
1보 후퇴엔 3보 전진~~
레츠고 !
4-1) Volume
: 볼륨 조절하기
WAV files store audio as a sequence of “samples”: numbers that represent the value of some audio signal at a particular point in time.WAV files begin with a 44-byte “header”, and after the header, it contains a sequence of samples, each a single 2-byte (16-bit) integer representing the audio signal at a particular point in time. Scaling each sample value by a given factor has the effect of changing the volume of the audio. Write a program to modify the volume of an audio file.
<Distribution code>
The program should accept three command-line arguments. (input, output, factor)
The program should read the header from the input file and write the header to the output file.
The program should then read the rest of the data from the WAV file, one 16-bit(2-byte) sample at a time. It should multiply each sample by the factor and write the new sample to the output file.
★ atof() : used to convert a string into a floating-point number
A file format (likeBMP,JPEG, orPNG) that supports “24-bit color” uses 24 bits per pixel.A 24-bit BMP uses 8 bits to signify the amount of red in a pixel’s color, 8 bits to signify the amount of green in a pixel’s color, and 8 bits to signify the amount of blue in a pixel’s color.If the R, G, and B values of some pixel in a BMP are, say,0xff,0x00, and0x00in hexadecimal. In this problem, you’ll manipulate these R, G, and B values of individual pixels, ultimately creating your very own image filters.
<Distribution code>
//TODO #1
: Implement grayscale (흑백 필터)
We should make sure the red, green, and blue values are all the same value. But what value to make them?
→ If the original red, green, and blue values were all pretty high, then the new value should also be pretty high. If the original values were all low, then the new values should also be low.
You can take the average of the red, green, and blue values of each pixel to determine what shade of grey to make the new pixel.
//Loop over all pixels
for(int i = 0; i < height; i++)
{
for(int j = 0; j < width; j++)
{
//Take average of red, green, and blue
int average = round((image[i][j].rgbtRed + image[i][j].rgbtGreen + image[i][j].rgbtBlue) / 3.0);
//Update pixel values
image[i][j].rgbtRed = average;
image[i][j].rgbtGreen = average;
image[i][j].rgbtBlue = average;
}
}
The result being like :
//TODO #2 : Implement Sepia (오래된 붉은갈색 느낌 필터)
For each pixel, the sepia color values should be calculated based on the original color values per the below:
Any pixels on the left side of the image should end up on the right, and vice versa.
//Loop over all pixels
for (int i = 0; i < height; i++)
{
for (int j = 0; j < (width/2); j++)
{
//Swap pixels
RGBTRIPLE temp = image[i][j];
image[i][j] = image[i][width-j-1];
image[i][width-j-1] = temp;
}
}
★ Notice that j loop is until (width/2) because only half of the pixels of a row should be reflected!
★ image[i][width-j-1] means the pixel on opposite side of a row.
The result being like:
//TODO #4
: Implement Blur (블러 처리된 필터) ★
가장 어려워서 며칠간 고민했던 부분!!
We will use "box blur", which works by taking each pixel and, for each color value, giving it a new value by averaging the color values of neighboring pixels.
The new value of each pixel would be the average of the values of all of the pixels that are within 1 row and column of the original pixel (forming a 3x3 box).
Ex) For pixel 6 : new value of averaged the original color values of pixels 1,2,3,5,6,7,9,10, and 11.
For a pixel along the edge or corner, like pixel 15 : pixels 10,11,12,14,15and 16.
We are going to create a copy of image:
//Create a copy of image
RGBTRIPLE copy[height][width];
for(int i = 0; i < height; i++)
{
for(int j = 0; j < width; j++)
{
copy[i][j] = image[i][j];
}
}
Let's write psedocodes first, stey-by-step!
//전체 이미지의 각 픽셀 iterate
//copy 만들기
//3x3 box 픽셀 iterate
//픽셀이 valid한 경우 (edge case, corner case 포함)
//각 픽셀에 새로운 RGB 값 할당
//각 RGB 값의 평균을 copy에 직접 할당
//전체 픽셀에 대해 copy 값을 image 값에 원래대로 할당
즉, 전체 이미지에서 3x3 사이즈의 픽셀 박스를 iterate하면서 각 RGB값의 평균을 새로이 할당하고, 마지막에는 copy에 할당한 값을 원래대로 image에 돌려놓으면 됩니다!
① 3x3 box 픽셀 반복문
★새로운 변수 int di, int dj를 만들어서 상하좌우 한 칸씩을 뜻하는 -1부터 1까지 반복하는 box iteration을 만든다.
★이것을 전체를 반복하는 변수 i와 j에 더하여 전체 픽셀을 한정적인 3x3 box로 반복할 수 있게 새로운 변수 ni, nj로 정의한다.
② valid pixel 조건문
: 유효한 픽셀이라는 것은 edge나 corner 픽셀의 경우도 포괄해야 함을 뜻한다.
이는 새로이 정의된 변수 ni, nj가 0보다 크거나 같고 각각 height, width보다 작아야함을 의미한다.
③ 새로운 RGB 값 할당하기
: 먼저, 새로운 RGB값을 담아둘 변수 Red, Green, Blue를 정의한 후 각각에 값을 할당한다.
: float으로 정의한 이유는 각 값이 딱 정수로 떨어지지 않을 수 있기 때문. 이렇게 정의하면 정확도가 올라갈 수 있다.
: 각 픽셀의 빨간색, 초록색, 파란색 값을 각각 Red, Green, Blue에 더해준다.
④ counter 만들기
: 평균을 구하기 위해서는 몇 개의 유효한 픽셀의 RGB 값이 더해졌는지 알아야 한다.
따라서, 유효한 픽셀을 카운트 할 때마다 counter 변수를 하나씩 업 시켜준다.
먼저, counter 변수를 0으로 정의한다.
: 중요한 것은 counter을 0으로 정의하는 위치이다. 각 box를 반복한 후에 다시 0으로 reset 해야하기 때문에 box iteration 앞에 정의한다.
: 유효한 픽셀 조건문 안에 counter ++를 해준다.
⑤ RGB 값 평균 구하고 할당하기
: RGB 값의 평균은 직접적으로 copy에 할당한다. 이 때 주의할 점은 변수 Red, Green, Blue 값이 float 이기 때문에 round()를 통해 가까운 정수로 변환해준다.
: 이 때에도 중요한 것은 위치이다. Box iteration 바깥에서 copy에 할당해준다.
⑥ copy 값을 image 값으로 옮기기
: 각 픽셀의 값을 다 옮겨 할당해야 하므로, 전체 픽셀을 반복하는 for문 아래에서 할당시켜준다.
완성된 Blur 코드를 전체적으로 보면 다음과 같다:
The result being like:
4-3) Recover
: 삭제된 사진 복원하기
We spent the past several days taking photos around campus, all of which were saved on a digital camera as JPEGs on a memory card. Unfortunately, we somehow deleted them all! We’re hoping (er, expecting!) you can write a program that recovers the photos for us!
(약간 초등학교 수학 문제중에 달력을 찢었다.. 이런 거 생각났다 ㅋㅋㅋㅋㅋㅋ 왜 찢어.. 왜 삭제해..)
Background
JPEGs have "signatures", patterns of bytes that can distinguish them from other file formats. The first three bytes of JPEGs are 0xff, 0xd8 and 0xff.
The fourth byte is either 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, or 0xef. The fourth byte's first four bits are 1110.
Digital cameras only write to cards in units of 512 B. (block size = 512 bytes) →It means you can read 512 of memory card's bytes at a time for efficiency's sake.
위 그림처럼 각 색이 각 JPEG 파일을 의미한다. JPEG가 시작하는 블럭이 있고, 시작하지 않는 블럭이 있다.
Let's writepsedocodesfirst!
//Accept a single command-line argument
//Open the memory card
//While there's still data left to read from the memory
//Create JPEGs from the data
① single command-line argument
: 하나의 command-line argument가 아닐 시 에러 메세지로 경고한다. (argc != 2)
② open the memory card
: card memory로 가서 argv[1]을 여는데, read 모드로 연다. ("r") 만일 file이 제대로 열리지 않을 시 에러메세지로 경고한다.
③ memory에서 더 이상 읽을 데이터가 없을 때까지 조건문
: 파일에서 데이터를 읽을 때, 일시적으로 그 데이터를 보관할 "buffer"가 필요하다. 데이터가 512 bytes로 저장되어 있으므로, 해당 사이즈로 buffer을 만들면,
uint8_t buffer[512];
이렇게 만들 수 있겠지만, 코드에 constant(magic number)를 쓰는 것은 바람직하지 않으므로 보완하여 다시 쓸 수 있다.
★ Notice that there's no ; when you use #define
그 다음, fread()를 사용하여 계속해서 남은 데이터를 읽는다. 이 때, fread() returns the number of bytes it has read, so make sure that the condition is equal to BLOCKSIZE(512).
④ If start of new JPEG
: 앞에서 memory block은 JPEG의 시작일 수도, 아닐 수도 있다고 언급했다. 먼저 JPEG의 시작일 경우 조건문을 생각해보자.
A variable : a name for some value that can change → has an address somewhere in memory
type : int *
name : p
value : &calls (the address of calls)
Reasons to use pointers
Pointers enable additional capabilities like:
You could write a function that allows you to pass something in by reference and not just by copy. The code you write is cleaner as a result.
You could ask your program for dynamic memory depending on what the user actually needs. Your programs can now scale their usage of memory according to user behavior.
2. Passing by Copy vs Passing by Reference
<Passing by Copy>
This seemed to be swapped in swap, but it's still the same in main function !
<Passing by Reference>
By copying the addresses of a and b(the pointer to a/ the pointer to b), we actually copy in the address, and we change the values exactly where those values currently in memory.
Let's debug this in VS Code and see step-by-step:
Here's my code swapping two values, a and b.
If I run debug50 :
① Notice that a = 10, b = 50
Let's step into the swap function!
② In variable window, you can see the addresses of a and b now. (&a, &b)
Let's step over :
③ Notice that the value of location a(*a = 10) is handed over to temp.(temp = 10)
Let's step over:
④ Now, the value of b(*b = 50) is handed over to a.(*a = 50)
Let's step over for the last time :
⑤ Notice that the value of temp(10) is handed over to *b.(*b = 10)
And now you can check that a and b are swapped.
3. FILE I/O
We can also open up files, read data from them, and write data to those files!
fopen
opens a file for future reading/ writing.
fclose
closes a file.
Always fclose all files you fopen!
fopen
"r" means 'read' mode.
"w" means 'write' mode.
fclose
<Reading and Writing>
fread
reads data from a file into a buffer*.
fwrite
writes data from a buffer* to a file.
*a buffer is a chunk of memory that can temporarily store some data from the file.
Why do we need a buffer?
→ We often don't know how big a file is, so the best we can do is to look at small bits of it, not the entire file itself.
<Reading from a File>
From where are you reading?
To where are you reading?
What size is each block of data you want to read? (ex. text file-1 byte/ image file-3 bytes)
How many blocks do you want to read?
fread(Towhere, what size , how many , Fromwhere);
ex) fread(buffer, 1, 4, f);
<Practice with Reading>
Create a program, pdf.c, that opens a file given as a command-line argument.
Check if that file is a PDF. A PDF always begin with a four-byte sequence, corresponding to these integers: 37, 80, 68, 70
① Open up a file
② Create a buffer
③ Use fread to read 4 individual bytes from that file
④ Print those out
uint8_t : Unsigned Int of 8 bytes (always positive, takes 8 bits or 1 byte long) Meaning, you can store a positive Integer with max number being 2^8-1.
If we use int buffer instead of uint8_t, we might not get the values we expect,
because int buffer[4] is 16 bytes long(an integer is 4 bytes long), whereas we're only going to read four of them.
#include <cs50.h>#include <stdio.h>
int main(void)
{
//Get two integers
int i = get_int("i: ");
int j = get_int("j: ");
//Compare integers
if (i == j)
{
printf("Same\n");
}
else
{
printf("Different\n");
}
}
In case of comparing two strings:
#include <cs50.h>#include <stdio.h>
int main(void)
{
//Get two strings
char *s = get_string("s: ");
char *t = get_string("t: ");
//Compare strings' addresses
if (s == t)
{
printf("Same\n");
}
else
{
printf("Different\n");
}
}
→ If you type in HI! for both strings, it still results in the output of Different. But Why?
Now you realize that the code above is actually attempting to see if the memory addressess are different: not the strings
In your terminal window, type in make memory and valgrind ./memory
: Notice that it points out some bugs here, which are
① Invalid write of size 4 (in line 9)
② 12 bytes in 1 blocks are definitely lost in loss record 1 of 1 (in line 6)
We can fix this by
① x[0] = 72;
x[1] = 73;
x[2] = 33;
② We never freed x after using malloc, so this code below has to be involved at last
free(x);
8. Garbage Values
When you ask the compiler for a block of memory → Is this memory empty?
: We cannot guarantee !
It's possible that this memory you allocated was previously utilized by the computer, so you may see junk or garbage values.
★ After you allocate a block of memory, you have to initialize it.
#include <stdio.h>
int main(void)
{
int scores[1024];
for (int i = 0; i < 1024; i++)
{
printf("%i\n", scores[i]);
}
}
Running this code will allocate 1024 locations in memory for your array. In your terminal window, you'll see this:
garbage values
9. Swap
How do we swap two values?
#include <stdio.h>
void swap(int a, int b);
int main(void)
{
int x = 1;
int y = 2;
printf("x is %i, y is %i\n", x, y);
swap(x, y);
printf("x is %i, y is %i\n", x, y);
}
void swap(int a, int b)
{
int tmp = a;
a = b;
b = tmp;
}
Here, we created our own swap function whose arguments are int a and int b.
We used a temporary holding space 'int tmp' to swap a and b. But will it work?
Hmm... seems like two values are not swapped. Why?
Let's learn about scope first.
scope: (변수에 접근할 수 있는) 범위
Global scope : 전역 스코프. 전역에 선언되어 있어 어느 곳에서든 해당 변수에 접근 가능.
▷ The values of x and y created in the { } of the main function only have the scope of the main function :
Various functions are stored in the stack in memory.
Now, consider the following image:
Notice that main and swap have two spearate frames or areas of memory.
Even though we swapped two variables in swap(), the function will not exist afterwards.
#include <stdio.h>
void swap(int *a, int *b);
int main(void)
{
int x = 1;
int y = 2;
printf("x is %i, y is %i\n", x, y);
swap(&x, &y);
printf("x is %i, y is %i\n", x, y);
}
void swap(int *a, int *b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
Now it works! What did I change?
★ Notice that variables are not passed by value but by reference.
That is, the addresses of a and b are provided to the function.
Therefore, the swap function can know where to make changes to the actual a and b from the main function.
Follow the arrows !
10. scanf ★
Instead of functions like get_int in CS50, scanf is a built-in function that can get user input :
#include <stdio.h>
int main(void)
{
int x;
printf("x: ");
scanf("%i", &x);
printf("x: %i\n", x);
}
★ Notice that the value of x is stored at the location of x in the line (scanf("%i", &x))
However, attempting to reimplement get_string is not easy :
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 ++;
returntrue;
}
}
returnfalse;
★ 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
#defineMAX_VOTERS100
#defineMAX_CANDIDATES9
// preferences[i][j] is jth preference for voter i
intpreferences[MAX_VOTERS][MAX_CANDIDATES];
// Candidates have name, vote count, eliminated status
typedefstruct
{
stringname;
intvotes;
booleliminated;
} candidate;
// Array of candidates
candidatecandidates[MAX_CANDIDATES];
// Numbers of voters and candidates
intvoter_count;
intcandidate_count;
// Function prototypes
boolvote(intvoter, intrank, stringname);
voidtabulate(void);
boolprint_winner(void);
intfind_min(void);
boolis_tie(intmin);
voideliminate(intmin);
intmain(intargc, stringargv[])
{
// Check for invalid usage
if (argc<2)
{
printf("Usage: runoff [candidate ...]\n");
return1;
}
// Populate array of candidates
candidate_count=argc-1;
if (candidate_count>MAX_CANDIDATES)
{
printf("Maximum number of candidates is %i\n", MAX_CANDIDATES);
return2;
}
for (inti=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);
return3;
}
// Keep querying for votes
for (inti=0; i<voter_count; i++)
{
// Query for each rank
for (intj=0; j<candidate_count; j++)
{
stringname=get_string("Rank %i: ", j+1);
// Record vote, unless it's invalid
if (!vote(i, j, name))
{
printf("Invalid vote.\n");
return4;
}
}
printf("\n");
}
// Keep holding runoffs until winner exists
while (true)
{
// Calculate votes given remaining candidates
tabulate();
// Check if election has been won
boolwon=print_winner();
if (won)
{
break;
}
// Eliminate last-place candidates
intmin=find_min();
booltie=is_tie(min);
// If tie, everyone wins
if (tie)
{
for (inti=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 (inti=0; i<candidate_count; i++)
{
candidates[i].votes=0;
}
}
return0;
}
// Record preference if vote is valid
boolvote(intvoter, intrank, stringname)
{
// TODO
returnfalse;
}
// Tabulate votes for non-eliminated candidates
voidtabulate(void)
{
// TODO
return;
}
// Print the winner of the election, if there is one
boolprint_winner(void)
{
// TODO
returnfalse;
}
// Return the minimum number of votes any remaining candidate has
intfind_min(void)
{
// TODO
return0;
}
// Return true if the election is tied between all candidates, false otherwise
boolis_tie(intmin)
{
// TODO
returnfalse;
}
// Eliminate the candidate (or candidates) in last place
voideliminate(intmin)
{
// 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;
returntrue;
}
}
returnfalse;
① 모든 후보자를 포괄하도록 후보자 수만큼 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);
returntrue;
}
}
returnfalse;
① 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)
{
returnfalse;
}
}
returntrue;
여기서도 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을 마무리하는 이 시점이 아주 뿌듯 ! 행복 ! 합니다 ㅎㅎ
시작할 때부터 원했던 정말 끈기를 길러주는 코딩 녀석.. 한 번 시작하니 오기가 생겨 정말 올해 안에 끝내고 싶네요 ^~^