레이블이 알고리즘인 게시물을 표시합니다. 모든 게시물 표시
레이블이 알고리즘인 게시물을 표시합니다. 모든 게시물 표시

2021년 3월 20일 토요일

[C언어] graph 탐색 BFS


https://ko.wikipedia.org/wiki/%EB%84%88%EB%B9%84_%EC%9A%B0%EC%84%A0_%ED%83%90%EC%83%89

 넓이 우선 탐색 : 1 2 5 9 3 6 8 10 4 7

인접 리스트를 이용한 구현

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
#include <stdio.h>
#include <stdlib.h>
#define TRUE 1
#define FALSE 0
#define MAX_VERTICES 20
 
typedef struct AdjNode
{
    int vertex;
    struct AdjNode *link;
} AdjNode;
 
typedef struct GraphType {
    int n;
    AdjNode *adj_list[MAX_VERTICES];
    int visited[MAX_VERTICES];
} GraphType;
 
void graph_init(GraphType *g)
{
    int v;
    g->= 0;
    for (v = 0; v<MAX_VERTICES; v++){
        g->adj_list[v] = NULL;
        g->visited[v] = 0;
    }
}
 
void insert_vertex(GraphType *g, int v)
{
    if (((g->n) + 1> MAX_VERTICES) {
        printf("error");
        return;
    }
    g->n++;
}
 
void insert_edge(GraphType *g, int u, int v)
{
    AdjNode *AdjNode;
    if (u > g->|| v > g->n) {
        printf("error");
        return;
    }
    AdjNode = (struct AdjNode *)malloc(sizeof(AdjNode));
    AdjNode->vertex = v;
    // 이전에 저장된 주소는 제일 마지막에 연결되었던 AdjNode이므로 이 주소를 지금 생성한 AdjNode의 link에 연결하여 인접 리스트를 구성한다.
    AdjNode->link = g->adj_list[u];
    // 새로 추가되는 AdjNode를 adj_list를 이용해서 access 가능하도록 저장
    g->adj_list[u] = AdjNode;
 
}
 
#define MAX_QUEUE_SIZE 100
typedef int element;
typedef struct {
    element  queue[MAX_QUEUE_SIZE];
    int  front, rear;
} QueueType;
 
void error(char *message)
{
    fprintf(stderr, "%s\n", message);
    exit(1);
}
 
void init(QueueType *q)
{
    q->front = q->rear = 0;
}
 
int is_empty(QueueType *q)
{
    return (q->front == q->rear);
}
 
int is_full(QueueType *q)
{
    return ((q->rear + 1) % MAX_QUEUE_SIZE == q->front);
}
 
void enqueue(QueueType *q, element item)
{
    if (is_full(q))printf("q full");
    q->rear = (q->rear + 1) % MAX_QUEUE_SIZE;
    q->queue[q->rear] = item;
}
 
element dequeue(QueueType *q)
{
    if (is_empty(q))printf("q empty");
    q->front = (q->front + 1) % MAX_QUEUE_SIZE;
    return q->queue[q->front];
}
 
void bfs_list(GraphType *g, int v)
{
    AdjNode *w;
    QueueType q;
    init(&q);
    g->visited[v] = TRUE;
    printf("%d ", v); 
    enqueue(&q, v);
    while (!is_empty(&q)) {
        v = dequeue(&q);
        for (w = g->adj_list[v]; w; w = w->link)
            if (!g->visited[w->vertex]) {
                g->visited[w->vertex] = TRUE;
                printf("%d ", w->vertex);
                enqueue(&q, w->vertex);
            }
    }
}
 
main()
{
    int i;
    GraphType g;
 
    graph_init(&g);
 
    for (i = 1; i<=10; i++)
        insert_vertex(&g, i);
    insert_edge(&g, 19);
    insert_edge(&g, 15);
    insert_edge(&g, 12);
    insert_edge(&g, 23);
    insert_edge(&g, 34);
    insert_edge(&g, 58);
    insert_edge(&g, 56);
    insert_edge(&g, 67);
    insert_edge(&g, 910);
    bfs_list(&g, 1);
}
cs


실행 결과


1
1 2 5 9 3 6 8 10 4 7
cs



2017년 8월 27일 일요일

문제 풀기(서로 다른 숫자를 사용해서 연산해 내기, 순열이용) in Java


어느날 우리애가 풀어보라면 들고 왔습니다. 선생님이 내줬다는 문제인데요....


과연 풀려면 어떻게 할까 생각해보다가, 오랜만에 PC로 풀어볼려고 마음먹었습니다.  정리하자면 아래와 같은 문제입니다.

문제) 1~9까지 서로 다른 숫자를 사용해 들어갈 숫자를 완성하세요.

   A B
X    C
--------
   D E
+ F G
--------
   H I

이중에 힌트로 B=7,I=3가 주어졌다고 합니다.
힌트 없이 풀어보도록 하겠습니다.

각각의 값은 중복이 되지 않으므로 순열이라고 보면됩니다.
방법은 A=1...9까지 선택하고 B=1....9 까지 선택하고... 이런식으로 I까지 선택한 후 이전에 선택된 값이라면 무시하고 식이 맞는지 확인하는 방법으로 구해보았습니다.
여러개의 루프를 사용하면서 안쪽에서 다시 루프를 사용하고 아래와 같은 코드에 의해 이미 선택된 값이라면 선택이 되지 않도록 합니다.
if(usedflag[b])continue;

여러개의 루프를 사용하는 방법:
package testProject;

public class Test3 {

 public static void main(String[] args) {
  
  test1();
  
 }
 

 private static void test1() {
  boolean usedflag[]={false,false,false,false,false,false,false,false,false,false};
  
  for(int a=1;a<=9;a++){
   usedflag[a]=true;
   for(int b=1;b<=9;b++){
    if(usedflag[b])continue;
    usedflag[b]=true;
    for(int c=1;c<=9;c++){
     if(usedflag[c])continue;
     usedflag[c]=true;
     for(int d=1;d<=9;d++){
      if(usedflag[d])continue;
      usedflag[d]=true;
      for(int e=1;e<=9;e++){
       if(usedflag[e])continue;
       usedflag[e]=true;
       for(int f=1;f<=9;f++){
        if(usedflag[f])continue;
        usedflag[f]=true;
        for(int g=1;g<=9;g++){
         if(usedflag[g])continue;
         usedflag[g]=true;
         for(int h=1;h<=9;h++){
          if(usedflag[h])continue;
          usedflag[h]=true;
          for(int i=1;i<=9;i++){
           if(usedflag[i])continue;
           usedflag[i]=true;
           if(((a*10+b)*c)==((d*10)+e)){
            if(((d*10)+e)+((f*10)+g)==(h*10)+i){
             System.out.println("a="+a+",b="+b+",c="+c+",d="+d+",e="+e+",f="+f+",g="+g+",h="+h+",i="+i);
            }
           }
           usedflag[i]=false;
          }
          usedflag[h]=false;
         }
         usedflag[g]=false;
        }
        usedflag[f]=false;
       }
       usedflag[e]=false;
      }
      usedflag[d]=false;
     }
     usedflag[c]=false;
    }
    usedflag[b]=false;
   }
   usedflag[a]=false;
  }
 }
 
 
}

결과:
a=1,b=7,c=4,d=6,e=8,f=2,g=5,h=9,i=3

위 방법대로 할경우 문제가 있습니다. 많은 변수를 사용하게 되면 굉장히 코드도 길어지고 그러다보면 실 수 할 수도 있게 됩니다.
그렇다면 코드의 중복되는 부분을 함수로 만들어서 호출해주는 방법을 고민하게 됩니다.

기본적으로 구현은 자기자신을 호출해주는 재귀부와 조건이 맞으면 더이상 재귀를 동작 안하게 하는 두 부분으로 구성됩니다.
재귀부:
  for(int i=1;i<=9;i++){
   test2(depth+1,usedflag,selected);
  }
더 이상 재귀를 동작 안하게 하는 부분:
  if(depth == 9) {
   return;
  }

재귀를 동작 안하게 하려면 얼마나 호출되었는지 관리가 필요합니다. 여기에서는 depth 변수를 이용하여 관리를 하고(자신을 호출시 depth+1 사용) 특정 조건에 맞으면 더 이상의 재귀 동작을 멈추게 합니다. (if(depth==XX) return 조건 사용)

아래는 구현한 내용입니다.

재귀 호출을 이용한 방법:
package testProject;

public class Test3 {

 public static void main(String[] args) {

  boolean usedflag[]={false,false,false,false,false,false,false,false,false,false};
  int selected[]={-1,-1,-1,-1,-1,-1,-1,-1,-1};
  test2(0, usedflag, selected);
  
 }
 
 private static void test2(int depth, boolean usedflag[], int selected[])
 {
  if(depth == 9) {
   if(((selected[0]*10+selected[1])*selected[2])==((selected[3]*10)+selected[4])){
    if(((selected[3]*10)+selected[4])+((selected[5]*10)+selected[6])==(selected[7]*10)+selected[8]){
     System.out.println("a="+selected[0]+",b="+selected[1]+",c="+selected[2]+",d="+selected[3]+",e="+selected[4]+",f="+selected[5]+",g="+selected[6]+",h="+selected[7]+",i="+selected[8]);
    }
   }
   return;
  }
  for(int i=1;i<=9;i++){
   if( usedflag[i] ) continue;
   usedflag[i]=true;
   selected[depth]=i;
   test2(depth+1,usedflag,selected);
   usedflag[i]=false;
  }
 }
 
}


순열에 대한 자세한 내용은 아래 링크를 확인해보세요.
http://swlock.blogspot.kr/2016/11/permutation-combination-algorithm_13.html

2016년 11월 13일 일요일

중복 순열 (Permutation) 조합(combination)알고리즘 생성 방법 algorithm (full search 부터 이해)

중복 순열 (Permutation) 조합(combination)알고리즘 생성 방법 algorithm (full search 부터 이해)


앞에서 아래와 같이 정리했었습니다.

N=3 {1,2,3} 존재시 R=2
순열은 N개의 항목에서 R개를 선택할때 순서를 가지면서 나열하는 방법입니다.
{1,2},{1,3},{2,1},{2,3},{3,1},{3,2}

중복 순열은 R개 선택시 중복이 가능하다는 의미입니다.
{1,1},{1,2},{1,3},{2,1},{2,2},{2,3},{3,1},{3,2},{3,3}

조합은 N개의 항목에서 R개를 선택할때 순서를 갖지지 않은면서 나열하는 방법입니다.
{1,2},{1,3},{2,3}

중복 조합은 R개 선택시 중복이 가능하다는 의미입니다.
{1,1},{1,2},{1,3},{2,2},{2,3},{3,3}


일단 전체 탐색을 하는 내용을 그림으로 정리해 보겠습니다.


그림이 커져서 오른쪽 절반 정도는 그림을 생략하였습니다.
이 그림의 의미는 4개의 data가 주어질때 3개를 선택하는 방법에 대한 내용입니다. 순열로 할지 조합을 할지 중복이 되게 할지는 아직 고민하지 않고 그린 그림입니다.
선택을 할때마다 depth가 증가하게 됩니다. R이 depth가 되면 선택된 항목을 출력하면 될겁니다.
아래는 2->3->1 3개를 선택하는 순서를 나타냅니다.



각각 선택해서 출력하는 알고리즘 입니다. 즉 처음에는 1-1-1, 그리고 1-1-2, 1-1-3, 1-1-3, 1-1-4, 1-2-1 ... 이런식으로 출력이되어야 합니다.
이걸 코드로 구현해 보도록 하겠습니다.

그래서 첫번째 depth 0 에서는 1, 2, 3, 4 른 순차적으로 선택한 후 다음 depth로 넘기는 과정이 필요합니다.
코드를 대충 만들어 보면 아래와 같습니다.
full()
{
  int i;
  for(i=1;i<=N;i++){
    selected[]=i;
    full(depth+1);
  }
}
그다음 고민해볼것은 선택되는것을 어디엔가 저장해야할텐데요 그걸 selected 배열에 넣었습니다. 순차적으로 뒤에 추가해주면 됩니다.
그림에서 잘보면 depth가 증가될때마다 하나씩 선택되고 있기 때문에 선택되는 갯수는 depth의 수와 일치합니다.
따라서 선택 하는 코드는 selected[]=i; => selected[depth]=i; 로 해주면 됩니다.
그리고 전체적으로 얼마나 depth가 진행되고 있는지 depth를 인자로 넘겨받아야 할 겁니다.
지금까지 코드를 만들어 보면 아래와 같습니다.
full(int depth)
{
  int i;
  for(i=1;i<=N;i++){
    selected[depth]=i;
    full(depth+1);
  }
}

full(depth+1); 함수 호출 후 복귀했을때 다른 값을 선택해야 합니다. 즉 이 의미는 아래와 같이 복귀하게되면 더이상 이전에 선택했던것을 취소해야한다는 의미입니다.
아래과 같이 구현해야합니다.
    selected[depth]=i;
    full(depth+1);
    selected[depth]=-1;
하지만 아래 그림을 보면 다시 해당 depth로 돌아왔을때 새로 선택하는 index가 depth에 의해 결정되므로 이전 선택했던 depth를 취소할 필요는 없습니다. 즉  selected[depth]=-1; 이건 다음번 loop를 진행할때 selected[depth]=i; 이 코드에 의해서 필요없는 코드라는 의미입니다.

마지막으로 종료하는 코드를 넣어야 합니다.
재귀 종료 조건은 여기에서는 3개 선택될때까지, R=3 즉 R==depth 가 되면 모두 선택하고나서 다시 재귀를 들어왔다는 의미입니다. depth 가 0일때 하나를 선택하게되고 R=depth-1 일때 모두 선택이 되고 다음번 depth가 되는 재귀함수를 들어올때 모두 선택이 완료되게 됩니다.

void full(int depth)
{
  int i;
  if( R==depth){
    // 모두 선택 되었음 출력하기
    for(i=0;i<N;i++){
      printf("%d ",selected[i]);
    }
    printf("\n");
    return;
  }
  for(i=1;i<=N;i++){
    selected[depth]=i;
    full(depth+1);
  }
}

아래는 완성된 코드입니다.

Cpasted 1 second ago: 
#include <stdio.h>

#define N 4
#define R 3

int selected[R];

void full(int depth)
{
  int i;
  if( R==depth){
    // 모두 선택 되었음 출력하기
    for(i=0;i<R;i++){
      printf("%d ",selected[i]);
    }
    printf("\n");
    return;
  }
  for(i=1;i<=N;i++){
    selected[depth]=i;
    full(depth+1);
  }
}

int main()
{
  full(0);
  return 0;
}


Output:
1 1 1 
1 1 2 
1 1 3 
1 1 4 
1 2 1 
1 2 2 
1 2 3 
1 2 4 
1 3 1 
1 3 2 
1 3 3 
1 3 4 
1 4 1 
1 4 2 
1 4 3 
1 4 4 
2 1 1 
2 1 2 
2 1 3 
2 1 4 
2 2 1 
2 2 2 
2 2 3 
2 2 4 
2 3 1 
2 3 2 
2 3 3 
2 3 4 
2 4 1 
2 4 2 
2 4 3 
2 4 4 
3 1 1 
3 1 2 
3 1 3 
3 1 4 
3 2 1 
3 2 2 
3 2 3 
3 2 4 
3 3 1 
3 3 2 
3 3 3 
3 3 4 
3 4 1 
3 4 2 
3 4 3 
3 4 4 
4 1 1 
4 1 2 
4 1 3 
4 1 4 
4 2 1 
4 2 2 
4 2 3 
4 2 4 
4 3 1 
4 3 2 
4 3 3 
4 3 4 
4 4 1 
4 4 2 
4 4 3 
4 4 4 



순열 만들기
http://swlock.blogspot.com/2016/11/permutation-combination-algorithm_13.html
조합 만들기
http://swlock.blogspot.com/2016/11/permutation-combination-algorithm_45.html

중복 순열 (Permutation) 조합(combination)알고리즘 생성 방법 algorithm (combination 구현)

중복 순열 (Permutation) 조합(combination)알고리즘 생성 방법 algorithm (combination 구현)

이전글
http://swlock.blogspot.com/2016/11/permutation-combination-algorithm-full.html

계속해서 조합에 대해 알아보겠습니다.
출발은 full search에서부터 출발합니다.

여기에서 조합할때 선태되는 부분에 원을 그려보도록 하겠습니다.

중복이 안되어야하고 순서도 관계없으니 {1,2,3} {1,2,4} {1,3,4} {2,3,4} 더는 없는것 같습니다.
그림을 봐서는 어떤 규칙성을 찾기가 어려워 보입니다.



루프를 돌기 위한을 시작점의 숫자에서, 부모 depth 와 자식 depth와의 관계에서 보면 자식은 부모 +1 만큼 더해주는 위치에서 시작합니다.  왜냐하면 순열과 다르게 조합은 부모보다 같거나 작은 index의 경우 이미 검토가 끝났기 때문입니다. 이것을 코드로 구현해보면 다음과 같습니다.

void full(int depth,int index)
{
  int i;
  if( R==depth){
    // 모두 선택 되었음 출력하기
    return;
  }
  for(i=index;i<=N;i++){
    selected[depth]=i;
    full(depth+1,i+1);
  }
}
주의할점은 재귀 호출시 여기 예제에서는 1부터 나오기 때문에 시작지점을 1로 호출했습니다. 아래 완성된 코드를 보면 됩니다.


Cpasted just now: 
#include <stdio.h>

#define N 4
#define R 3

int selected[R];

void full(int depth,int index)
{
  int i;
  if( R==depth){
    // 모두 선택 되었음 출력하기
    for(i=0;i<R;i++){
      printf("%d ",selected[i]);
    }
    printf("\n");
    return;
  }
  for(i=index;i<=N;i++){
    selected[depth]=i;
    full(depth+1,i+1);
  }
}

int main()
{
  full(0,1);
  return 0;
}


Output:
1
2
3
4
1 2 3 
1 2 4 
1 3 4 
2 3 4 

그리고 좀 더 성능을 높이기 위해서는 여기에서는 2,3,4 까지 진입하게되는데 4는 진입할 필요가 없으므로 i<=N 조건을 추가로 변경이 가능하리라 봅니다.

이번에는 중복이 가능한 조합을 살펴보겠습니다.
중복 가능한 조합의 경우 그림에 표시를 해봤습니다. 빨간색


자식과 부모의 관계를 파란색으로 표시해봤습니다.
자식의 loop와 부모 loop의 index가 동일하게 하면 될것 같습니다.
소스에서 i+1 => i 로 변경하면 됩니다.
void full(int depth,int index)
{
  int i;
  if( R==depth){
    // 모두 선택 되었음 출력하기
    return;
  }
  for(i=index;i<=N;i++){
    selected[depth]=i;
    full(depth+1,i);
  }
}


Cpasted 1 second ago: 
#include <stdio.h>

#define N 4
#define R 3

int selected[R];

void full(int depth,int index)
{
  int i;
  if( R==depth){
    // 모두 선택 되었음 출력하기
    for(i=0;i<R;i++){
      printf("%d ",selected[i]);
    }
    printf("\n");
    return;
  }
  for(i=index;i<=N;i++){
    selected[depth]=i;
    full(depth+1,i);
  }
}

int main()
{
  full(0,1);
  return 0;
}


Output:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1 1 1 
1 1 2 
1 1 3 
1 1 4 
1 2 2 
1 2 3 
1 2 4 
1 3 3 
1 3 4 
1 4 4 
2 2 2 
2 2 3 
2 2 4 
2 3 3 
2 3 4 
2 4 4 
3 3 3 
3 3 4 
3 4 4 
4 4 4