알고리즘 문제를 풀고 있는데, 어느 부분에서 시간이 오래걸리는지 찾아주실 분...

leejk9592의 이미지

CTU Open Contest 2014년도 대회문제 중
Karel the Robot 이란 알고리즘 문제를 풀고 있습니다

일단 제가 도움을 요청드리는 부분은 제 알고리즘 소스와 모범답안의 소스는
변수이름 정도만 다르고 순서도는 거의 같다는 것이 저의 생각입니다.
(아닐 수도 있지만 제 소스와 답안 소스를 비교해보시면 수긍하시리라 생각합니다)

그런데 실행시간에 있어서는 너무 큰 시간 차이를 보여주는데,
이게 어느 부분에서 시간 차이가 생기는지 알아내려 노력해봤지만
알 수가 없어 도움을 요청하고자 합니다...

* 알고리즘 문제를 간단히 한글로 설명드리자면 Karel 이란 로봇이 있는데

1. 3 종류의 명령어를 실행할 수 있습니다 전진(S), 시계 방향 회전(R), 반시계 방향 회전(L)

2. 문제에는 이 로봇이 수행할 명령어 집합이 입력되고 최대 저장공간은 10칸(즉, 1 ~ 10개의 명령어 저장가능)으로

3. 마지막 명령어가 실행된 후에는 첫 번째 명령어로 돌아가 계속 실행하게 됩니다.

4. 문제에는 이 로봇이 명령을 수행하게 될 미로가 입력되고(높이, 너비, 지형) 지형은 탈출구, 빈공간, 벽이 입력됩니다.
(탈출구와 빈공간은 합쳐서 자유 공간으로 불리게 됩니다)

5. 로봇은 문제에서 제시된 지형의 모든 자유 공간에서 시작되며, 시작시 무조건 을 바라보고 시작합니다.

6. 문제의 답안은 로봇이 문제에서 제시한 명령어를 실행하여 탈출 가능한 자유 공간 시작 위치의 개수를 출력하면 됩니다.
= 명령어 집합을 영원히 실행하여도 탈출이 불가능한 경우의 수를 알아내어 전체 경우의 수에서 빼준 개수를 출력

7. 문제의 자유공간(탈출구 포함) 개수와 문제의 답안이 일치한다면 OK를 출력하면 됩니다.

※ 대회 모범답안은 입력이 이상하게 되길래 첨부해 놓았습니다.(모범 답안은 콘솔 입출력)

제 알고리즘 소스는 아래와 같고(입출력은 첨부된 파일을 통해)

#include "stdio.h"
#include "Windows.h"
#include "time.h"

char strCmd[11] = {0, };
char arsMaze[100][101] = {0, };

int unRoadAlreadyBeen[100][100][10][4] = {0, };
int nTry = 0;

int nFreeTerrain;

int x, y;

int nHeight;
int nWidth;
int nCmdLen;

int dir;

int nResult;

void Init();
void InitRobot(int x, int y);

bool check(int x, int y);

void DoAllCase();
void DoCmd();

int main()
{
FILE *in, *out;

in = fopen("input.txt", "r+");
out = fopen("output.txt", "w");

int i;
bool bContinue;

do
{
nResult = 0;
nFreeTerrain = 0;

if( fscanf(in, "%d %d", &nHeight, &nWidth) != 2 )
break;

for( i = 0 ; i fscanf(in, "%s", arsMaze[i]);

fscanf(in, "%d", &nCmdLen);

bContinue = (bool)(fscanf(in, "%s", strCmd) != EOF);

DoAllCase();

if( nResult == nFreeTerrain )
fprintf(out, "OK\n");
else
fprintf(out, "%d\n", nResult);
}
while( bContinue );

fclose(in);
fclose(out);

return 0;
}

void DoAllCase()
{
clock_t start, end;
int i, j, cnt;

start = clock();

for( i = 0 ; i {
for( j = 0 ; j {
if( arsMaze[i][j] == 'X' )
continue;

nFreeTerrain++;
nTry++;

cnt = 0;
dir = 0;

x = j; y = i;

while( true )
{
if( arsMaze[y][x] == 'E' )
{
nResult++;
break;
}

if( unRoadAlreadyBeen[y][x][cnt][dir] == nTry )
break;
else
unRoadAlreadyBeen[y][x][cnt][dir] = nTry;

switch( strCmd[cnt] )
{
case 'S':
switch( dir )
{
case 0:
if( check(x, y - 1) )
y -= 1;
break;
case 1:
if( check(x + 1, y) )
x += 1;
break;
case 2:
if( check(x, y + 1) )
y += 1;
break;
case 3:
if( check(x - 1, y) )
x -= 1;
break;
}
break;
case 'L':
dir = (dir + 3) % 4;
break;
case 'R':
dir = (dir + 1) % 4;
break;
}

cnt = (cnt + 1) % nCmdLen;
}
}
}

end = clock();

printf("[%d] : %f\n", nTry, ((double)(end - start)) / CLOCKS_PER_SEC);
}

bool check(int x, int y)
{
return (x = nWidth || y = nHeight || arsMaze[y][x] == 'X') ? false : true;
}

File attachments: 
첨부파일 크기
Plain text icon input.txt1.01 MB
Plain text icon output.txt1.15 KB
Plain text icon ContestSolution.txt1.64 KB
leejk9592의 이미지

글 삭제가 불가능 하다는 사실을 알게 되었네요...

이왕 삭제가 불가능한 김에 제가 찾게된 해답이나 적어 놓아보려고 합니다.

제 소스는 한 케이스 내에서 <시작위치가 변할 때 마다> nTry 값을 증가시켜

이미 (n번째 명령어이고, d방향 일때) 지나갔었던 곳인지를 unRoadAlreadyBeen

에 nTry 값을 이용하여 판별하는 방식이었습니다.

모범 답안과 비교하면서 보다보니 같은 케이스 내에서 <시작위치가 변할 때 마다>

nTry 값을 증가시킬 필요가 없다는 것을 깨닫게 되었습니다.

즉, 같은 케이스 내에서 라면 시작위치에 상관없이 같은 nTry 값을 사용하면 되고,

케이스가 변할 때만 nTry 값을 증가시키면 된다는 것을 깨닫은 후

코드 nTry++ 의 위치를 main 루프로 옮기고 나니 엄청난 시간을 아끼게 되었습니다...

혹시나 해결해 주시려던 분들이 계셨다면 감사의 말씀을 전하고 싶습니다.

rgbi3307의 이미지

알고리즘에서 수행시간에 가장 많이 영향을 주는 것이 반복문(while, for)입니다.
반복문이 몇번 수행되는지는 반복문안의 제어변수를 가지고 계산할 수 있습니다.
위에서 제시한 알고리즘은 아래와 같이 반복문이 4개 충첩되어 있고,
반복 회수를 계산해 보면,

while (input.txt안의 명령라인 수) //c
  for (미로높이) //y
    for (미로폭) //x
      while(true) //n

반복회수(N) = c * y * x * n

예를들어 input.txt 파일안에 명령라인 수가 10개, 미로높이 100, 미로폭 100 이면
N = 10 * 100 * 100 * n = 100000n

여기에서 n이 실행시간에 또 많이 영향을 주는 관심 대상입니다.

n은 while(true) 루프를 빠져 나오는 if 조건에 따라서

최소 n = 1,
최대 n = y * x * 10 * 4 가 됩니다.

따라서 위의 알고리즘은 최악의 경우
N = 100000 * (100 * 100 * 10 * 4)
= 4 * 10000000000
= 400억 입니다.

답글에서 nTry 전역변수는 미로공간을 이미 방문했었는지 판단하는 변수인데,
DoAllCase() 함수 안쪽의 루프에서 증가시키면 위의 결과가 나옵니다.

이 변수를 DoAllCase() 함수 바깥쪽에서 증가하면, 즉 main() 함수에서 증가시키고
DoAllCase() 함수를 실행하면 n의 값이 많이 줄어드는 결과입니다.

알고리즘에서 위와 같이 반복문의 제어변수를 어떻게 운용하는가에 따라서
효율성이 결정되는 예들이 많은데, 대표적인 것들로 정렬 알고리즘, 탐색 알고지즘등이 있습니다.
또한 자료구조도 중요한데,
위에서는 자료구조를 배열을 사용했지만, 트리구조를 사용하면 좀더 좋은 결과가 나올수도 있는데,
문제는 주어진 문제를 가장 효율적으로 해결할 수 있는 알고지즘을 구현하려면
많은 연습과 노력이 필요할듯 합니다.

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.