마방진이라는 희한한 물건이 있다.

 

정사각행렬에 1부터 홀수 n의 제곱까지 배치된 물건인데

 

이게 모든 행, 열, 그리고 대각선으로 합한 결과가 모두 같다.

 

마방진을 만드는 알고리즘과 이걸 또 C로 작성해보자.

 

여기서는 3x3 마방진을 예로 들 것이다.

 

마방진을 담기 위한 정사각 행렬 E에 대해서, 시작은 (0, n / 2) 부터다. (소숫점은 버린다)

 

[  ] [ 1 ] [  ]

[  ] [   ] [  ]

[  ] [   ] [  ]

 

오른쪽 위로 올라간다. 끝을 넘어서면 반대방향으로 돌아서 온다.

 

[   ] [ 1 ] [   ]

[   ] [   ] [   ]
 
[   ] [   ] [ 2 ]
[   ] [ 1 ] [   ]

[ 3 ] [   ] [   ]
 
[   ] [   ] [ 2 ]

 

오른쪽 위로 올라가다보니 1이 걸리적 거린다.

채울 곳에 이미 채워져 있다면 바로 밑 칸에 채우고 다시 오른쪽 위로 올라간다.

 

[   ] [ 1 ] [   ]

[ 3 ] [   ] [   ]
 
[ 4 ] [   ] [ 2 ]
[   ] [ 1 ] [   ]

[ 3 ] [ 5 ] [   ]
 
[ 4 ] [   ] [ 2 ]
[   ] [ 1 ] [ 6 ]

[ 3 ] [ 5 ] [   ]
 
[ 4 ] [   ] [ 2 ]
[   ] [ 1 ] [ 6 ]

[ 3 ] [ 5 ] [ 7 ]
 
[ 4 ] [   ] [ 2 ]
[ 8 ] [ 1 ] [ 6 ]

[ 3 ] [ 5 ] [ 7 ]
 
[ 4 ] [   ] [ 2 ]
[ 8 ] [ 1 ] [ 6 ]

[ 3 ] [ 5 ] [ 7 ]
 
[ 4 ] [ 9 ] [ 2 ]

 

완성. 굳이 3x3 마방진이 아니더라도, n이 3 이상의 홀수이기만 하다면 얼마든지 큰 마방진을 만들어낼 수 있다.

 

#include <stdio.h> // printf, puts

// 마방진의 크기?
#define  ORDER        3

void magic_square(int board[ORDER][ORDER]) {
    // 초기값 및 초기 위치 설정
    int i = 0, j = ORDER / 2, cnt = 1;
    
    // 마방진 채우기 시작 (크기^2 까지)
    while (cnt <= ORDER * ORDER) {
        // 잘못된 위치에 있다면 교정
        // 삐져나간 만큼 교정
        if (i < 0)           i = ORDER + i; 
        else if (i >= ORDER) i = i - ORDER;
        else if (j < 0)      j = ORDER + j;
        else if (j >= ORDER) j = j - ORDER;
        
        // 이미 채워져 있다면
        if ( board[i][j] != 0 ) {
            // 이전 위치의 밑으로 이동
            i += 2;
            j -= 1;
            
            // 다시 돌아가기
            continue;
        }
        
        // 현재 값 마방진에 기록
        board[i][j] = cnt;
        
        // 다음 칸으로 이동
        --i;
        ++j;
        ++cnt;
    }
}

int main(void) {
    // 너무 작거나 짝수라면?
    if (ORDER < 3 || !(ORDER % 2)) {
        return -1; // 나가
    }
 
    // 마방진용 배열 ( 0으로 초기화 )
    int ms_board[ORDER][ORDER] = { 0 }; 

    // 마방진 계산
    magic_square(ms_board);

    // 마방진 출력
    int i, j;
    for (i = 0; i < ORDER; ++i) {
        for (j = 0; j < ORDER; ++j) {
            printf("%4d ", ms_board[i][j]);
        }
        
        puts(""); // 개행
    }
    
    return 0;
}

 

scanf와 동적할당을 이용하면 사용자 입력에 따라 크기를 자유자재로 조정할 수 있는 마방진을 만들 수 있을 것이다.

 

그런데 좀 많이 큰 (대충 99x99 정도라고 치자) 마방진을 만들려고 하면 이런 경고가 뜰 수 있다.

 

영문

warning C6262: Function uses 'n' bytes of stack: exceeds /analyze:stacksize 'm'. Consider moving some data to heap

 

한국어

경고 C6262: 함수에서 'n'바이트의 스택을 사용하는데 이 크기가 /analyze:stacksize 'm'을(를) 초과합니다. 일부 데이터를 힙으로 이동하십시오.

 

이건 VS에서 설정된 함수당 스택 사이즈를 초과하는 변수를 선언해서 생긴 일인데, 해결 방법은 두가지다.

 

1. 전역변수로 이동

2. 동적할당 사용

3. 스택 크기 조정

 

전역변수는 데이터가 스택이 아닌 .data 영역에 배치되기 때문에 스택 크기 관련 문제에서 상대적으로 자유롭다.

동적할당또한 스택이 아닌 힙(heap) 영역에 데이터가 배치되기 때문에 (배열이 있던 자리에는 포인터가 대신한다) 저 경고를 해결할 수 있다.

 

마지막은 굳이 건들 이유가 없다. 그러니까 위에 둘 중 하나를 선택해서 적용해보면 되겠다.