오늘은~^^*
최.적.경.로. 찾기 과정~ 어디까지 진행되고 있나~~^^* 표현하는 코드를 공부하는 날이예요~^^*
신선한 한기가 느껴지는 산뜻한 아침~^^* 두툼하고 폭신한 첼로 브러시로 마음결을 부드럽게 빗을까요?
아침 단장 즐겁게 하고 계셔요~^^* 코딩 공부 정리해서 돌아올게요~^^* 쓩~^^*
네~^^*
다루어야 할 모든 경우 중 현재까지 몇 개의 경우를 다루었나?를 계산하여 표현하면 어떨까요~^^*
네~!!! 그런 것 같아요~!!!
분수의 개념이 되는 것 같네요!!!
음,,,그럼…분자는…? 그리고 분모는….?
현재 몇 개의 경우를 다루었나 / 다루어야 할 모든 경우
네~^^*
분자는 <현재 몇 개의 경우를 다루었나>이고 분모는 <다루어야 할 모든 경우>가 될 것 같네요~^^*
하나. 그럼 먼저~ <현재 몇 개의 경우를 다루었나>를 표현할 변수를 준비해 볼게요~^^*
var count = 0;
둘. 경로를 생성할 때마다 변수 count를 1 증가시켜 볼게요~^^*
function setup() {
.
.
.
for (var i = 0; i < totalCities; i++) {
var v = createVector(random(width), random(height));
cities[i] = v;
order[i] = i;
}
count++;
.
.
.
}
//최초의 경로가 만들어 질 때도 count를 1 증가시키면, 경우의 수를 1부터 시작할 수 있을 것 같아요.
//경우의 수를 0부터 시작하는 것보다 좀 더 자연스러운 느낌~^^*
function nextOrder() {
.
.
.
count++;
}
//새로운 경로가 만들어지면 count를 1 증가시켜 보겠습니다~^^*
셋. 모든 경우의 수를 담을 변수를 준비하겠습니다~^^*
var totalPermutations;
넷. 모든 경우의 수는 n! 팩토리얼이니까요~^^* Factorial 팩토리얼을 계산하는 함수를 만들어 볼게요~^^*
1! = 1;
2! = 2 * 1;
3! = 3 * 2 * 1;
4! = 4 * 3* 2 * 1;
5! = 5 * 4 * 3 * 2 * 1;
n! = n * (n-1) * ((n-1) -1) * (((n-1) -1) -1) * ... * 1;
음…parameter로 n을 받아서, n-1의 팩토리얼 Facotrial을 계산하는 재귀함수 factorial(n-1)을 호출해서 그 return 값에 n을 곱하는 재귀함수 factorial(n)이 필요할 것 같아요…
n이 1이 될 때까지 n-1의 factorial 팩토리얼을 계산하는 자기 자신을 계속 호출하는 썰물 파도가~~~~,
n이 1이 되면 n! factorial을 계산하여 반환하고, 반환받은 그 값에 n (이때 n은 이전 n에 1을 더한 값을 가지고 있겠지요…?)을 곱하여, 다시 n! factorial 값으로 반환하는 밀물 파도가~~~~,
호출 썰물과 반환 밀물이 발생하는 재귀함수의 바다를 만들어 보겠습니다~~^^*
function factorial(n) {
if (n == 1) {
return 1;
} else {
return n * factorial(n - 1);
}
}
다섯. 재귀함수 factorial(n)의 값을 return 반환받아 변수 totalPermutaions (순열의 모든 경우의 수)에 저장하겠습니다~
function setup() {
.
.
.
totalPermutations = factorial(totalCities);
}
여섯. 현재까지 다룬 경우의 수 (count) / 모든 경우의 수 (totalPermutation)를 백분율 percentage로 나타내 보겠습니다.
(i) 변수 percent를 마련해 볼까요~^^*
(ii) 소수점 둘째 자리까지 담아서 percent 값을 시각적으로 표현해 볼게요~^^* 함수 nf()를 사용하면 백분율을 소수점 몇째 자리까지 표현할 지 지정할 수 있습니다~^^*
function draw() {
.
.
.
textSize = 32;
fill(255);
var percent = 100 * (count / totalPermutations);
text(nf(percent, 0, 2) + "% completed", 20, height - 50);
.
.
.
}
그럼, 우리 전체 코드를 함께 살펴 볼까요~~^^*
var cities = [];
var order = [];
var totalCities = 5;
var recordDistance;
var bestEver;
var count = 0;
var totalPermutations;
function setup() {
createCanvas(400, 400);
for (var i = 0; i < totalCities; i++) {
var v = createVector(random(width), random(height));
cities[i] = v;
order[i] = i;
}
count++;
var d = calcDistance(cities, order);
recordDistance = d;
bestEver = order.slice();
totalPermutations = factorial(totalCities);
}
//가장 짧은 경로의 거리값을 저장할 변수 recordDistance를 준비하겠습니다.
//이때의 도시 구성요소 인덱스 배열을 저장할 변수 bestEver를 준비하겠습니다.
//첫번째 도시 경로의 거리값을 저장한 변수 d의 값을 변수 recordDistance에 저장하겠습니다.
//이때의 도시 구성요소 인덱스 배열을 복사하여 변수 bestEver에 저장하겠습니다.
//도시총갯수 toralCities의 팩토리얼 값을 구하여 변수 toralPermutation에 저장하겠습니다.
function draw() {
background(0);
fill(255);
console.log(count, totalPermutations);
for (var i = 0; i < cities.length; i++) {
ellipse(cities[i].x, cities[i].y, 8, 8);
}
stroke(255, 0, 255);
strokeWeight(4);
noFill();
beginShape();
for (var i = 0; i < order.length; i++) {
var n = bestEver[i];
vertex(cities[n].x, cities[n].y);
}
endShape();
//가장 빠른 경로의 도시 인덱스 배열인 bestEver의 구성요소들(도시 배열 구성요소의 인덱스)이 가리는 도시의 위치 좌표를 꼭지점으로 하여 핫핑크 선을 굵게 그려 볼까요~^^*
stroke(255);
strokeWeight(1);
noFill();
beginShape();
for (var i = 0; i < order.length; i++) {
var n = order[i];
vertex(cities[n].x, cities[n].y);
}
endShape();
var d = calcDistance(cities, order);
if (d < recordDistance) {
recordDistance = d;
bestEver = order.slice();
}
textSize = 32;
fill(255);
var percent = 100 * (count / totalPermutations);
text(nf(percent, 0, 2) + "% completed", 20, height - 50);
//현재까지 다룬 경우의 수 / 모든 경우의 수
//를 백분율로 변환하겠습니다.
//소수점 둘째 자리까지 나타내겠습니다.
nextOrder();
}
function nextOrder() {
//Lexicographic Order 사전식 배열
var largestI = -1;
for (var i = 0; i < order.length - 1; i++) {
if (order[i] < order[i + 1]) {
largestI = i;
}
}
if (largestI == -1) {
noLoop();
console.log('finished');
}
//STEP 1: largestI 구하기
var largestJ = -1;
for (var j = 0; j < order.length; j++) {
if (order[largestI] < order[j]) {
largestJ = j;
}
}
//STEP 2: largestJ 구하기
swap(order, largestI, largestJ);
//STEP 3: largestI와 largestJ 맞바꾸기
var endArray = order.splice(largestI + 1);
endArray.reverse();
order = order.concat(endArray);
//STEP 4: 맞바꿈이 일어난 지점 뒷부분 거꾸로 정렬하기
count++;
}
function swap(a, i, j) {
var temp = a[i];
a[i] = a[j];
a[j] = temp;
}
function calcDistance(points, order) {
var sum = 0;
for (var i = 0; i < order.length - 1; i++) {
var cityAIndex = order[i];
var cityA = points[cityAIndex];
var cityBIndex = order[i + 1];
var cityB = points[cityBIndex];
var d = dist(cityA.x, cityA.y, cityB.x, cityB.y);
sum += d;
}
return sum;
}
//도시 배열의 구성요소의 인덱스 값을 모은 배열 order의 i 번째 값을 변수 cityAIndex에 저장합니다.
//qoduf order의 i + 1 번째 값을 변수 cityBIndex에 저장합니다.
//도시들을 모은 배열 point의 cityAIndex번째 도시를 변수 cityA에 저장합니다.
//배열 point의 cityBIndex번째 도시를 변수 cityB에 저장합니다.
// cityA의 위치와 cityB의 위치 사이의 거리를 계산하여 변수 d에 저장합니다.
// 거리 총합을 나타내는 변수 sum에 더합니다.
function factorial(n) {
if (n == 1) {
return 1;
} else {
return n * factorial(n - 1);
}
}
//n!를 계산하는 재귀함수입니다~^^*
5개의 도시를 여행하는 최.적.경.로.가 찾아지는 과정을 편안한 마음으로 지켜볼까요, 우리~^^*
이번에는 10개 도시를 도전해 봅시다~~^^* YEAH~~^^*
와우! 10!은 정말 큰 수이구나!! 실감할 수 있는 것 같아요!!! 한참을 지켜봐도 1 percent까지도 진행되지 않는 것을 보니까요…와우!!! 3628800는 정말 큰 수인 것 같아요!!!
오늘 저와 함께 최.적.경.로. 찾기 과정의 현재 진행상황까지 표시해 주는 프로그램을 완성해 주셔서 감사합니다~^^*
와우! 우리가 동영상 강의 하나를 또 마무리 해내었네요~^^*
너무 멋진 프로그램이 완성된 것 같아요~^^* 함께 만들어 주셔서 감사합니다~^^*
우리~~^^* 내일부터는~~^^*
Travelling Salesman Problem을 해결하기 위한 최.적.경.로. 찾기 과정을 Genetic Algorithm을 기반으로 새롭게 작업해 볼까요~^^*
어떤 부분은 어떤 방식으로 변화시켜 볼 수 있을까요~^^* SWAP()의 정신을 창의적으로 실천해 볼까요~^^* 또 다르게 멋진 창작의 과정이 될 것 같아요~^^*
많은 땀방울을 흘려 깜짝 놀랍도록 멋진 공연 작품을 완성하는 것처럼~~^^*
새로운 고민을 담아 또 다르게 멋지고 깜짝 놀라운 공연 작품을 탄생시키는 것처럼~~^^*
우리도~~^^*
내일부터~~^^*
Genetic Algorithm 버전의 새로운 Travelling Salesman Problem 프로그램을 만들어 이 세상에 선물해 볼까요~~^^*
YEAH~~^^*
오늘도
점심 맛있게 드시고요!
보람있는 하루 즐겁게 보내시고요!
따뜻한 저녁 피로 푸는 밤 되시기 바래요!
내일 또 만나는 거예요, 우리~~^^*?
네^^* 좋아요~^^* 고마워요~^^*
네~~^^* 꿈은 이루어 집니다~~^^*
댓글 남기기