과제 번호 : 1데이터 구조Report(1. 매직스퀘어 구현)제출일자 : 2009년 9월 24일학과 : 컴퓨터과 2학년1 - (1). 문제제기 :매직스퀘어를 구현한다. 마방진이라고도 하는데, 정사각형의 1변에 나열된 수의 개수 n에 따라서 n방진을 구성한다. 이 문제에서는 n이 홀수일 경우만으로 제한한다.개의 수를 가로, 세로, 대각선 방향의 수를 더하면 모두 같은 값이 나오도록 n by n 행렬에 배열한 것이다. 일반적으로 마방진의 각 칸에는 1부터까지 수가 한 개씩 들어간다.2. 문제분석 :n값을 입력받았을 때, n by n의 행렬을 생성한다. 이때 n은 홀수 값만을 받도록 예외처리를 해야 한다. 마방진 안에는 1부터까지 숫자가 한 번씩 들어간다. 이때 숫자가 채워지는 규칙이 있다. 이 규칙을 이용하여 n차 마방진을 생성한다.① (0, n/2)의 위치에 1을 대입한다. n/2는 정수부분만 계산된다.② 현 위치에서 행과 열을 모두 1씩 감소시킨 (row-1, column-1)위치로 이동한다.③ 그 위치에 2를 대입한다.④ 다시 ②의 과정을 반복하여, 그 위치에 3, 4... 숫자를 증가시켜가며 대입한다.⑤ 만약, 그 위치에 이미 숫자가 채워져 있다면, 아래로 내려가 대입하고, ②로 간다.⑥ 만약, 행의 범위가 (0~Size-1)을 벗어난다면, 중심축을 기준으로 행을 대칭시킨다.⑦ 마찬가지로, 열의 범위가 (0~Size-1)을 벗어나면, 중심축을 기준으로 반대편에 대입.⑧ 특별히, (0,0)의 경우 ②의 규칙을 사용하면 (-1,-1)이 되므로, 예외적으로 (1,0)처리.⑨ 대입하던 key값이에 이르면, 반복을 중단한다.3. 프로그래밍 소스 :magic_square.h// magic_square.h : 마방진을 생성할 함수에 포함된 클래스를 선언할 헤더파일입니다.//#include class MSquare { //클래스의 선언public:MSquare() {Size=0;}; //생성자를 통해 private변수 초기화~MSquare(){}; //소멸자//함수의 선언void set_MaxSize(int n); //사이즈를 셋팅할 함수.void Magic_Square(); //마방진을 구현하는 함수.void makeArray(); //정수형배열을 동적으로 할당하여 생성하는 함수.void initialize(); //생성된 배열을 초기화하는 함수.void Display(); //생성된 매직스퀘어를 출력하는 함수.private://변수의 선언.int Size; //배열의 사이즈를 저장할 변수.int** Array; //동적할당으로 생성할 정수형 2차원포인터};magic_square.cpp// magic_square.cpp : 헤더파일에 포함된 내용을 구현합니다.//#include "magic_square.h"#include using std::cout;using std::cin;using std::endl;void MSquare::set_MaxSize(int n)//사이즈를 셋팅할 함수.{Size = n;//Size를 n으로 세팅한다.}void MSquare::makeArray()//정수형배열을 동적으로 할당하여 생성하는 함수.{int** cArray;//임시로 만든 정수형2차원포인터cArray=new int*[Size];//가로방향을 동적할당for(int i=0;i
실습번호 1객체 지향 프로그래밍report제출일자:3월 25일학과:컴퓨터공학1.문제제기함수를 이용하여 계산기 구현하기2.문제분석3.문제해결처음에 초기값 a를 입력받아서 set_result함수로 들어할 a값을 r값에 저장해놓고 연산할부호를 고른다. 그러면 그 부호에 해당하는 함수로 들어가 연산을 수행한 후에 연산을 계속 하기를 원한다면 y를 눌러서 다시 초기값을 입력받아서 계속 수행하고 멈추기를 원한다면 n을 눌러서 get_result함수에서 결과값이 return되어서 출력된다.4.프로그래밍 소스//함수를 이용하여 계산기 구현하기#includeusing std::cout; //표준라이브러리에서 cout 함수를 사용하겠다using std::cin;//표준라이브러리에서 cin 함수를 사용하겠다using std::endl;//표준라이브러리에서 endl 함수를 사용하겠다int r;void plus(int); //덧셈void minus(int); //뺄셈void multiply(int); //곱셈void division(int); //나눗셈int get_result(); //결과값을 초기화한다void set_result(int);int main(){int g_r,a,b;char c,q;cout < "초기값을 입력하세요" < endl;cin >> a;set_result(a);while(1){cout < "사칙연산 계산기" < endl< "계산하고자 하는 연산문자를 입력하세요" < endl< "덧셈 - +" < endl< "뺄셈 - -" < endl< "곱셈 - *" < endl< "나눗셈 - /" < endl;cin >> c;switch(c){case '+' : cout < "계산하고자 하는 값을 입력하세요" < endl;cin >> b;plus(b);break;case '-' : cout < "계산하고자 하는 값을 입력하세요" < endl;cin >> b;minus(b);break;case '*' : cout < "계산하고자 하는 값을 입력하세요" < endl;cin >> b;multiply(b);break;case '/' : cout < "계산하고자 하는 값을 입력하세요" < endl;cin >> b;division(b);break;}cout < "더 계산 하시겠습니까?(y/n)" < endl;cin >> q;if(q=='y')continue; //계속한다else if(q=='n'){g_r = get_result(); //얻은 결과값-> g_rcout < "결과값은 " < g_r < "입니다" < endl; //g_r출력break; //멈춘다}}return 0;}void set_result(int a){r=a;}int get_result(){return r;}void plus(int a){r = r+a;}void minus(int a){r = r-a;}void multiply(int a){r = r*a;}void division(int a){r = r/a;}5.결과화면6.느낀점실습시간에 소스를 다 짜놨는데 다해놓고 버그가 걸려서 갑자기 통째로 날아가버렸다.그래서 할수없이 집에서 소스를 짜려고 페도라10을 깔았는데 소스를 짜고 컴파일하는과정에서 gcc가 없어서 깔다가 계속 설치도중에 오류가 나서 실패하였다. 그리고 우분투 최신 버젼을 받아서 쓰고있는데 리눅스운영체제인데도 불구하고 XP유저인 내가 불편한 사항을 거의 느끼지 못했다. 다만 터미널에서 입력을 할 때 cpp파일만들고 다시 바탕화면으로나가서 cpp파일 만든장소로 가서 cpp파일을 키고 편집하는것이 별탈없이 쉽게 소스작성이 되었다. 처음에 학교에서 내가 짤때는 연산부호를 선택받아서 if와 else if만을 이용해서 하였었는데 알고보니까 그게 아니었다. class개념을 이용하여 함수를 써서 선택된 연산부호에 따라서 연산이 되는것이다. 다음부터는 그날 실습시간에 해야하는 것은 꼭 통과해야겠다.
과제 번호 : 5데이터 구조Report(희소행렬 class)제출일자 : 2009년 10월 21일학과 : 컴퓨터과 2학년1. 문제제기 :희소행렬 클래스를 디자인한다. 희소행렬이란 0인 원소를 많이 포함하고있는 경우, 메모리 공간의 불필요한 낭비를 줄이기 위해서 링크드리스트를 이용해 0이아닌 원소만 저장하는 방식을 의미한다. 이러한 희소행렬 클래스를 정의하고, 사용자로부터 임의의 원소 Aij를 입력받았을 때 해당 원소를 반환하는 함수를 구현한다. 또한 반환된 값을 변경할 수 있도록 하라.2. 문제분석 & 문제해결 :(책의 구조와 약간 다르게 구현하였습니다.)① 사용자로부터 행렬의 크기와 0이 아닌 원소의 개수 입력받기사용자로부터 행렬의 크기 및 0이 아닌 원소의 개수를 입력받습니다. 그러면 이 값들을 파라미터로 전달하여 Sparse_Matrix클래스의 객체를 생성자를 통해 생성합니다. 생성자에서는 전달받은 파라미터들을 각각 행의 크기, 열의 크기, 0이 아닌 원소의 개수를 저장하는 private변수에 저장합니다.행이나 열을 처리하기 위해 headnode와 tailnode를 사용하였습니다. 구분을 위해 행번호와 열 번호 및 값은 (-1,-1,0)으로 초기화합니다. headnode를 행의 개수만큼 배열로 선언합니다. 그리고 각각의 headnode[i]는 i번째 행을 담당하게 됩니다.(각각의 headnode[i]가 각 행을 의미한다.)이렇게 초기화된 것이 행렬을 행끼리 따로 떼어놓은 모양이 됩니다. 즉, 1행의 3열 원소란, headnode[0]으로 시작하는 리스트에서 열 값이 2인 원소를 찾아내면 됩니다.② 0이 아닌 원소의 값 입력받기이 부분에서 필요한 예외처리는 0이 아닌 원소의 개수입니다. 행렬크기는 row*column이므로 0이 아닌 원소의 개수는 최대 row*column을 넘을 수 없습니다. 따라서 가능한 경우는 0부터 row*column개까지의 범위입니다.값의 입력은 Set_Element()함수에서 이루어집니다. 기본적인 알고리즘은 지난번 과제에서 항을 추 정렬했다면, 이번 과제에서는 입력받은 행이 속한 리스트에서 열 번호를 비교하면서 열 번호가 작은 순서로 정렬합니다. 링크드리스트를 조작하기 위해 walker변수를 사용하여 리스트의 처음부터 끝까지 읽어나가는 동작을 합니다. 반복문 안에서 두 개의 walker변수가 서로 연결된 다음 항을 포인팅하면서 반복적인 동작으로 리스트를 끝까지 탐색합니다. 처음 시작시 walker1을 x행의 가장 첫 번째인 headnode[x]로 지정하고, walker2를 walker1->next로 지정한 뒤에, walker2가 tailnode[x]가 되기까지 반복문을 돌리면 됩니다. 한번 반복이 지나면 walker1 = walker2, walker2 = walker2->next의 동작을 함으로써 한 칸씩 이동합니다. 이 때 입력된 y열의 위치에 정확히 끼워넣기 위해, 현재 입력된 열 번호 y값을 이미 생성되어있는 리스트의 원소들과 비교를 하는 과정을 거칩니다. 여기에는 3가지의 경우가 있습니다.1) 입력된 항의 지수가 다음항(walker2)보다 작을경우walker1과 walker2의 조작으로 리스트를 탐색하다가, walker2보다 작은 열 번호를 가진 원소가 나타나면, 원소를 walker1과 walker2사이에 끼워넣습니다. 항을 생성할 때부터 이 방법을 사용하기 때문에, 리스트는 항상 열 번호의 순서로 정렬이 되며 따라서 앞항이 자신보다 클 경우는 발생하지 않으므로 고려하지 않아도 됩니다.2) 입력된 열 번호와 생성되어있는 원소의 열 번호가 같은 경우 (이미 원소가 있는 경우)이미 해당위치에 원소가 있는 경우, 새로운 원소를 만들지 않고 이미 만들어진 항의 값을 변경하면 됩니다. 이때 new_walker는 사용되지 않으므로 RetNode()함수를 사용하여 가용공간리스트로 메모리를 반환합니다.3) 리스트의 끝까지 자신이 들어갈 위치를 찾지 못한 경우 (그것이 가장 큰 행)리스트의 끝까지 찾았다는 것은 walker2->right가 tailnoxe[x]가 되었다는 의미입니다.현재가지 생성right 는 tailnode[x]가 되므로, walker2와 tailnode[x]사이에 끼워 넣습니다. 실제적으로 이것은 가장 마지막에 끼워 넣은 것이 됩니다.③ 임의의 Aij위치의 값을 반환하는 함수 Get_Element()사용자로부터 임의의 ij값을 입력받아서, 그 위치에 해당하는 원소를 반환하는 함수입니다. 사용자에게는 행렬의 표현으로 보이기 때문에 i와 j값으로 표현되지만, 실제로는 링크드리스트로 연결되어서 0인 원소는 메모리에 존재하지 않습니다. 이러한 차이를 느낄 수 없게끔 i와 j값을 토대로 위치를 잘 계산하여야 합니다. 이때 보통 수학에서 index로 사용하는 범위는 1부터 n 이지만, 컴퓨터에서 배열의 번호를 카운팅할 때의 혼동을 방지하기위해서 index를 0부터 n-1로 통일하였습니다.사용자로부터 x행의 y열을 입력받습니다. 그렇다면 그 원소는 headnode[x]가 속한 리스트의 사이에 있을 것입니다. walker1과 walker2변수를 사용해서 이 리스트를 탐색합니다. 만약 walker1이나 walker2의 열 번호와 y값이 일치한다면, 위치 (x, y) 에 원소가 존재하는 것입니다.그 위치에 원소가 존재한다면, 그 리스트원소의 value값을 출력해주면 됩니다.이 부분에 대해서 조금 더 시간복잡도와 프로그램의 효율성을 고려했을 때, 리스트가 column 값이 작은 순서로 정렬 되어있다는 것을 이용할 수 있습니다. 즉 입력받은 y값보다 작은 범위의 column에 대해서만 검색을 하고, 만약 그 범위를 넘어선다면, 그 뒤에는 존재할 가능성이 없으므로 굳이 리스트의 끝까지 탐색할 필요 없이 탐색을 break하면 됩니다.즉, 탐색하여 같은 열 번호인 원소가 있으면 그 위치에 원소가 존재하는 것이므로 그 원소의 value값을 리턴하고, 탐색했는데 찾지 못했다면 그 위치에 원소가 존재하지 않는 것이므로 0을 리턴 합니다.이 부분에서 필요한 예외처리는 index값의 범위를 벗어났을 경우입니다.④ Get_Element()한 값을 변경하기사용자가 입력한기능은 굳이 다른 함수를 쓰지 않고, 위에서 처음 원소들을 추가할 때 사용한 Set_Element()를 그대로 사용합니다. 다만, 항을 변경하는 부분에는 두 가지 경우의 수가 존재합니다.만약 리턴 된 값이 0이라면, 그 위치에는 원소가 원래 존재하지 않았던 것이므로, 해당 위치에 새로운 원소를 생성하여 그 자리에 끼워 넣어야 합니다.반대의 경우로, 만약 리턴 된 값이 0이 아니고, 변경할 값이 0이라면 그 위치에 있던 원소를 삭제해야 합니다.⑤ 희소행렬을 출력하는 함수 (연산자 오버로딩)희소행렬을 출력하는 함수를 정의합니다. 이 부분에서는 먼저 희소행렬이 어떠한 형식으로 만들어져있는지를 고려해야합니다. 또한 행렬을 출력할 때는 일반적으로 행단위로 출력하고 줄내림(n문자)로 행을 구분합니다. 원소끼리의 구분을 용이하게 하기 위해 원소와 원소사이의 거리는 t문자로 구분합니다.이제 원소들을 출력하는 규칙을 살펴보면,예를 들어, 위와 같은 형식의 리스트가 있다고 가정합시다. 열의 크기는 13로 가정합시다. 이것은 0번 row의 리스트이며, 출력시 가장 첫 번째 줄인 1행(index는 0)이 될 것입니다. 또한 이 리스트를 실제적인 행렬 꼴로 출력하자면이 될 것입니다. 이를 분석하면,위의 그림과 같이 나타낼 수 있습니다. 이때 분류되는 영역은 크게 3가지입니다.첫째원소가 시작되기전의 앞부분, 원소와 원소사이 부분, 마지막원소의 뒷부분입니다. 이 3가지 부분에는 원소가 존재하지 않는 영역이므로 이 범위에 속한 원소를 출력할 때는 0을 출력해줘야 합니다.우선, 만약 그 행에 아무런 원소도 없다면 고려할 필요없이 0만 출력합니다. 따라서 원소가 있을 경우만 가정했을 때 다음과 같이 분류할 수 있습니다.1) 첫째 원소의 앞부분에는 첫째원소의 column값 만큼의 0이 출력되어야 합니다. 위의 그림에서 보면 첫째원소의 column은 3이며 따라서 column 0~2의 범위의 자리에는 0이 3번 출력되어야 합니다.첫째 원소는 headnode[x]->right입니다. 그렇다면 원소사이에도 0을 끼워넣어야 합니다. 위의 그림에서 보면 3번열과 5번열 사이에 1개의 0이있고, 5번열과 9번 열 사이에 3개의 원소가 있습니다. 이를 보면 알 수 있듯이, (후자의 열 번호 - 전자의 열 번호 - 1)개의 0이 출력되어야 합니다.위에서 리스트를 탐색했던 방법과 마찬가지로 walker1과 walker2사이에 (walker2의 column값 - walker1의 coloumn값 -1)개의 0을 출력합니다.3) 마지막 원소의 뒷부분도 0으로 채워넣어야 합니다. 위의 그림에서 보면 마지막 원소인 10번 열의 원소 뒤로 2개의 0이 채워져 있음을 볼 수 있습니다. 이 리스트의 열의 크기는 13입니다. 마지막원소의 열 번호는 10이므로, (list의 열 크기 - 마지막원소 열 번호 -1)개의 0이 출력되어야 합니다.리스트를 순차적으로 탐색하였다면 마지막순간에 walker가 가르키는 위치가 마지막 원소가 될 것이며 그 위치는 walker->right 가 tailnode인 상태입니다. 이때 (columnSize - walker의 column값 -1)개의 0을 출력합니다.물론 Get_Element()함수의 Retrun값 출력시키는 방법도 있지만, 그 경우 함수호출이 매우 많이 발생하며, Get_Element()함수의 시간복잡도 O(n)을 여러번 통과하게 됩니다. 하지만 이런 방식이 아니라, 원소가 0인 위치와 0이 아닌 경우를 직관적으로 판단하는 방법을 사용하면 훨씬 쉽고 빠르게 알 수 있습니다. 이와같은 방법으로 행번호를 기준으로 headnode[0]이 속한 리스트부터 headnode[row_size]가 속한 리스트까지 순차적으로 출력해주면 됩니다.3. 프로그래밍 소스 :main.cpp#include #include "sparse_matrix.h"using std::cout;using std::cin;using std::endl;using std::istream;using std::ostream;int main(void){int s_row, s_columncout