재귀를 이용한 하노이의 탑 시각화(windows와 linux에서만 컴파일 가능)





-sleep 함수 사용에 대해..


windows에서는 windows.h헤더 내에 선언된 Sleep함수(단위 ms)를 이용.


Linux에서는,

linux의 unistd.h에 존재하는 sleep 함수는 최소 대기시간이 1초이다. 만약 1초보다 짧은 시간 동안 프로그램을 재우고 싶다면, 다른 함수가 필요하다.

usleep함수는 microsecond 단위로 대기할 수 있지만, 더이상 잘 사용되지 않는다.

time.h의 nanosleep(시간단위는 nanosecond)을 사용했다(http://stackoverflow.com/a/7684399).




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
// Visualization: Towers of Hanoi
 
#include <stdio.h>
#include <stdlib.h>
 
#ifdef _WIN32 // Windows (x64 and x86)
#include <windows.h>
#define WAIT(n) Sleep(400*(n))
#define CLRSCR() system("cls")
 
#elif defined __linux__
#include <time.h>
#define WAIT(n) nanosleep((const struct timespec[]){{0, 400000000L * (n)}}, NULL);
#define CLRSCR() system("clear && printf '\e[3J'")
#endif
 
void showTower(int arr[], int n);
void solveHanoi(int *arr, int n, int numDisks, int fromPeg, int toPeg);
void moveDisk(int *arr, int n, int fromPeg, int toPeg);
 
int main(void)
{
    int *arr;
    int size = -1;
    int fromPeg, toPeg;
    
    while (size < 0 || size > 9) {
        printf("원판의 갯수 입력(1-9): ");
        scanf("%d"&size);
    }
    getchar(); // skips '\n'
    for (;;) {
        printf("원판들이 있는 현재 막대기 입력(A, B, C): ");
        fromPeg = getchar();
        if (fromPeg == 'a' || fromPeg == 'A') {
            fromPeg = 0;
            break;
        }
        if (fromPeg == 'b' || fromPeg == 'B') {
            fromPeg = 1;
            break;
        }
        if (fromPeg == 'c' || fromPeg == 'C') {
            fromPeg = 2;
            break;
        }
    }
    getchar(); // skips '\n'
    for (;;) {
        printf("원판들이 이동할 막대기 입력 - 위에서 입력한 것과 다른 막대기(A, B, C): ");
        toPeg = getchar();
        if (fromPeg != 0 && toPeg == 'a' || toPeg == 'A') {
            toPeg = 0;
            break;
        }
        if (fromPeg != 1 && toPeg == 'b' || toPeg == 'B') {
            toPeg = 1;
            break;
        }
        if (fromPeg != 2 && toPeg == 'c' || toPeg == 'C') {
            toPeg = 2;
            break;
        }
    }
    
    /*
    원판 위치에 대한 배열 생성:
    배열의 i번째 멤버의 값은 (i+1)크기의 원판이 위치한 기둥 번호를 나타냄
    0: 기둥 A, 1: 기둥 B, 2: 기둥 C
    ex) arr[] = {0, 2, 1};
    => 크기 1의 원판은 기둥 A, 크기 2의 원판은 기둥 C, 크기 3의 원판은 기둥 B에 있음
    */
    arr = malloc(sizeof(int* size);
    
    for (int i = 0; i < size; i++) {
        arr[i] = fromPeg;
    }
 
    showTower(arr, size);
    WAIT(1);
    solveHanoi(arr, sizesize, fromPeg, toPeg);
    printf("\tdone! :)\n");
    return 0;
}
 
void showTower(int arr[], int n)
{
    int **coord;
    coord = malloc(sizeof(int ** n);
    CLRSCR();
    if (coord == NULL) {
        printf("malloc failed\n");
        exit(EXIT_FAILURE);
    }
    for (int i = 0; i < n; i++) {
        if ((coord[i] = malloc(sizeof(int* 3)) == NULL) {
            printf("malloc failed: %d\n", i);
            exit(EXIT_FAILURE);
        }
    }
 
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < 3; j++)
            coord[i][j] = 0;
    }
 
    int heightA = 0, heightB = 0, heightC = 0;
    for (int i = n - 1; i >= 0; i--)
    {
        switch (arr[i]) {
        case 0:
            coord[n - 1 - heightA++][0= i + 1;
            break;
        case 1:
            coord[n - 1 - heightB++][1= i + 1;
            break;
        case 2:
            coord[n - 1 - heightC++][2= i + 1;
            break;
        default:
            printf("error: %d\n", i);
            return;
        }
    }
 
    printf("\n\n");
    for (int i = 0; i < n; i++) {
        printf("\t");
        for (int j = 0; j < 3; j++) {
            int num = coord[i][j];
            if (!num)
                printf(":\t");
            else
                printf("%d\t", num);
 
        }
        printf("\n");
    }
    printf("\t_________________\n\tA\tB\tC\n");
 
    //frees the array
    
    for (int i = 0; i < n; i++)
        free(coord[i]);
    free(coord);    
    
    WAIT(1);    
}
 
void solveHanoi(int *arr, int n, int numDisks, int fromPeg, int toPeg)
{
    int sparePeg;
    if (numDisks == 0)
        return;
 
    switch (fromPeg + toPeg) {
    case 0 + 1:
        sparePeg = 2break;
    case 0 + 2:
        sparePeg = 1break;
    case 1 + 2:
        sparePeg = 0break;
    }
    solveHanoi(arr, n, numDisks - 1, fromPeg, sparePeg);
    moveDisk(arr, n, fromPeg, toPeg);
    solveHanoi(arr, n, numDisks - 1, sparePeg, toPeg);
}
 
void moveDisk(int *arr, int n, int fromPeg, int toPeg)
{
    for (int i = 0; i < n; i++) {
        if (arr[i] == fromPeg) {
            arr[i] = toPeg;
            break;
        }
    }
    showTower(arr, n);
}
cs


C언어에서 공식적으로 2진수 표현 방법을 지원하지 않아서 직접 만들었다.

void print_int_to_bin 함수는 unsigned int를 인자로 받아 그것을 2진수 형태로 변환해서, 8자리마다 한 칸씩 띄워서 출력한다.

#include <stdio.h>

void print_char_to_bin(unsigned char ch)
{
    int a;
    _Bool b;
    for (a = 0; a < 8; a++) {
        b = (ch & 1 << 8 - a - 1)? 1 : 0;
        printf("%d", b);
    }
}

void print_int_to_bin(unsigned int i)
{
    unsigned char int8;
    int len = sizeof(int);
    int a;
    printf("in bin: ");

    for(a = 0; a < len; a++)
    {
        int8 = i >> (len - a - 1) * 8 & 0xFF;
        print_char_to_bin(int8);
        printf(" ");
    }
    printf("\n");
}


int main(void)
{
    print_int_to_bin(0xffffffff);
    return 0;
}




/**** stack.h ****/
#include <stdbool.h>

void make_empty(void);       // 스택 내의 모든 노드를 삭제
bool is_empty(void)  ;       // 스택이 비어있는지 여부를 반환함
void push(int i);            // 정수 i를 스택의 맨 위에 push
int pop(void);               // 스택의 맨 위에 있는 정수를 pop


/**** main.c ****/
#include <stdio.h>
#include "stack.h"

int read_number(void);
int main(void) {
	int i;
	printf("연결 리스트로 스택 짜기\n"); 
	do {
		i = read_number();
		push(i);
	} while (i!=0);
	for(;;)
		printf("%d ", pop());
}

int read_number(void) {
	int i;
	printf("숫자 입력(0: 종료): ");
	scanf("%d", &i);
	return i;
}


/**** stack.c ****/
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>


struct node {
	int value;
	struct node *next;
};
/* external variables */
struct node *top = NULL;

void make_empty(void) {
	struct node *prev = NULL;
	while (p!= NULL) {
		prev = top;
		top = top->next;
		free(prev);
	}
}

bool is_empty(void) {
	return (top == NULL);
}

void stack_underflow(void) {
	printf("stack is empty.\n");
	exit(EXIT_FAILURE);
}

void stack_overflow(void) {
	printf("cannot allocate memory for new member in stack.\n");
	exit(EXIT_FAILURE);
}

bool push(int i) {
	struct node *new_top;
	new_top = malloc(sizeof(struct node));
	if (new_top == NULL)
		return false;
	new_top->value = i;
	new_top->next = top;
	top = new_top;
	return true;
}

int pop(void) {
	struct node *temp;
	int pop_value;
	if (is_empty())
		stack_underflow();
	temp = top;
	top = top->next;
	pop_value = temp->value;
	free(temp);
	return pop_value;
}


#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#define STACK_SIZE 100

/**** STACK ****/

/* external variables */
int contents[STACK_SIZE];
int *top_ptr = &contents[0];

void make_empty(void) {
	*top_ptr = 0;
}

bool is_empty(void) {
	return top_ptr == &contents[0];
}

bool is_full(void) {
	return top_ptr == &contents[STACK_SIZE];
}

void stack_underflow(void) {
	printf("stack underflow.\n");
	exit(EXIT_FAILURE);
}

void stack_overflow(void) {
	printf("stack overflow.\n");
	exit(EXIT_FAILURE);
}

void push(int i) {
	if (is_full())
		stack_overflow();
	else
		*top_ptr++ = i;
}

int pop(void) {
	if (is_empty())
		stack_underflow();
	else
		return *--top_ptr;
}


최대공약수(gcd, great common divisor)를 구하는 함수이다. 이 방법은 유클리드의 알고리즘(Euclid's algorithm으로 알려져 있다. 내용은 다음과 같다.

m과 n을 각각 양의 정수라고 하자(m이 n보다 크거나 같다). 만약 n이 0이면, 멈추고, m이 최대공약수가 된다.

그렇지 않다면, m을 n으로 나눈 나머지를 n에 저장하고, 원래 n의 값을 m에 저장한다. 이것을 n이 0이 될 때까지 반복한다.


int get_gcd(int m, int n) {

int temp;

if (m < n) {

temp = m;

m = n;

n = temp;

}

while (n > 0) {

temp = m%n;

m = n;

n = temp;

}

return m;

}


한편 두 수의 최대공약수를 구하면 최소공배수(lcm, least common multiple)도 쉽게 구할 수 있다. 

m * n = gcd(m, n) * lcm(m, n) 이므로, lcm(m, n) = m * n / gcd(m, n)이다.


int get_lcm(int m, int n) {

int gcd = get_gcd(m, n);

return m * n / gcd;

}

주사위 2개를 '시행회수' 만큼 굴린다.

각각의 주사위에서 1부터 6까지 값이 나온 빈도와 두 주사위의 합의 빈도를 표시.


참고로 두 주사위의 합에 대한 확률은 다음과 같다.

합: 확률(퍼센트)    확률(분수)

2 : 2.78    1/36

3 : 5.56    2/36

4 : 8.33    3/36

5 : 11.11    4/36

6 : 13.89    5/36

7 : 16.67    6/36

8 : 13.89    5/36

9 : 11.11    4/36

10 : 8.33    3/36

11 : 5.56    2/36

12 : 2.78    1/36


#include <stdio.h>

#include <time.h> // time function

#include <stdlib.h> // srand, rand function

#define 시행회수 1000000


int roll_dice(void);

int ar_dice1[6] = { 0 }, ar_dice2[6] = { 0 };

int sum[13] = { 0 };



int main(void) {

int i;

srand((unsigned)time(NULL));

for (i = 0; i < 시행회수; i++)

roll_dice();


printf("DICE 1 SUMMARY\n");

for (i = 0; i < 6; i++)

printf("%d: %d회 (%.3f%%)\n", i+1, ar_dice1[i], (float) ar_dice1[i]*100 / 시행회수);

printf("\nDICE 2 SUMMARY\n");

for (i = 0; i < 6; i++)

printf("%d: %d회 (%.3f%%)\n", i + 1, ar_dice2[i], (float)ar_dice2[i]*100 / 시행회수);

printf("\nSUM SUMMARY\n");

for (i = 2; i <= 12; i++)

printf("%d: %d회 (%.3f%%)\n", i, sum[i], (float)sum[i] * 100 / 시행회수);

}


int roll_dice(void) {

int dice1, dice2;

dice1 = rand() % 6 + 1;

dice2 = rand() % 6 + 1;

ar_dice1[dice1-1]++;

ar_dice2[dice2-1]++;

sum[dice1 + dice2]++;

}


결과(예시):


DICE 1 SUMMARY

1: 166469회 (16.647%)

2: 166873회 (16.687%)

3: 166482회 (16.648%)

4: 167126회 (16.713%)

5: 167062회 (16.706%)

6: 165988회 (16.599%)

DICE 2 SUMMARY

1: 166995회 (16.699%)

2: 166479회 (16.648%)

3: 166093회 (16.609%)

4: 166700회 (16.670%)

5: 166916회 (16.692%)

6: 166817회 (16.682%)


SUM SUMMARY

2: 27616회 (2.762%)

3: 55578회 (5.558%)

4: 83506회 (8.351%)

5: 111237회 (11.124%)

6: 138731회 (13.873%)

7: 166778회 (16.678%)

8: 139064회 (13.906%)

9: 110607회 (11.061%)

10: 83449회 (8.345%)

11: 55763회 (5.576%)

12: 27671회 (2.767%)

계속하려면 아무 키나 누르십시오 . . .


+ Recent posts