헤드퍼스트 디자인 패턴 책에는 오리를 이용하여 스트래티지 패턴을 설명하였습니다. 스트래티지 패턴은 OOP의 중요한 원칙 중 하나인 “상속보다는 합성” 원칙을 직접 표현하고 있기 때문에 반복학습을 통한 확실한 이해를 하기 위해 오리가 아닌 액션어드벤쳐 게임을 주제로 삼아 이 패턴을 다시한번 설명하도록 하겠습니다.

전체 클래스와 인터페이스를 한눈에 보기 위해 starUML을 이용해 클래스 다이어그램을 작성해 보았습니다.

Strategy Pattern - Action Adventure Game

Strategy Pattern - Action Adventure Game

먼저 Character 클래스는 Knight 와 Wizard 클래스의 추상층입니다. 각 직업들이 가지는 공통적인 특성을 정의하여 서브클래스에서 확장(상속) 사용하도록 합니다.

모든 캐릭터들은 무기를 사용할 수 있고 갑옷을 입을 수 있기 때문에 이 두가지의 기능을 커다란 알고리즘으로 분류하여 각각 인터페이스를 만듭니다.
무기를 사용하는 것은 useWeapon() 메서드를 호출하여 무기를 사용하는 모습을 그래픽으로 처리하게 하고, 갑옷을 착용하면 갑옷 종류에 따라 다른 보너스 hp 를 얻도록 할 계획입니다.

아래의 IArmor 인터페이스를 보면 wearBonus() 메서드의 리턴값이 int 로 되어 있음을 볼 수 있습니다. 이것은 갑옷 종류에 따른 hp 보너스 포인트를 리턴할 수 있도록 한 것입니다.

0
1
2
3
4
5
6
package
{
	public interface IArmor
	{
		function wearBonus():int;
	}
}

IArmor 인터페이스를 구상하는 클래스중 하나인 Plate 클래스를 보면 인터페이스에 있는 메서드를 구현하고 있고 wearBonus() 메서드에서 int 값 50을 리턴하고 있습니다.

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package
{
	public class Plate implements IArmor
	{
		public function Plate ()
		{
			trace( "판금갑옷을 착용하였습니다." )
		}
 
		public function wearBonus():int
		{
			trace( "현재 hp에 + 50" )
			return 50;
		}
	}
}

이런식으로 아무것도 입지 않은 Naked 클래스를 포함하여, 필요한 갑옷마다 hp 보너스를 설정하여 인터페이스를 구현합니다.

그럼 이제 IWeapon 과 IArmor 인터페이스를 사용하게 되는 Character 클래스를 보면 모든 캐릭터들이 공통적으로 필요한 변수를 설정하고 몇가지에는 getter 를 설정하였습니다. 각 메서드에 대한 설명은 주석으로 달아놓았습니다.

0
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
package
{
	public class Character
	{
		protected var weapon:IWeapon;
		protected var armor:IArmor;
		protected var _hp:int = 0;
		protected var _name:String;
		protected var _characterClass:String;
 
		public function Character ()
		{
			trace( "-----새로운 캐릭터를 생성합니다.-----" )
		}
 
		//무기를 장비함
		public function equipWeapon( $weapon:IWeapon ):void
		{
			weapon = $weapon;
		}
 
		//무기를 사용함
		public function weaponAction():void
		{
			weapon.useWeapon()
		}
 
		//갑옷을 입음
		public function equipArmor( $armor:IArmor ):void
		{
			armor = $armor;
 
			//해당 갑옷의 보너스 hp를 리턴받아 캐릭터의 hp에 더함
			_hp += armor.wearBonus();
 
			//방어력, 이동속도 등 다른 착용효과 구현 가능
		}
 
		//현재 장비하고 있는 무기를 리턴
		public function getObjectWeapon():IWeapon
		{
			return weapon
		}
 
		//캐릭터 이름, 직업, hp 현황을 리턴
		public function getStat():String
		{
			return _name + "(" + _characterClass + ") 현재 hp : " + _hp
		}
 
		public function get hp():int { return _hp; }
 
		public function get name():String { return _name; }
 
		public function get characterClass():String { return _characterClass; }
	}
}

그리고 Character 클래스를 확장한 Knight 클래스에서는 Character 클래스에서 protected 로 선언하여 상속한 변수들에 초기값을 입력하여 캐릭터를 생성하게 됩니다.

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package
{
	public class Knight extends Character
	{
		public function Knight ( $name:String )
		{
			_hp = 100;
			_name = $name;
			_characterClass = "기사"
			trace( name , " (이)라는 이름을 가진 ", characterClass ," 캐릭터를 생성하였습니다." )
			weapon = new Fist();
			armor = new Naked();
		}
	}
}

여기까지 하면 주먹( new Fist(); ) 이라는 무기와 아무것도 걸치지 않은 맨몸( new Naked(); )의 hp 100짜리 기사 캐릭터가 생성 됩니다.

IArmor를 짚어나가고 있으므로 그 부분을 집중해서 보도록 하겠는데요, 여기서 중요한 부분은 Character 클래스에서 IArmor 데이터형으로 인스턴스 변수를 생성한 armor 에[01] new Naked(); 라는 갑옷를 대입하여 현재 갑옷상태를 아무것도 입지 않은 상태로 초기 지정하였습니다. 즉, 추상층[02] 에서는 상위 형(type)인 인터페이스 형(type)으로 변수 선언을 하고, 구상층에서 상속받은 그 변수에 구체적인 형태를 대입하는 것입니다. 이렇게 하는 이유는 아래 호스트 코드에서 살펴보도록 하겠습니다.

0
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
package
{
	import flash.display.Sprite;
 
	public class Main extends Sprite
	{
		public function Main():void
		{
			var player1:Character = new Knight( "세계의끝" );
							//출력 : -----새로운 캐릭터를 생성합니다.-----
							//출력 : 세계의끝  (이)라는 이름을 가진  기사  캐릭터를 생성하였습니다.
							//출력 : 맨주먹입니다. (무기가 없습니다)
							//출력 : 아무것도 착용하지 않았습니다.
			trace( player1.getStat() );		//출력 : 세계의끝(기사) 현재 hp : 100
			player1.weaponAction();		//출력 : 주먹을 휘두릅니다.
			player1.equipWeapon( new Sword() );	//출력 : Sword 를 장비하였습니다.
			player1.weaponAction();		//출력 : Sword 를 휘두릅니다
			player1.equipArmor( new Plate() );	//출력 : 판금갑옷을 착용하였습니다.
							//출력 : 현재 hp에 + 50
			trace( player1.getStat() );		//출력 : 세계의끝(기사) 현재 hp : 150
			trace( player1.getObjectWeapon() );	//출력 : [object Sword]
 
			var player2:Character = new Wizard( "댄스댄스댄스" );
							//출력 : -----새로운 캐릭터를 생성합니다.-----
							//출력 : 댄스댄스댄스  (이)라는 이름을 가진  마법사  캐릭터를 생성하였습니다.
							//출력 : 맨주먹입니다. (무기가 없습니다)
							//출력 : 아무것도 착용하지 않았습니다.
			trace( player2.getStat() );		//출력 : 댄스댄스댄스(마법사) 현재 hp : 80
			player2.weaponAction();		//출력 : 주먹을 휘두릅니다.
			player2.equipWeapon( new Staff() );	//출력 : Staff 를 장비하였습니다.
			player2.weaponAction();		//출력 : Staff 에서 마법을 발사합니다.
			player2.equipArmor( new Leather() );	//출력 : 가죽갑옷을 착용하였습니다.
							//출력 : 현재 hp에 + 30
			trace( player2.getStat() );		//출력 : 댄스댄스댄스(마법사) 현재 hp : 110
			trace( player2.getObjectWeapon() );	//출력 : [object Staff]
		}
	}
}

8 번 라인을 보면, 위에서 언급한 것과 마찬가지로 new Knight( “세계의끝” ) 를 상위 type 인
var player1:Character 로 받고 있는데요, 이런 형태의 캐스팅을 하게 되면, 인스턴스 변수를 사용할 때에 그게 어떤 구체적인 형태인지 호스트코드에서 알 필요 없이 알아서 사용됩니다.
위의 player1은 type 이 Character 지만 Knight 클래스라는 것을 player1 은 이미 알고 있다는 의미 입니다. 마찬가지로 Character 클래스의 equipArmor() 메서드에서 armor.wearBonus() 를 하면 캐릭터가 뭘 입었는 몰라도,[03] 보너스 hp는 조사하면 다 나온다는 것이죠.

이러한 구조는 아래와 같은 절차지향 코드의 조건문을 상쇄한 효과를 냅니다.

0
1
2
3
4
5
6
7
8
9
10
function wearBonus():int {
	var bonus:int;
	if ( armor == "Plate" ) {
		bonus = 50;
	} else if ( armor == "Leather" ) {
		bonus = 30;
	} else {
		bonus = 0;
	}
	return bonus;
}

게다가 새로운 갑옷이 늘어날 때마다 else if 를 추가하지 않아도 됩니다. 무엇보다 확장에 유연하고 수정사항이 생겼을때 어디를 얼마만큼 고쳐야 하는지 명확하게 알 수 있게 됩니다.

zelda_link_to_the_past_stamp

Strategy Pattern : Action Adventure Game 액션스크립트 코드 다운로드 (93)


[출처 : http://ufx.kr/blog/163]
- 추상 알고리즘(Strategy)을 통해 알고리즘을 구현한 부분(ConcreteStrategy)들을 교환 가능
- 알고리즘을 교체해서 동일한 문제를 다른 여러가지 방법으로 해결하는 패턴
- 실행중에 알고리즘의 변경이 가능


1. Strategy (interface)
   - 여러 알고리즘들의 추상 알고리즘의 역할
    - Strategy strategy = new ConcreteStrategy1(); // Strategy strategy = new ConcreteStrategy();

2. Context 
    - Strategy를 구성(Composition) 으로 가지고 있음.
   - Strategy를 구현한 ConcreteStrategy1 or ConcreteStrategy2 의 인스턴스를 통해 알고리즘 변경 가능

3. ConcreteStrategy
   - Strategy 를 구현한 실제 알고리즘들

관련패턴)
   - Flyweight 패턴, Abstract Factory 패턴, State 패턴
Sample)

1. Sort 인터페이스를 구현한 BubbleSort 와 QuickSort 클래스의 sorting() 에 실제 정렬 알고리즘 구현체가 존재
2. ContextSort는 Sort를 구성으로 가지고 contextSorting()에서 알고리즘을 교체한다.


이번에는 가장 많이 사용하는 패턴 중 하나인 FactoryMethod 패턴에 대해 살펴본다.
정의를 내려보자면 생성될 객체의 정확한 클래스를 명시하지 않고 특정 메소드에서 결정한다.
역시 와닿지 않는다....

Observer 때와 마찬가지로 스타크래프트를 예로 들어본다.
테란의 메카닉 유닛을 생산하는 건물이 마침 Factory 니까... 그걸로 설명 한다.

사용자 삽입 이미지

FactoryMethod 는 말 그대로 생산, 생성의 의미를 가지고 있다.
생산될 대상은 Product 이고 이는 스타크래프트의 팩토리가 생산하는 유닛들이다.
이 유닛들은 SiegeTank, Vulture 같은 유닛들로 구체화된다.
그리고 이들 유닛을 생성하는 처리를 담당하는 Creator 를 사용함으로써 FactoryMethod 패턴이 구성된다.
위의 그림과 같이 SiegeTank 란 유닛을 생산하기 위한 SiegeTankCreator 로써 존재하는데.
Creator 에서 유닛을 생성하여 반환하는 메소드를 factoryMethod 라 했다. 
그렇다면 Creator 가 곧 스타크래프트의 팩토리 일까?
아니다 Creator 는 각각의 유닛별로 고유한 생산 로직을 담은 모듈에 해당된다.
스타크래프트의 팩토리는 그 Creator 를 모아놓은 것이다. (이는 좀 더 뒤에 다른 패턴에서 다루게 된다.)
Creator 는 게이머가 팩토리를 선택하고 시즈탱크를 생산하겠다라고 명령을 내릴때 동작한다.
유닛 생산시 필요한 시간을 소비시키고 적합한 애니메이션이나 소리를 들려주는 등의 처리가 Creator 에서 일어나는 일이 될 것이다.

예제 소스를 통해 살펴보자..
나름 재미가 있길래 조금 오바를 했다. FactoryMethod 패턴을 설명하기 위한 핵심은 진하게 표시된 부분이다.
public abstract class Unit {
    protected final String name;
    protected Unit(String name) {
        this.name = name;
    }
    public String getName() { return this.name; }
    public void command1() { System.out.println(name + ":이동"); }
    public abstract void command2();
    public abstract void command3();
}
유닛은 공통적인 처리나 로직등을 담을 필요가 있다 판단해서 인터페이스를 사용하지 않고 추상 객체로 작성했다.
유닛의 이름이나 command1 에서 일어나는 이동 같은 것들이 이런 이에 해당한다.
이 유닛을 상속하여 SiegeTank와 Vulture 를 만들수 있다.
public class Vulture extends Unit {
    private int countSpiderMine;
    Vulture() {
        super("벌처");
        this.countSpiderMine = 3;
    }
    public void command2() { System.out.println(name + ":수류탄"); }
    public void command3() {
        if ( this.countSpiderMine > 0 ) {
            this.countSpiderMine--;
            System.out.println(name + ":마인심기");
        } else {
            System.out.println(name + ":마인없음");
        }
    }
}
public class SiegeTank extends Unit {
    private boolean siegeMode;
    SiegeTank() {
        super("탱크");
        this.siegeMode = false;
    }
    public void command1() {
        if ( !this.siegeMode ) {
            super.command1();
        } else {
            System.out.println(name + ":이동못함");  // 시즈모드일때는 이동하지 못한다.
        }
    }
    public void command2() {
        if ( this.siegeMode ) {
            System.out.println(name + ":시즈포");
        } else {
            System.out.println(name + ":통통포");
        }
    }
    public void command3() {
        if ( this.siegeMode ) {
            this.siegeMode = false;
            System.out.println(name + ":탱크모드");
        } else {
            this.siegeMode = true;
            System.out.println(name + ":시즈모드");
        }
    }
}
나름  SiegeTank와 Vulture 의 특성을 구현해 보았다. 
이렇게 해서 만들어야 할 대상 Product 가 준비되었다. 이제 Creator 를 만들어본다.
public abstract class Creator {
    protected Creator() {}
    public abstract Unit createUnit();
}
위의 경우는 인터페이스로 작성해도 되지만 추상 객체로 만든 이유는 어떠한 공통 로직이 나중에라도 필요하지 않을까 해서이다. 이제 SiegeTank와 Vulture 를 만드는 각각의 Creator 를 작성한다.
public class SiegeTankCreator extends Creator {
    public Unit createUnit() {
        Unit unit = new SiegeTank();
        System.out.println(unit.getName() + " 생산");
        return unit;
    }
}
public class VultureCreator extends Creator {
    public Unit createUnit() {
        Unit unit = new Vulture();
        System.out.println(unit.getName() + " 생산");
        return unit;
    }
}
각각의 Creator 가 Unit 이라는 추상 객체로 반환하지만, 내부에서 각각 new SiegeTank(), new Vulture() 했다.
다시 한번 SiegeTank, Vulture 의 소스를 보면 Constructor 이 package private 이기 때문에 package 밖에서는 생성할 수 없다. 오로지 createUnit 으로만 생산할 수 있다. FactoryMethod 패턴을 사용하는 의미를 바로 안다면 SiegeTank, Vulture 는 결코 밖에서 보이면 안되고, 오로지 Unit 으로만 핸들링 되어야 한다. 그렇지 않다면 구지 FactoryMethod 패턴을 사용할 이유는 없다.
이제 이들 Creator 들을 모아놓은 팩토리를 만들고 테스트 해본다.
public class Factory {
    private Creator creator1;
    private Creator creator2;
    public Factory() {
        this.creator1 = new SiegeTankCreator();
        this.creator2 = new VultureCreator();
    }
    public Unit createUnit1() { return this.creator1.createUnit(); }
    public Unit createUnit2() { return this.creator2.createUnit(); }
}
public static void main(String[] args) {
    Factory factory = new Factory();
    Unit unit1 = factory.createUnit1();
    unit1.command1();
    unit1.command2();
    unit1.command3();
    unit1.command2();
    unit1.command1();
    unit1.command3();
    unit1.command1();

    Unit unit2 = factory.createUnit2();
    unit2.command1();
    unit2.command2();
    unit2.command3();
    unit2.command3();
    unit2.command3();
    unit2.command3();
    unit2.command1();
}

사용자 삽입 이미지

실행된 모습


그런데 왜 객체를 직접 생성하지 않고, FactoryMethod 패턴을 사용하여 생성할까..?
이유는 간단하다. ExtentionPack Blood War 를 만들기 위해서다.
스타크래프트 오리지날에 골리앗이란 유닛이 없었다고 가정하고...
확장팩에 골리앗이라는 새로운 유닛을 만들어 추가로 넣고자 한다...
FactoryMethod 패턴이 적용되어 있지 않다면 꽤나 많은 수정이 일어날 것이다.
FactoryMethod 패턴을 바탕으로 설계되어 있다면
먼저 Unit 을 상속해 특화된 기술 요소를 넣은 골리앗을 만든다.
그리고 이를 생성하는 Creator 를 만들고, 그곳에 빌드 타임이나 제한 요소등을 넣을 수 있다.
지금까지 새로운 객체를 추가만 했지 기존 소스의 수정은 없었다는 점이 중요 포인트다...
앞서의 예에서 팩토리에 골리앗을 만드는 Creator 를 넣는 부분은 팩토리를 수정해야 하지만...
차후 이 부분은 다른 패턴을 가지고 설명할 기회가 있을 것이다.

참고 삼아서 스타크래프트의 사용자 인터페이스를 잠시 말한다.
게이머가 유닛을 선택하고 그 유닛에 내리는 명령의 가짓수는 몇가지 일까?
한 유닛에 한번에 선택할 수 있는 명령은 우측 하단의 최대 9개 버튼뿐이다.
마우스 왼쪽버튼, 오른쪽버튼으로 맵을 찍거나, 키보드로 단축키를 넣는 것은 
Observer 패턴에 의해 9개 버튼중의 하나인 명령을 실행하는 것뿐이다.
건물짖기를 누르면 지을 수 있는 건물로 버튼이 바뀌는 것은 Shift 일 뿐이다.
예제 소스에서 유닛의 명령메쏘드 이름을 move, attack 으로 하지 않고, command1, command2 로 한건 이 때문이다.
이렇게 해서 각각의 개성넘치는 유닛들의 기능이 한 곳에서 단순한 구성으로 제어가 가능해진다.
여기서 이 얘기를 하는 것은 FactoryMethod 패턴에서 모든 유닛이 Unit 이란 슈퍼 클래스하나로 사용될 수 있어야 함을 이야기 하고자 한 것이다. 이것은 FactoryMethod 패턴의 활용 가치를 높여주는 핵심적 사항이다.
결국 제일 어려운 것은 FactoryMethod 가 아니라 Product 를 결정하는 것이다.

스타크레프트 예제도 어려우면 다음을 보자..
public class creator1 {
    public Number createNumber() {  return new Integer(1234); }
}
public class creator2 {
    public Number createNumber() {  return new Long(1234); }
}

이 예제도 객체(Integer, Long)의 생성이 메쏘드 내에서 이루어 지고, 
그렇게 생성된 객체의 슈퍼클래스 형태(Number)로 리턴되는 FactoryMethod 패턴이다.
즉, createNumber() 란 메쏘드가 객체를 생성해서 반환하는 공장 메소드란 것이다.
그리고...
지금 필요한 건 Integer 와 Long 이었지만, 혹시 나중에 new Double 을 붙일 필요가 있다면...
그것만 쉽게 만들어 붙일 수 있도록 공통적인 부분을 인터페이스나 추상 객체로 만들어 놓은 것일 뿐이다.

아마 객체를 상속해서 쓰기 좋아했던 개발자라면...
실제 이게 팩토리메소드 패턴이야라고 알지 못하고도 그간 사용하고 있었을 수도 있다.

 


FACTORY METHOD

Factory Method 패턴이란 객체를 생성하되 직접 객체 생성자(Constructor)를 호출해서 객체를 생성하는 것이 아니라 대행 함수를 통해 간접적으로 객체를 생성하는 방식을 Factory Method 패턴이라고 한다. 또한 이때 객체 생성을 대행해주는 함수를 Factory Method라고 한다.

 

Factory Method 패턴의 유용한 경우

  • 구체적으로 어떤 클래스의 객체를 생성해야 할지 미리 알지 못할 경우 Factory Method 패턴이 유용하다. 왜냐하면 Factory Method 패턴을 적용하면 구체적으로 생성될 객체의 자료형이 각각의 하위 클래스에 의해 결정되며, 이러한 하위 클래스는 필요할 경우 계속 확장 가능하기 때문이다.
  • 하위 클래스가 객체를 생성하기를 원할 때 Factory Method 패턴이 유용하다. Factoty Method 패턴은 객체의 생성을 하위 클래스에게 위임하는 것이라고 볼 수 있다.
  • Factory Method 패턴은 하위 클래스들에게 개별 객체의 생성 책임을 분산시켜 객체의 종류별로 객체 생성과 관련된 부분을 국지화시킬 때 유용하다.

 

Factory Method 패턴의 장·단점

  • Factory Method 패턴의 장점은 어떤 객체를 생성할 것인지와는 무관하게 동일한 형태로 프로그래밍이 가능하다는 점이다. 이는 두 개의 상위 클래스가 존재하기 때문에 어떤 객체를 생성하든지 상관없이 클래스 두 개가 제공하는 인터페이스를 이용해서 생성할 객체의 자료형과는 무관하게 독립된 형태로 프로그래밍을 할 수 있다는 점이다.
  • Factory Method 패턴의 또 다른 장점은 직접 생성자를 호출해서 객체를 생성하는 것보다 훨씬 유연하고 확장성 있는 구조라는 점이다. 왜냐하면 새로운 객체를 생성하거나 이전 객체를 확장해서 생성하고자 할 때 굳이 기존 소스코드를 분석해서 변경할 필요없이 새로운 하위 클래스를 정의하고 Factory Method에 해당하는 멤버 함수만 재정의 시키면 충분하기 때문이다.
  • Factory Method 패턴에서 Factory Method 멤버 함수는 Creator 클래스에 의해서만 불리워질 수 있는 게 아니라 외부에서도 얼마든지 불러서 사용할 수 있다. Factory Method 패턴은 Abstract Factory 패턴에서처럼 서로 연관된 클래스들의 상속 관계가 병렬로 존재해서 한 쪽 상속 관계의 클래스들이 다른 쪽 상속 관계의 클래스 객체들을 생성할 때 편리하다.
  • 상속 관계에 있는 클래스들의 멤버 함수가 동일한 프로그램 로직을 가지고 있으면서 내부적으로 생성할 객체만 서로 다를 때 Factory Method 패턴을 적용하면 편리하다. 왜냐하면 클래스마다 동일한 프로그램 로직의 멤버 함수를 각각 구현하지 않고, 상위 클래스에서 한 번만 구현한 다음 각각의 클래스는 객체를 생성해주는 멤버 함수만 다시 구현해주면 되기 때문이다.
  • Factory Method 패턴의 잠재적인 단점은 생성할 객체의 종류가 달라질 때 마다 새로운 하위 클래스를 정의해야 한다는 점이다. 이는 불필요하게 많은 클래스를 정의하게 만들 수 있는 문제를 내포하고 있다.


사실 객체지향 설계를 처음 접하기 이전부터 MVC 모델에 대한 개념적인 지식은 한번쯤 접해 보았을 것입니다가장 기본적인 정의를 설명하자면 M( Mode ) ApplicationBusiness Logic , 객체 또는 데이터의 가공을 책임지는 컴포넌트들의 집합체이고 C ( Control )이란 객체 또는 데이터의 흐름을 책임지는 컴포넌트 , V( View )는 객체 또는 데이터의 생성을 책임지는 컴포넌트로써 일반적으로 Data의 입력 부분과 출력 부분을 담당합니다

하지만 MVC 모델을 설명한 대부분의 설명서를 보면 위와 같이 너무 기본적인 정의에만 초점을 두고 있어 초급 개발자들은 자신이 개발해야 할 프로그램에서 어는 부분을 M으로 V C로 나누어야 할지 개념을 잡기 힘들뿐만 아니라 설사 각각의 업무에 맞게 class를 만들었다 하더라도 각각의 컴포넌트또는 Class ) 사이의 관계를 어떻게 설계해야 할지 판단하기 힘든 상황이 발생하게 됩니다이런 어려움을 가지고 있는 초급 개발자에게 조금이나마 도움이 될까 하여 MVC모델에 대한 개괄적인 내용을 아래와 같이 설명하고자 합니다

1.       M, V, C 전체 개념보기

2.       M, V, C 나누기

3.       M, V, C에 관계 적용하기


1.       M, V, C 전체 개념보기

Model은 객체나 Data의 가공을 책임지는 컴포넌트

View는 객체나 Data의 생성입력( User Action ), 출력( Display )을 책임 지는 컴포넌트

      Control은 객체나 Data의 흐름을 책임지는 컴포넌트

View
먼저 데이터객체 )의 입력과 출력을 담당하는 View는 필요에 따라 Model로부터 객체의 상태를 요청할 수 있고 응답 받은 상태에 따라 다른 출력 형식을 가지 수 있으며 Controller에게 전달 시 상태정보를 같이 보낼 수 있습니다여기서 중요한 것은 Model로부터 응답 받은 객체의 상태에 의해 View가 직접 제어가공을 하는일이 없도록 해야 합니다다시 말하면 View는 상태에 따라 출력형식만 다르게 가야하고 모든 제어나 가공은 Controller에게 위임을 해야 합니다.
Controller

객체 또는 데이터의 흐름을 책임지는 Controller는 활성화 된 View로부터 넘겨 받은 메시지(User Action) 또는 객체를 파악하여 해당 객체를 어떤 Model로 전달할지를 결정하고 필요한 객체를 데이터를 가공할 Model로 전달합니다기본 MVC 모델에서는 Model에서 Controller쪽으로 Event를 보낼 수 없도록 되어 있지만 구현참조가 아니라 인터페이스 참조일경우 Model에서 Controller쪽으로 Event를 보낸는것이 오히려 MVC 모델 흐름을 더욱 유연하게 만들기도 합니다.

Model

Model Controller로부터 전달받은 객체 또는 데이터를 Application의 Business Logic에 따라 가공처리하는 책임을 가지고 있으며 필요에 따라 View Controller에게 변경된 객체의 상태를 전달하게 됩니다.


1.       M, V, C 나누기

간단한 예를 통한 MVC 도출

 ) Database을 이용해서 고객정보를 읽고 쓰는Application


       
Cust_Dlg ( View )      고객 정보를 입력 또는 출력하는 GUI 관련 class

       Cust_Ctrl (Controller )  :  GUI로부터 입력 받은 고객 정보 데이터 또는 객체를 Model로 전달받는 Class

       Cust_DB ( Model ) : Controller로부터 전달 받은 객체 또는 데이터를 DB에 입력 또는 조회하는Class


 

2.       M, V, C에 관계 적용하기

 

3.       결론

M. V. C 모델의 궁극적인 목적은 각각의 컴포넌트에 대해 역할을 서로 독립적으로 부여하고 또한 각각의 컴포넌트의 관계는 구연 참조가 아니라 인터페이스 참조로 설계하여 차후 새로운 요구사항에 대해 최소한의 비용으로 보다 유연하게 대처하고자 하는 목적이며 이는 객체 지향 설계방법의 기본적인 목적이기도 합니다.





MVP(Model-View-Presenter) 패턴을 적용한 GUI Architecture 설계

1. MVP 패턴 소개

사용자 삽입 이미지
 

그림 1 MVP Pattern Diagram

MVP 패턴은 MVC(Model-View-Controller) 패턴에 기반을 둔 UI 프리젠테이션 패턴입니다.
MVP 패턴은 Model, View, View Interface, Presenter 네 개의 컴포넌트(구성요소)로 구성됩니다.
각 컴포넌트에서 담당하는 책임은 다음과 같습니다.

  • View
    View Interface의 Display 멤버(Properties, Display Methods)를 구현하여 실제적인 UI 요소를 그려줍니다.
  • View Interface
    Presenter에서 Concrete View를 직접 참조하지 않고 View Interface를 참조함로써 Concrete View와의 커플링을 감소시키고 View의 실제 UI 요소가 어떻게 구현되는지 몰라도 데이터를 올바르게 표현할 수 있도록 합니다.
  • Presenter
    View와 Model간의 상호작용을 담당합니다. Model의 데이터를 View Interface를 통해 Concrete View에 출력(바인딩)해 주고 사용자의 이벤트를 View에서 구독하여(실제적인 이벤트 핸들러 구현) Model의 데이터를 갱신하는 역할을 수행합니다.
    Presenter를 통해 View와 Model간의 의존관계를 없앨 수 있습니다.
  • Model
    데이터와 상태를 유지하며 데이터 처리 로직을 포함합니다. 일반적으로 비즈니스 엔티티와 비즈니스 로직을 Model 컴포넌트로 간주합니다.

2. MVP 패턴 적용

위에서 소개한 MVP 패턴을 적용하여 샘플 어플리케이션을 만들어 보도록 하겠습니다.
샘플 어플리케이션의 Class Diagram은 다음과 같습니다.

사용자 삽입 이미지

그림 2 MVP Pattern이 적용된 샘플 어플리케이션의 Class Diagram

Model 컴포넌트는 Member Class이며 Member Entity와 데이터 처리 로직을 포함하고 있습니다. 
Presenter 컴포넌트는 MemberPresenter Class이며 View와 Model을 참조합니다. 그리고 View의 이벤트 핸들러를 구현합니다.
View Interface 컴포넌트는 IMemberView Interface이며 View의 속성과 이벤트를 정의합니다.
Concrete View 컴포넌트는 MemberView Form이며 IMemberView를 구현하며 UI요소를 렌더링합니다.

먼저 Model 컴포넌트인 Member Class를 만들도록 합니다.
Member Class는 실제 데이터 소스에 접근하여 데이터를 검색하거나 갱신하는 로직을 포함합니다. 또한 Member의 Age, Name 속성을 갖는 Entity Class의 역할도 수행합니다.
데이터 소스는 실제 데이터베이스가 아닌 <표 1>과 같이 더미 데이터를 임시로 구현하여 사용합니다.

static class DummyMember
{
    #region 회원 더미 데이터 생성

    public static List<Models.Member> List = new List<Models.Member>();

    static DummyMember()
    {
        List.Add(new Models.Member("홍길동", 20));
        List.Add(new Models.Member("김갑수", 10));
    }

    #endregion

}

표 1 DummyMember Class

public int SaveMember()
{
    #region 저장

    if (id == null)
    {
        // 추가
        Data.DummyMember.List.Add(new Member(this.name, this.age));
        id = Data.DummyMember.List.Count - 1;
    }
    else
    {
        // 수정
        Data.DummyMember.List[id.Value].Name = name;
        Data.DummyMember.List[id.Value].Age = age;
    }


    #endregion

    return 0;
}

public bool LoadMember(int id)
{
    if (id >= Data.DummyMember.List.Count || id < 0)
    {
        this.id = null;
        return false;
    }

    this.id = id;

    name = Data.DummyMember.List[id].Name;
    age = Data.DummyMember.List[id].Age;

    return true;
}

표 2 Member Class의 데이터 처리 메서드


다음으로 IMemberView Interface를 정의합니다.
IMemberView Interface는 Concrete View에서 구현해야할 UI 요소와 표현 메서드를 정의합니다. 일반적으로 UI요소는 Property로 정의하며 Concrete View의 UI 요소에서 이벤트가 발생했을 때 이를 Presenter에서 처리할 수 있도록 이벤트를 정의합니다.

interface IMemberView
{
    int ID { get; set; }
    string Name { get; set; }
    int Age { get; set; }

    event EventHandler LoadMember;
    event EventHandler SaveMember;
}

표 3 IMemberView Interface

세 번째로 UI 요소를 출력하는 MemberView Form을 생성합니다.
MemberView Form은 System.Windows.Forms.Form Class를 상속하는 WinForm Class입니다. 닷넷의 WinForm Control이 UI 요소가 됩니다.
또한 IMemberView Interface를 구현하여 Presenter에서 UI 요소에 데이터를 채우거나 이벤트 핸들러를 구현하여 View를 제어할 수 있도록 합니다.

public partial class MemberView : Form, IMemberView
{
    MemberPresenter presenter;

    public MemberView()
    {
        InitializeComponent();

        presenter = new MemberPresenter(this);    
    }

    #region IMemberView 멤버

    int IMemberView.ID
    {
        get { return (int)numericUpDown1.Value; }
        set { numericUpDown1.Value = value; }
    }

    string IMemberView.Name
    {
        get { return textBox1.Text; }
        set { textBox1.Text = value; }
    }

    int IMemberView.Age
    {
        get { return (int)numericUpDown2.Value; }
        set { numericUpDown2.Value = value; }
    }

    event EventHandler IMemberView.SaveMember
    {
        add { button1.Click += value; }
        remove { button1.Click -= value; }
    }

    event EventHandler IMemberView.LoadMember
    {
        add { button2.Click += value; }
        remove { button2.Click -= value; }
    }

    #endregion
}

표 4 MemberView Form

주목할 부분은 SaveMember, LoadMember 이벤트를 구현한 부분입니다. button1.Click과 button2.Click의 실제 이벤트 핸들러는 Presenter에서 구현하는데, 이 곳에서 Model의 데이터를 검색하고 갱신하는 코드를 작성하게 됩니다.

마지막으로 View와 Model의 상호작용을 처리하는 MemberPresenter Class를 구현합니다. 
MemberPresenter Class는 Concrete View의 UI 요소(WinForm Control)의 실제 이벤트 핸들러를 구현하며 Member Class의 데이터 처리 메소드를 호출해 데이터를 검색하거나 갱신하는 코드를 구현합니다. 또한 IMemberView 인터페이스를 통해 Concrete View인 MemberView Form의 UI 요소(WinForm Controls)에 데이터를 출력하는 코드도 구현하고 있습니다.

class MemberPresenter
{
    IMemberView view;
    Models.Member model;

    public MemberPresenter(IMemberView view)
    {
        this.view = view;
        this.view.SaveMember += new EventHandler(view_SaveMember);
        this.view.LoadMember += new EventHandler(view_LoadMember);

        this.model = new SingleLayerMVP.Models.Member();
    }


// 회원정보 불러오기
    void view_LoadMember(object sender, EventArgs e)
    {
        int id = view.ID;

        if (model.LoadMember(id))
        {
            view.Name = model.Name;
            view.Age = model.Age;
        }
        else
        {
            MessageBox.Show("존재하지 않는 회원입니다.");

            view.Name = string.Empty;
            view.Age = 0;
        }
    }


    // 회원정보 저장하기
    void view_SaveMember(object sender, EventArgs e)
    {
        model.Name = view.Name;
        model.Age = view.Age;

        if (model.SaveMember() == 0)
        {
            MessageBox.Show("성공");
        }
        else
        {
            MessageBox.Show("실패");
        }
    }
}

MemberPresenter Class는 IMemberView와 Member의 참조를 모두 갖게 되며 view와 model간의 상호작용을 담당하는 코드를 실제 구현하게 됩니다. 이로써 View와 Model간의 커플링을 없앰과 동시에 View와의 상호작용을 IMemberView Interface를 통해 처리함으로써 실제 Concrete View인 MemberView Form과의 커플링도 없앨 수 있게 되는 것입니다.

3. Multi Layer Application Architecture 에서 MVP Pattern 적용하기

사용자 삽입 이미지

그림 3 MVP Pattern Diagram with C/S Application

일반적으로 비즈니스 레이어와 Presentation Layer가 분리 되어 있는 어플리케이션에서 MVP Pattern의 적용은 <그림 3>과 같은 구조를 적용하면 됩니다. Model의 Business Entity는 직렬화가 가능하도록 [Serializable] 어트리뷰트를 추가하여 개발하고 Client Application에서는 Web Service참조를 Model로 간주하여 Presenter에 구현하면 됩니다.

[Serializable]
public class Member
{
    int? id = null;

    public int? Id
    {
        get { return id; }
        set { id = value; }
    }

표 5 직렬화 가능한 Member Entity

class MemberPresenter
{
    IMemberView view;
    WebService.Service1 model;

    public MemberPresenter(IMemberView view)
    {
        this.view = view;
        this.view.SaveMember += new EventHandler(view_SaveMember);
        this.view.LoadMember += new EventHandler(view_LoadMember);

        this.model = new Client.WebService.Service1();
    }


    // 회원정보 불러오기
    void view_LoadMember(object sender, EventArgs e)
    {
        int id = view.ID;

        WebService.Member member = model.wpLoadMember(id);

표 6 MemberPresenter Class에서 Web Service 참조를 Model로 참조하기

4. View를 위한 단위 테스트 코드 작성

우선 단위 테스트 코드는 NUnit Framework를 사용하도록 합니다. 
NUnit Framework의 다운로드와 설치는 다음 링크를 참조하세요.
http://www.nunit.org

일반적으로 GUI에 대한 테스트는 사용자 액션(이벤트 발생)을 시뮬레이션하기 어렵기 때문에 자동화하기가 어렵습니다. 그래서 마우스와 키보드 동작을 레코드하여 매크로 동작으로 테스트하는 상용화된 테스팅 툴을 사용하게 됩니다. 
하지만 MVP 패턴을 적용하게 되면 IMemberView 인터페이스를 사용해서 View만을 위한 단위 테스트 코드를 작성할 수 있습니다. 이것이 의미하는 것은 GUI 개발을 테스트 지향적(TDD, 테스트 코드 먼저구현->실제 코드 구현)으로 개발할 수 있다는 것이기도 합니다.

단위 테스트 코드를 작성하기 위해 먼저 IMemberView Interface를 구현하는 DummyView Class를 생성합니다.

public class DummyView : IMemberView
{
    int id;
    int age;
    string name;

    event EventHandler loadMember;
    event EventHandler saveMember;

    #region IMemberView 멤버

    …

    #endregion

    #region 테스트용 인터페이스

    public void TestLoadMember()
    {
        loadMember(this, null);
    }

    public void TestSaveMember()
    {
        saveMember(this, null);
    }

    #endregion
}

표 7  DummyView Class

DummyView Class에서 IMemberView Interface를 구현한 것과 테스트용 메서드인 TestLoadMember()와 TestSaveMember() 메서드를 추가한 것입니다. 실제 View의 UI 요소를 제어하는 코드가 있는 Class는 MemberPresenter이기 때문에 IMemberView와 MemberPresenter를 모두 테스트하는 코드라고 볼 수 있습니다.

using NUnit.Framework;

namespace SingleLayerMVP.Tests
{
    [TestFixture]
    public class MemberViewTest
    {
        IMemberView view;
        MemberPresenter presenter;

        [SetUp]
        public void SetUp()
        {
            view = new DummyView();
            presenter = new MemberPresenter(view);
        }

        [TearDown]
        public void TearDown()
        {
            // null
        }

        /// <summary>
        /// Test LoadMember()
        /// </summary>
        [Test]
        public void TestLoadMember()
        {
            view.ID = 0;
            (view as DummyView).TestLoadMember();

            Assert.AreEqual(view.Name, "홍길동");
            Assert.AreEqual(view.Age, 20);
        }

        /// <summary>
        /// Test SaveMember()
        /// </summary>
        [Test]
        public void TestSaveMember()
        {
            view.ID = 0;
            view.Name = "홍길삼";
            view.Age = 21;
            (view as DummyView).TestSaveMember();

            Assert.AreEqual(view.Name, "홍길삼");
            Assert.AreEqual(view.Age, 21);           

        }


    }
}

표 8 IMemberView와 MemberPresenter 단위 테스트 코드

NUnit Framework를 사용해서 테스트 코드를 작성하는 방법은 http://www.nunit.org 사이트를 참조하세요.

TestLoadMember() 테스트 메서드와 TestSaveMember() 테스트 메서드를 보면 실제 GUI의 동작을 시뮬레이션하는 코드가 구현되어 있는 것을 볼 수 있습니다.

TestLoadMember() 메서드를 자세히 살펴보죠.
view.ID = 0; 이라는 코드에서 사용자가 ID UI 요소에 0이란 값을 입력한 것을 의미하고,
(view as DummyView).TestLoadMember(); 코드에서는 “불러오기” 버튼을 클릭(또는 유사한 동작)한 것을 의미합니다.

Assert.AreEqual() 구문에 의해서 DummyMember.List[]의 첫 번째 데이터인 {‘홍길동’, 20}이 정상적으로 View에 출력되었는지 확인하게 됩니다.

NUnit에서 컴파일된 어셈블리를 로드하여 테스트를 수행하면 다음과 같이 테스트가 통과한 것을 볼 수 있습니다.
 

사용자 삽입 이미지

그림 4 NUnit 테스트 결과

물론 MemberView 폼을 대상으로 테스트를 수행하지 않은 것은 아쉬운 점입니다. 특히 View의 입력 요소에 값이 입력될 때 유효성 체크하는 코드를 작성할 수 없다는 것도 역시 아쉬운 점입니다. 이 두 항목을 포함하여 본 문서에서 미흡하게 다뤘거나 또는 건너 뛴 부분(MVC 패턴, MVC와 MVP 패턴의 차이점과 장단점…)들은 참조문헌에 있는 링크를 참조하세요.

이상으로 MVP 패턴을 적용한 Gui Archicecture 설계를 마치도록 하겠습니다.

5. 참조문헌

5.1. MSDN Magazine, Model View Presenter : 
http://msdn.microsoft.com/msdnmag/issues/06/08/DesignPatterns/
5.2. MVC or MVP Pattern – What’s the difference :
http://blogs.infragistics.com/blogs/tsnyder/archive/2007/10/17/mvc-or-mvp-pattern-whats-the-difference.aspx
5.3. Martin Fowler’s GUI Architectures :
http://www.martinfowler.com/eaaDev/uiArchs.html
5.4. MSDN, Model View Controller
http://msdn2.microsoft.com/en-us/library/ms978748.aspx
5.5. MSDN, Implementing Model-View-Controller in ASP.NET
http://msdn2.microsoft.com/en-us/library/ms998540.aspx
5.6. MSDN, Page Controller
http://msdn2.microsoft.com/en-us/library/ms978764.aspx
5.7. MSDN, Front Controller
http://msdn2.microsoft.com/en-us/library/ms978723.aspx
5.8. KLDP, M-V-C가 서로의 영역을 전혀 침범하지 않고 개발하는 것이 과연 가능한가?
http://kldp.org/node/70219
5.9. Javaworld, JSP의 MVC 모델1과 모델2
http://www.javaworld.com/javaworld/jw-12-1999/jw-12-ssj-jspmvc.html


[출처 :http://blog.jeidee.net/321]

 다음 [그림 1]은 유전자 알고리즘의 일반적인 수행과정을 나타낸다.

    

[그림 1] 유전자 알고리즘 수행과정


유전자 표현(Gene Representation)은 유전자 알고리즘을 구축하는 첫단계로 문제의 잠재해를 유전적 표현 즉, 개체로 표현하여야 한다. 이 유전적 표현은 유전자 알고리즘의  다른 절차(적합도 평가와 유전연산자 등)에 영향을 주므로 문제의 특성을 잘 반영할 수 있도록 하고, 문제에 따라 표현은 다를 수 있다. 유전자 알고리즘은 개체들로 구성된 모집단을 바탕으로 해를 찾아 나가기 때문에 초기에 모집단이 생성되어야 한다. 생성 방법은 임의생성 방법이 흔히 사용된다.


적합도(Fitness)는 유전자 개체가 갖고 있는 피부색, 저항력 등과 같은 특성을 나타내는 값으로서 이것은 환경에서 생존할 수 있는 즉, 적응할 수 있는 정도를  나타내는 값이다. 개체가 주어진 어떤 환경에서 생존하기 위해서는 최적의 적합도를 가져야 하며, 이러한 최적값은 일반적인 최적화문제의 목적함수에서와 같이 목적함수인 적합도 함수를 설정하여 이것을 최적화함으로써 얻을 수  있다. 예를 들면, 임의의 염색체(스트링)의 유전자값이 주어질 때, 이 값들을 변수로 한 적합도 함수를 설정하여 최적화시킴으로써 최적의 적합도를 구한다. 그러나 주어진 특정한 값을 갖는 염색체가 원하는 수준의 적합도를 충족시키지 못하면 유전자 변형을 통하여 염색체를 변화시켜 원하는 최적 적합도가 생성될 때까지 반복한다.


선별(Selection)은 적자생존의 자연법칙에 기초하여 환경에 대한 적합도를 평가하여 현 세대의 모집단으로부터 다음 세대를 만들 개체를 선택하는 과정이다. 모집단의 다양성과 선별압력이 조화를 이룰 수 있어야 하고, 강한 선별압력(적합도가 높은 우수개체가 열성개체보다 생존확률이 아주 높은 것)은 모집단의  개체들을 조기에 수렴시키나 해의 다양성이 떨어지며, 약한 선별압력은 해의 다양성은 유지되나 효율적으로 해를 탐색하지 못한다.


유전 연산자(Operator)는 교차(crossover)와 돌연변이(mutation) 연산자로 나누어 진다. 교차는 두 부모(parent)가 갖는 유전자를 조합하여 자손(offspring)을 생성하는 과정이다. 교차시 부모의 좋은 형질(유전자)이 가능한 파괴되지 않고 자손에 상속될 수 있어야 한다. 돌연변이는 한 개체에서 아주 작은 수의 유전자를  임의로 변화시키는 과정이며 해를 다양하게 탐색할 수 있도록 한다.


세대(Generation)는 알고리즘에서 유전연산의 횟수이며, 유전 파라미터(Parameter)로는 모집단의   크기(pop-size), 교차율, 돌연변이율, 종료조건 등이 있다. 모집단의 크기는  모집단을 이루는 개체수를 의미하며, 교차율은 각 개체가 교차될 확률, 돌연변이율은 각 유전자가 돌연변이 될 확률을 나타낸다. 알고리즘 종료조건으로는 (1) 진행된 세대수 또는 생성된 개체수, (2) 해의 개선의 이루어지지 않고   진행된 세대수 또는 생성된 개체수, (3) 계산소요시간 등이 사용된다. 다음 
<표 1>과 [그림 2]는 알고리즘의 세부 절차 및 흐름도를 나타낸다.

<표 1> 유전자 알고리즘의 절차

단 계

내            용

1

종료조건을 설정한다(진행할 세대수, 계산소요시간 등)

2

최초의 유전자개체 모집단을 구성한다

3

최초 모집단의 유전자개체에 대하여 적응도를 평가한다

4

교차율에 의해 모집단에서 두 유전자개체를 선별한다.

5

선별된 두 개체에 대하여 교차를 실시한다.

6

돌연변이율에 의해 임의 개체를 변형시킨다.

7

교차와 돌연변이된 유전자개체의 적응도를 평가한다.

8

최초 정해진 종료조건을 만족하면 계산을 끝내며, 그렇지 않으면  단계 4로 다시 간다.


procedure GA

initialize Population;

   eval   uate Population;

while not (terminal condition satisfied) do

select chromosomes for next population;

Crossover and Mutation;

   eval   uate Population;

end while

end procedure

[그림 2] 유전자 알고리즘 흐름도



[출처 : http://kr.blog.yahoo.com/g0jeon01/47]
개요 ( What are Genetic Algorithms? ) 유전자 알고리즘(Genetic Algorithm, GA)은 적자 생존과 유전의 메카니즘을 바탕으로 하는 탐색 알고리즘이다. 다시 말해 주어진 환경에 잘 적응하는 유전자만을 선택(selection)하고 교배(crossover)하고 때에 따라서는 돌연변이(mutation)도 하며 다음 세대에 우수한 유전 형질이 전달(reproduction)되게 된다. 따라서 진화(evolution)가 거듭될수록 주어진 환경에 더 적합한 유전자들만이 남아있게 될 것이다.
유전자 알고리즘은 미시간대학의 홀랜드(John Holland)에 의해 탄생하였으며. 당시 연구원들의 연구 목표는
[1] 자연시스템의 적응적 과정을 추상화시키며 철저하게 분석하여,
[2] 그러한 시스템을 소프트웨어적으로 디자인하는 것이었다.
유전자 알고리즘(GA)은 그러한 배경에서 만들어졌으며, 근본적으로 다른 탐색이나 최적화 알고리즘과는 세 가지 정도가 다르다.
[1] GA는 하나의 개체(변수 등)가 아닌 개체들의 군(pool) 단위로 탐색한다.
[2] GA는 적합성 함수(fitness function)를 사용한다.
[3] GA는 확률적인 변이 규칙을 사용한다.
간단한 예를 들어보자. f(x) = x2[0 ≤ x ≤ 31] 이라는 환경이 있으며, 환경에 살아가는 개체(유전자)가 x인 셈이다. 그리고 환경에 잘 적응하는 정도를 함수값으로 정의하자. 그러므로 x = 0이라는 개체보다 x = 4라는 개체가 f(x) = x2[0 ≤ x ≤ 4]라는 환경에서 더 잘 적응하는 것이다.
환경 : f(x) = x2[0 ≤ x ≤ 31]
개체 : 0 ≤ x ≤ 31
적합성(fitness) : f(x)
01101
11000
01000
10011
위의 네 개체가 오손도손(^_^) 살고 있는 군(집단)에서 과연 f(x) = x2이라는 환경에서 누가 더 잘 적응할 것으로 생각되는가? 위의 이진수를 십진수로 바꾸어 생각하면 쉽게 알 수 있다. 위의 예에서 01000은 살기 어렵고, 11000은 그나마 가장 잘 적응할 것이다. 그러므로 다음 세대에서는 01000은 도태를 하고, 11000은 자손을 생산하게 된다. 이와 같은 과정을 거친 먼 훗날의 개체는 바로 11111이라는 개체가 탄생할 것이고, 이 넘(^_^)이 바로 주어진 환경을 지배할 것이다.
11111
11111
11111
11111
군(pool)이 위와 같은 개체(gene)로 구성되었을 때 '환경에 적응하였다'라고 말한다. 또는 '해를 찾았다'라고 말하기도 한다. 이와 같이 유전자 알고리즘은 자연의 적자 생존 원칙에 기반을 하고 있는 탐색 알고리즘이다. 다음 내용에서는 유전자 알고리즘의 단계별 과정들인 재생산(reproduction), 교배(crossover), 돌연변이(mutation)들을 알아보겠다
----------------
이번에는 유전자 알고리즘(GA; Genetic Algorithm)에 대해서 하겠습
니다.랜덤을 사용하는 알고리즘중에 제가 젤 좋아하는 알고리즘입니다
.(잘은 사용 하지만...^^)
그럼 강좌 시작하겠습니다.

찰스 다윈은 "종의 기원"에서 종이 어떻게 진화하는가를 적자 생존
의 원칙으로 명하였습니다. 그리고 멘델은 완두콩에 대한 실험으로 자
손의 형질은 원소로서 유전되며, 두 부모로 부터 각각 한개씩 받는다
고 설명하였습니다.

유전자 알고리즘은 적자 생존의 원칙과 교배, 돌연변이를 사용하여
탐색하는 기법으로 국소 탐색이나 시뮬레이티드 어닐링이 한개의 해
를 가지고 운영하는데 비해 유전자 알고리즘은 여러개의 해를 가지고
운영합니다.

유전자 알고리즘의 기본적인 구조는 다음과 같습니다.

Procedure GenetcAlgorithm;
begin
t := 0;
P(t)의 초기화(초기모집단 생성)
P(t)의 적응도 평가
while(종료조건이 만족되지 않으면) do
begin
t := t + 1;
P(t-1)로부터 P(t)를 선별
P(t)의 유전연산(교차와 돌연변이)
P(t)의 적응도 평가
end;
end;

P(t)는 t세대에서의 모집단을 나타냄니다. 간단한 문제를 가지고 예
를 들어 설명하겠습니다.

문제) f(x) = | 10*x-100 | 이고, 0 <= x <= 31 일때 함수 f(x)를
최대로 하는 x
를 찾아라. 단 x는 정수이다.

위 문제는 국소탐색을 할때 사용한 문제죠... 숫자만 약간 바꿨습니
다. 우선 해의 표현 방법을 정해야 겠죠. (유전 알고리즘에서 해는 보
통 유전자라고 부름니다. 이것 말고도 많은 용어들이 생물학에서 사용
되는 용어를 많이 사용합니다.) 0 ~ 31까지 나타내기 위해서는 5비트
가 필요 합니다. 유전자는 2진백터로 표현 합니다. 즉, (10101) = 21,
(11111) = 31, (01100) = 12 가 됩니다.

그러면 0세대 부터 시작 합니다. 처음에는 초기 집단을 생성 합니다.
초기 집단은 랜덤하게 만듭니다. 20개의 염색체로, 초기 집단이 다음
과 같이 만들어 졌다고 합시다.

v[1]: (01101)
v[2]: (01111)
v[3]: (10100)
v[4]: (01101)
v[5]: (10101)
v[6]: (01011)
v[7]: (10111)
v[8]: (10000)
v[9]: (01101)
v[10]: (01011)


평가 단계에서 각 염색체를 해독하고, 해독된 값으로 부터 적합도
함수값을 계산 한 결과 다음과 같은 결과를 얻었습니다.

eval(v[1]) = f(13) = 30
eval(v[2]) = f(15) = 50
eval(v[3]) = f(20) = 100
eval(v[4]) = f(13) = 30
eval(v[5]) = f(21) = 110
eval(v[6]) = f(11) = 10
eval(v[7]) = f(24) = 130
eval(v[8]) = f(16) = 60
eval(v[9]) = f(13) = 30
eval(v[10]) = f(11) = 10

이제 이중에서 몇개의 염색체를 선택해야 합니다. 선택하는 방법은
여러가지가 있는데 여기에서는 룰렛선택을 합니다. 룰렛선택은 적합도
에 비례해서 선택될 확률이 높아짐니다. 룰렛선택은 다음과 같이 합니
다.

------------------------------------------------------------------------------
1) 개체집단의 총 적합도를 계산한다.
F = eval(v[1]) + eval(v[2]) + .... + eval(v[10])
2) 각 염색체에 대한 선택 확률 p[i]를 계산한다.
p[i] = eval(v[i])/F
3) 각 염색체에 대한 누적 확률 q[i]를 계산한다.
q[i] = p[1] + p[2] + ... p[i]

선택과정은 유전자의 개수만큼 다음 방법으로 새로운 개체집단을 위
한 하나의 염색체를 선택합니다.

범위 0..1사이의 난수 r를 발생시킨다.
q[i-1]< r <=q[i] 인 i번째 염색체를 선택한다.
------------------------------------------------------------------------------

그럼 위와 같은 방법으로 룰렛 선택을 합니다. 개체집단의 총 적합도는

F = eval(v[1]) eval(v[2]) +...+ eval(v[10]) = 560

입니다. 각 염색체가 선택될 확률 p[i]는 다음과 같습니다.

p[1] = eval(v[1])/F = 0.0535714286
p[2] = eval(v[2])/F = 0.0892857143
p[3] = eval(v[3])/F = 0.1785714286
p[4] = eval(v[4])/F = 0.0535714286
p[5] = eval(v[5])/F = 0.1964285714
p[6] = eval(v[6])/F = 0.0178571429
p[7] = eval(v[7])/F = 0.2321428571
p[8] = eval(v[8])/F = 0.1071428571
p[9] = eval(v[9])/F = 0.0535714286
p[10] = eval(v[10])/F = 0.0178571429

그러면 각 염색체의 누적 확률 q[i]는 다음과 같습니다.

q[1] = 0.0535714286
q[2] = 0.1428571429
q[3] = 0.3214285714
q[4] = 0.3750000000
q[5] = 0.5714285714
q[6] = 0.5892857143
q[7] = 0.8214285714
q[8] = 0.9285714286
q[9] = 0.9821428571
q[10] = 1.0000000000

이제 0부터 1사이의 난수를 10(유전자의 개수)개 발생시켜 유전자를선택합니다.
만약 난수가 0.45772737743 이라면 q[4] < 0.45772737743 <= q[5] 를 만족하기 때
문에 유전자v[5] 를 선택합니다. 난수가 다음과 같이 발생 되었습니다.

0.4572273774 0.6205325316 0.2902582685 0.3707185990
0.6674934615 0.5626070339 0.7364188557 0.9917080770
0.6376504887 0.9917636998

그러면 선택된 개체들은 최종적으로 다음과 같습니다.

v'[1] = (10101)
v'[2] = (10111)
v'[3] = (10100)
v'[4] = (01101)
v'[5] = (10111)
v'[6] = (10101)
v'[7] = (10111)
v'[8] = (01011)
v'[9] = (10111)
v'[10] = (01011)

그러면 이제 유전 연산을 하면 됩니다. 유전 연산은 교배과 돌연변
이가 있는데 우선 교배부터 합니다. 교배하는 방법은 우선 교배 확률
에 의해 교배시킬 염색체를 선택합니다. 교배 확률은 0.25로 하겠습니
다. 다음과 같은 난수를 10개 발생시켜서 다음과 같은 수를 얻었습니
다.

0.5725024704 0.0490196129 0.8726333449 0.3486277370
0.3863749911 0.8044754410 0.8187371953 0.5160406448
0.0951179727 0.6477678808

결과 선택된 것은 v'[2]와 v'[9]가 선택되었습니다. 운좋게 짝수개
가 선택되어서 모두 교배시킬수 있습니다. 만약에 홀수개가 선택되었
다면 하나를 빼든가 추가해야 됩니다. 교배하는 방법은 다음과 같이
합니다.

------------------------------------------------------------------------------
염색체 x = (x1 x2 x3 x4 x5)와 염색체 y = (y1 y2 y3 y4 y5)가 있
다으면 우선 1 ~ 4(5-1) 사이의 수 중 난수 r를 발생 시킨다. 만약
r이 3이라면

x' = (x1 x2 x3 | y4 y5)
y' = (y1 y2 y3 | x4 x5)

로 r번째 다음에 있는 유전인자들을 바꿔주어서 새로운 유전자를 만
듭니다.
------------------------------------------------------------------------------

v'[2] = (10111) 와 v'[9] = (10111) 를 교배시키기 위해 난수 r를
발생 시킴니다. r은 1이 발생 되었습니다. 다음과 같이 됩니다.

v'[2] = (1 | 0111)
v'[9] = (1 | 0111)

v'[2]와 v'[9] 서로 같아서 아무런 변화가 없습니다. (예제가 나쁘
죠...프로그램을 실행시키면서 쓰는 것이라서 다른 걸로 바꿀수가...^^),
그리고 여기에서는 2개의 유전자가 선택 되었는데 만약 4개 또는 6개 이
상이 선택 되었다면은 임의로 2개씩 짝지어서 교배 시켜야 됩니다. 교배
이후에 개체집단은 다음과 같습니다.

v'[1] = (10101)
v'[2] = (10111)
v'[3] = (10100)
v'[4] = (01101)
v'[5] = (10111)
v'[6] = (10101)
v'[7] = (10111)
v'[8] = (01011)
v'[9] = (10111)
v'[10] = (01011)

이제 돌연변이를 시켜 줍니다. 돌연 변이 확률은 0.1로 하겠습니다.
돌연 변이는 각 염색체에 대해서 난수를 발생시켜 (돌연 변이 확률)<(
난수) 이면은 유전인자를 바꾸어 줍니다. 즉, 1이면 0으로 0이면 1로
바꿉니다. 돌연 변이를 시킨 다음에 개체집단은 다음고 같습니다. 바
뀐 염색체는 위에 '_' 표시를 했습니다.
_
v'[1] = (11001)
_
v'[2] = (00111)
_
v'[3] = (10101)
_
v'[4] = (00101)

v'[5] = (10111)
_
v'[6] = (10111)

v'[7] = (10111)

v'[8] = (01011)

v'[9] = (10111)
_
v'[10] = (00011)

그러면 v'에서 '표시를 지우고 다시 고쳐쓰면 다음고 같습니다.

v[1] = (11001)
v[2] = (00111)
v[3] = (10101)
v[4] = (00101)
v[5] = (10111)
v[6] = (10111)
v[7] = (10111)
v[8] = (01011)
v[9] = (10111)
v[10] = (00011)

이 개체의 적합도를 계산 하면 다음과 같습니다.

eval(v[1]) = f(25) = 150
eval(v[2]) = f(7) = 30
eval(v[3]) = f(21) = 110
eval(v[4]) = f(5) = 50
eval(v[5]) = f(23) = 130
eval(v[6]) = f(23) = 130
eval(v[7]) = f(23) = 130
eval(v[8]) = f(11) = 10
eval(v[9]) = f(23) = 130
eval(v[10]) = f(3) = 70

대체적으로 이전세대 보다 더 좋은 값을 갖는 개체집단이 생겼습니
다. 이런 방법을 반복하면은 꽤 좋은 해를 찾을수 있습니다. 10세대
까지 진행 시킨 결과는 다음과 같습니다.

v[1] = (11111)
v[2] = (11011)
v[3] = (11011)
v[4] = (11100)
v[5] = (10111)
v[6] = (11100)
v[7] = (11111)
v[8] = (11111)
v[9] = (11100)
v[10] = (11111)

이것을 평가하면 다음과 값을 얻을수 있습니다.

eval[2] = f(27) = 170
eval[3] = f(27) = 170
eval[4] = f(28) = 180
eval[5] = f(23) = 130
eval[6] = f(28) = 180
eval[7] = f(31) = 210
eval[8] = f(31) = 210
eval[9] = f(28) = 180
eval[10] = f(31) = 210

여기에서는 운좋게 마지막 세대에서 최적값 210을 얻을수 있었습니
다. 하지만 마지막 세대에 최적값을 못얻는 경우가 생길수 있으므로
여태까지 출현했는 염색체중에서 가장 좋은 평가값을 갖는 염색체를
따로 저장해 둡니다.
----
이번에는 죄수의 딜레마 라는 단순한 게임에 대한 전략을 학습하
기 위하여 어떻게사용될 수 있는지 설명하겠습니다.

------------------------------------------------------------------------------
죄수의 딜레마 - 두 명의 죄수가 독립된 방에 수감되어 있어서 서
로 통신할 수가 없는 상태에 놓여 있습니다. 죄수들은 각각 상대방
을 이탈하고 배신(죄를 시인하는 것이라 생각하면 됩니다)을 하도록
요구됩니다.

┌───────────┬──────────┬──────────┐
│ 나 \ 상대방 │협력 │ 배신 │
├───────────┼──────────┼──────────┤
│ 협력 │ 각각 2년형 │ 나만 5년형 │
├───────────┼──────────┼──────────┤
│ 배신 │ 상대방만 5년형 │ 각각 4년형 │
└───────────┴──────────┴──────────┘

위표와 같이 징역을 살게 됩니다. 그러면 이기적인 선택은 상대방
이 어떠한 선택을 하든 상관없이 협력하는 것보다는 더 나은 보상을
가져다 주게 됩니다(상대방이 협력 했을 경우에 내가 협력하면 2년형
을 살지만 배신하면 5년형이고, 상대방이 배신했을 때도 협력하면
5년형을 살지만 배신하면 4년형을 살게 되기 때문입니다) 하지만 둘
다 배신하면 둘다 협력했을 때보다 결과가 나쁘게 됩니다. 죄수의 딜
레마는 협력할 것인가 배신할 것인가를 선택하는 문제입니다.
------------------------------------------------------------------------------

만약 단, 한번의 기회만 주어진다면 어떤 선택을 해야 될까요?? 이
것을 여러번 시행하게 만들면 다음과 같이 점수를 주면 됩니다.


┌────────┬────────┬────────┬────────┐
│ Player1 │ Player2 │ Player1의 점수 │ Player2의 점수 │
├────────┼────────┼────────┼────────┤
│ 배신 │ 배신 │ 1 │ 1 │
├────────┼────────┼────────┼────────┤
│ 배신 │ 협조 │ 5 │ 0 │
├────────┼────────┼────────┼────────┤
│ 협조 │ 배신 │ 0 │ 5 │
├────────┼────────┼────────┼────────┤
│ 협조 │ 협조 │ 3 │ 3 │
└────────┴────────┴────────┴────────┘

위 표대로 점수를 주면서 게임을 합니다. 예를 들어 게임이 (협조,
협조),(협조,배신),(협조,배신)이 되었다면 Player1은 3+0+0=3점을 얻
게 되고, Player2는 3+5+5 = 13점을얻게 됩니다. 그러면 이것을 유전
자 알고리즘으로 점근해 보겠습니다.

1) 전략의 표현 - 전략은 최근에 했던 3게임을 가지고 현재의 행동을
선택합니다. 예를 들어 최근에 했던 게임이 (배신,배신),(배신,협조),
(협조,배신) 였고, 염색체 내의 이것에 해당되는 유전인자가 협조였다
면 그 염색체의 다음 행동은 협조가 됩니다. 이런 방법으로 표현하면
은 한 게임에 대해 4개의 가능한 결과가 존재하므로 4*4*4=64개의 서
로 다른 이전 행동들이 존재합니다.

64개의 서로 다른 행동에 대한 다음 행동이 협조냐 배신이냐에 따라
2가지 경우가 존재 하므로 비트 또는 D와 C로 표현할수 있습니다. 그리
고 게임시작 이전의 세 가설적인 행동에 관한 초기 가정을 규정할 필요
가 있으므로 추가로 6개의 유전인자가 필요하게 되빈다. 총 70개의 유
전인자가 하나의 염색체를 표현하게 됩니다.

2) 유전자 알고리즘의 윤곽
죄수의 딜레마에 대한 전략을 학습하기 위한 Axelrod의 유전자 알고
리즘은 다음과 같은 네 가지 과정으로 이루어집니다.

------------------------------------------------------------------------------
1. 초기 개체집단을 선택한다. 각 플레이어는 위에서 언급된 것처럼 하
나의 전략을 나타내는 70비트의 임의의 스트링에 할당된다.

2. 각 플레이어를 테스트하여 유효성을 결정한다. 각 플레이어는 자신의
염색체에 의해 정의된 전략을 사용하여 다른 플레이어와 게임한다. 그
플레이어의 점수는 그가 하는 모든 게임에 있어서의 평균이다.

3. 증식시킬 플레이어를 선택한다. 평균점수를 가진 플레이어에 한번의
증식 기회를 부여한다. 평균점수보다 하나의 표준편차 이상의 점수를 가
진 플레이어에는 두번의 증식기회를 부여한다. 평균점수보다 하나의 표
준편차 이하의 점수를 가진 플레이어는 증식기회를 갖지 못한다.

4. 성공적인 플레이어는 임의로 짝을 지어 교배당 두명의 자손을 만들어
낸다. 각 자손의 전략은 그의 부모의 전략으로부터 결정된다. 이것은 두
개의 유전 연산자인 교배와 돌연벼이에 의해 이루어진다.
------------------------------------------------------------------------------

3) 실험 결과
- 위와 같이 프로그램을 실행하고 나서 Axelrod가 얻은 결과는 놀랄
만한 결과를 얻었다고 합니다. 그 중 일부를 적으면 다음과 같습니다.

------------------------------------------------------------------------------
C는 협력, D는 배신;

1. 배를 흔들지 말라: 세번의 상호협력 이후에는 계속 협력한다
((CC)(CC)(CC) 이후에 C).

2. 화를 내라: 다른 플레이어가 배신하였을 때는 같이 배신하라
((CC)(CC)(CD) 이후에 D)

3. 사과를 받아들이라: 협력관계가 다시 정립되었을 때 계속 협력하라
((CD)(DC)(CC) 이후에 C)

4. 잊어버리라: 배신 후에 협력관계가 재정립되었을 때 협력하라
((DC)(CC)(CC) 이후에 C)

5. 관례를 받아들이라: 세번의 상호 배신 이후에 배신하라
((DD)(DD)(DD) 이후에 D)
------------------------------------------------------------------------------

------------------------------------------------------------------------------
제가 해본 결과는 신기하게도 ((CC)(CC)(CC) 이후에 D)로 진화하
는 개체가 되는군요...^^;; 아마도 어딘가 잘못한듯 싶은데... 혹시
시간 있으신 분은 한번 위 프로그램을 짜보세요...그래서 결과를 올
려 주시면 감사...(제가 한번 보고 싶어서여...^^;;)
-------------
이번에는 유전자 알고리즘의 동작 원리에 대해서 하겠습니다.

우선 스키마란 개념을 알 필요가 있습니다. 스키마란 염색체들 사이의
유사성을 나타낼 수 있는 하나의 전형 입니다. 이것은 '*'기호를 사용해
서 나타내는데 '*'기호에는 어떠한 유전인자와도 대응된다는 표시 입니
다. 예를 들어 (*10101101)은 다음 두개의 염색체와 일치 합니다.

(010101101), (110101101)

다른 스키마 (*101*01010*)은 다음 염색체와 일치 합니다.

(01010010100), (01010010101), (01011010100) (01011010101)
(11010010100), (11010010101), (11011010100) (11011010101)

스키마에서 '*'기호의 갯수가 k개이면 이것에 대응되는 염색체의 갯수
는 2^k개가 됩니다.

그러면은 유전 알고리즘은 #1에서 논의한 대로 선택, 교배연산, 돌연변
이연산을 거치게 됩니다. 우선 선택 연산만 생각하고 어떤 스키마 S가 살
아 남을 확률을 계산해 보겠습니다.

만약 어떤 염색체의 적합도가 상당히 높다면은 그 염색체는 다음세대에
여러개가 생길것이라고 예측할수 있습니다. 예를 들어 염색체가 다음과 같
이 있고, (강좌 #1에서의 문제 입니다)

v[1]: (01101)
v[2]: (01111)
v[3]: (10100)
v[4]: (01101)
v[5]: (10101)
v[6]: (01011)
v[7]: (10111)
v[8]: (10000)
v[9]: (01101)
v[10]: (01011)

각 염색체의 적합도는 다음과 같습니다.

eval(v[1]) = f(13) = 30
eval(v[2]) = f(15) = 50
eval(v[3]) = f(20) = 100
eval(v[4]) = f(13) = 30
eval(v[5]) = f(21) = 110
eval(v[6]) = f(11) = 10
eval(v[7]) = f(24) = 130
eval(v[8]) = f(16) = 60
eval(v[9]) = f(13) = 30
eval(v[10]) = f(11) = 10

위 개체의 총 적합도는 560이고, 평균 적합도는 56이 됩니다. 염색체
v[7]같은 경우에는 평균 적합도의 약 2배정도 됩니다. 이것은 다음 세대
에 염색체 v[7]이 약 2개정도 있을거라고 짐작할수 있습니다. 다음 세대
에서도 v[7]의 적합도가 높다면은 2개에서 4개로 늘어날수 있고, 그 다
음에 8개라는 식으로(실제로는 평균 적합도가 높아져서 이렇게 되기 힘
들겠지만) 될 것이라 생각할수 있습니다. 즉, 어떤 염색체가 평균 적합
도보다 높으면 그것은 다음 세대에서 기하 급수적으로 늘어날 것을 예상
할수 있습니다. 같은 방법으로 염색체가 평균 적합도보다 낮다면은 그것
은 다음 세대에서 기하 급수적으로 줄어 들게 되고, 평균이라면은 같은
수의 염색체들이 다음세대에 살아 있을거라 예상 할수 있습니다.

그러면 스키마 S와 일치하는 염색체들의 적합도의 평균을 구합니다.
이것을 스키마 S의 적합도라 부르겠습니다.
그러면 만약 스키마 S의 적합도가 평균 적합도 보다 크다면 다음 세대
에 스키마 S와 일치하는 염색체는 기하 급수적으로 늘어날 것이고, 평균
이라면 같은수의 염색체가 존재할 것이고, 평균 이하라면은 기하 급수적
으로 줄어들것이라고 예측할수 있습니다.

그러면 선택된 스키마가 교배나 돌연변이 연산에 의해서 파괴될수도
있습니다. 교배연산에서 다음과 같은 스키마가 선택되었다고 합시다.

(***111*****), (111*****01*)

그러면은 교배연산은 각 유전인자의 위치에 대해 골고루 선택될 확률이
높으므로 첫번째 스키마가 파괴될 확률은 두번째 스키마가 파괴될 확률보
다 낮습니다. 엄밀하게 계산하면 첫번째 스키마가 파괴될 확률은 3/11이
고, 두번재 스키마가 파괴될 확률은 10/11이 됩니다.

그리고 또 돌연변이 연산을 하게 됩니다. 돌연 변이할때는 고정된 유전
인자의 개수가 적을수록 스키마가 파괴될 확률이 적어지게 됩니다.(고정된
유전인자의 개수를 차수라고 합니다 : (101*1****)의 차수는 4가 됩니다),
위에 있는 염색체에서 첫번째 스키마의 차수는 3이 되고, 돌연변이 연산에
의해 파괴될 확률은 {1-(돌연변이 확률)}^3이 되고, 두번째 스키마가 돌연
변이에 의해 파괴될 확률은 {1-(돌연변이 확률)}^5가 됩니다.

즉, 이것을을 정리하면

선택 - 평균 이상이면 지수합수적으로 증가한다.
교배및 돌연변이 연산 - 짧고, 낮은 차수의 스키마는 생존율이 높다.

이것으로 부터 다음과 같은 스키마 정리와 구설부 가설이 생기게 됩니다.

------------------------------------------------------------------------------
스키마 정리 - 짧고, 낮은 차수의, 평균 이상의 스키마는 유전자 알고리
즘의 이후 세대에서 지수함수적으로 증가하는 시도를 받는다.

즉, GA는 짧고 낮은 차수의 스키마에 의해서 공간을 탐색한다는 것입니다.

구설부 가설 - 유전자 알고리즘은 구설부라고 불리는 짧고, 낮은 차수의,
고성능 스키마타의 병렬을 통하여 근사 최적 성능을 추구한다.
------------------------------------------------------------------------------

------------------------------------------------------------------------------
수학적인 식들은 다 제거해버리고 썼습니다.


[출처 : http://zzpark.egloos.com/5508970]

+ Recent posts