chriswarbo-net: aa57694334920ef41a1ed24fee2f8a9435b264d9
1: window.crossover_population = [];
2: window.crossover_blobs = [];
3: window.gradient = [];
4:
5: // Literate Programming merge sort (stable, in-place sort)
6: function msort(array, begin, end)
7: {
8: var size=end-begin;
9: if(size<2) return;
10:
11: var begin_right=begin+Math.floor(size/2);
12:
13: msort(array, begin, begin_right);
14: msort(array, begin_right, end);
15: merge_inplace(array, begin, begin_right, end);
16: }
17:
18: function merge_sort_inplace(array)
19: {
20: msort(array, 0, array.length);
21: }
22:
23: function merge_sort(array,comparison)
24: {
25: if(array.length < 2)
26: return array;
27: var middle = Math.ceil(array.length/2);
28: return merge(merge_sort(array.slice(0,middle),comparison),
29: merge_sort(array.slice(middle),comparison),
30: comparison);
31: }
32:
33: function merge_sort(array,comparison)
34: {
35: if(array.length < 2)
36: return array;
37: var middle = Math.ceil(array.length/2);
38: return merge(merge_sort(array.slice(0,middle),comparison),
39: merge_sort(array.slice(middle),comparison),
40: comparison);
41: }
42:
43: Array.prototype.swap=function(a, b)
44: {
45: var tmp=this[a];
46: this[a]=this[b];
47: this[b]=tmp;
48: }
49:
50: function insert(array, begin, end, v)
51: {
52: while(begin+1<end && array[begin+1]<v) {
53: array.swap(begin, begin+1);
54: ++begin;
55: }
56: array[begin]=v;
57: }
58:
59: function merge(left,right,comparison)
60: {
61: var result = new Array();
62: while((left.length > 0) && (right.length > 0))
63: {
64: if(comparison(left[0],right[0]) <= 0)
65: result.push(left.shift());
66: else
67: result.push(right.shift());
68: }
69: while(left.length > 0)
70: result.push(left.shift());
71: while(right.length > 0)
72: result.push(right.shift());
73: return result;
74: }
75:
76: // End merge sort
77:
78: $(function() {
79: $('#crossover_playfield').svg({onLoad: crossover_init});
80: $('#crossover_playfield').click(function(){
81: $('#crossover_playfield').unbind('click');
82: crossover_step();
83: });
84: $('#crossover_population').change(function(){
85: $('#crossover_population_display').text($('#crossover_population').val());
86: });
87: $('#crossover_population').change();
88:
89: $('#crossover_fitness').text('0');
90:
91: $('#crossover_rate').change(function(){
92: $('#crossover_rate_display').text(Math.round($('#crossover_rate').val()));
93: });
94: $('#crossover_rate').change();
95: });
96:
97: crossover_init = function(svg) {
98: _.times(Math.round(Math.random() * 100 + 1), function(){
99: var l = Math.random();
100: var r = Math.random();
101: var t = Math.random();
102: var b = Math.random();
103: var temp;
104: if (l > r) {
105: temp = l;
106: l = r;
107: r = temp;
108: }
109: if (t > b) {
110: temp = t;
111: t = b;
112: b = temp;
113: }
114: window.gradient.push(function(x, y, draw){
115: var w = $('#crossover_playfield').width();
116: var h = $('#crossover_playfield').height();
117: if (draw) {
118: $('#crossover_playfield').svg('get').rect(l*w, t*h, (r-l)*w, (b-t)*h, {'fill': 'white', 'fill-opacity': 0.02});
119: }
120: else {
121: if ((l < x / w) && (r > x / w) && (t < y / h) && (b > y / h)) {
122: return 1;
123: }
124: return 0;
125: }
126: });
127: });
128: $('#crossover_playfield').svg('get').rect(0,0,$('#crossover_playfield').width(),$('#crossover_playfield').height(),{'fill': 'black'});
129: _.each(window.gradient, function(a){a(0, 0, true);});
130: }
131:
132: fitn_reduce = function(a,b) { return a + b; };
133: fitn = _.memoize(function(z) {
134: var x = z[0];
135: var y = z[1];
136: if ((x < 0) || (x > $('#crossover_playfield').width())) { return 0; }
137: if ((y < 0) || (y > $('#crossover_playfield').height())) { return 0; }
138: return _.reduce(_.map(window.gradient, function(a){ return a(x, y, false); }), fitn_reduce);
139: });
140:
141: // Compare 2 solutions based on their fitness
142: comp = function (a, b) {
143: var fit_a = fitn(a['solution']);
144: var fit_b = fitn(b['solution']);
145: if (fit_a < fit_b) {
146: return -1;
147: }
148: if (fit_a > fit_b) {
149: return 1;
150: }
151: return 0;
152: };
153:
154: crossover = function(a, b) {
155: if (typeof a === 'undefined' || typeof b == 'undefined') { return 0; }
156: // Duplicate the parents, turning [x, y] into xy
157: var child1 = (512 * a['solution'][0]) + a['solution'][1];
158: var child2 = (512 * b['solution'][0]) + b['solution'][1];
159: var w = $('#crossover_playfield').width();
160: var h = $('#crossover_playfield').height();
161: var crossover_points = [];
162: _.times($('#crossover_rate').val(), function() {
163: do {
164: point = Math.round(Math.random() * 16.0);
165: } while ($.inArray(point, crossover_points) !== -1);
166: crossover_points.push(point);
167: });
168: _.each(crossover_points, function(p) {
169: var point = Math.pow(2, p);
170: var section1 = child1 % point;
171: var section2 = child2 % point;
172: child1 = child1 - section1 + section2;
173: child2 = child2 - section2 + section1;
174: });
175: // Now split apart the x and y again
176: var temp = child1 % 512;
177: child1 = {
178: solution:[temp, (child1 - temp) / 512],
179: blob:$('#crossover_playfield').svg('get').circle(temp, (child1-temp)/512, 2, {fill: 'red'})
180: };
181: temp = child2 % 512;
182: child2 = {
183: solution:[temp, (child2 - temp) / 512],
184: blob:$('#crossover_playfield').svg('get').circle(temp, (child2-temp)/512, 2, {fill: 'red'})
185: };
186: return [child1, child2];
187: }
188:
189: window.root2 = Math.sqrt(2);
190:
191: window.get_mid = _.memoize(function(top, bottom) {
192: return Math.ceil((top - bottom) / window.root2);
193: });
194:
195: window.reproduce = function() {
196: // Choose a parent probabilistically. We divide our population in 2
197: // over and over until we narrow down a single solution.
198: var top = window.crossover_population.length;
199: var bottom = 0;
200: // This function finds the point x such that the probability from
201: // bottom to x is equal to the probability from x to top
202: var mid;
203: var parents = [];
204: // Beware while loops! Do as little as possible in them.
205: do {
206: top = window.crossover_population.length;
207: bottom = 0;
208: while (top - bottom > 1) {
209: mid = get_mid(top, bottom);
210: if (Math.random() > 0.5) {
211: top = mid;
212: }
213: else {
214: bottom = mid;
215: }
216: }
217: bottom = Math.ceil(bottom);
218: if ($.inArray(bottom, parents) === -1) {
219: parents.push(Math.ceil(bottom));
220: }
221: } while (parents.length < 2);
222: // We have 2 parents, perform a crossover
223: var children = crossover(window.crossover_population[parents[0]], window.crossover_population[parents[1]]);
224: window.new_solutions.push(children[0]);
225: window.new_solutions.push(children[1]);
226: };
227:
228: window.draw_blobs = function(x) {
229: var solution = window.crossover_population[window.crossover_population.length - 1];
230: window.crossover_blobs.push(
231: $('#crossover_playfield').svg('get').circle(solution['solution'][0], solution['solution'][1], 5, {fill: 'lime'})
232: );
233: };
234:
235: crossover_step = function() {
236: // Grab our population first (if we keep reading the input, we
237: // may get inconsistent values)
238: var pop = parseInt($('#crossover_population').val());
239:
240: // We are swapping 1/4 of the population each time
241: var to_swap = Math.ceil(pop / 4.0);
242:
243: window.new_solutions = [];
244:
245: // Bulk out the population if the slider has been increased
246: if (pop > window.crossover_population.length) {
247: window.crossover_population.reverse();
248: window.crossover_population.push.apply(window.crossover_population, _.map(_.range(pop - window.crossover_population.length), function(x) {
249: return {
250: solution:[Math.round(Math.random()*$('#crossover_playfield').width()),Math.round(Math.random()*$('#crossover_playfield').height())],
251: blob:false
252: };
253: }));
254: window.crossover_population.reverse();
255: }
256:
257: _.times(Math.ceil(to_swap / 2), window.reproduce);
258:
259: // Sort the population by fitness
260: if ($('#crossover_stable').is(':checked')) {
261: window.crossover_population = merge_sort(window.crossover_population, comp);
262: }
263: else {
264: window.crossover_population.sort(comp);
265: }
266:
267: $('#crossover_fitness').text(fitn(window.crossover_population[window.crossover_population.length - 1]['solution'])+"");
268:
269: // At this point we have a sorted array, so draw our blobs
270: while (window.crossover_blobs.length > 0) {
271: $('#crossover_playfield').svg('get').remove(window.crossover_blobs.shift());
272: }
273:
274: window.draw_blobs(0);
275:
276: // Throw away any excess solutions and the least-fit solutions
277: var s;
278: while (window.crossover_population.length > pop) {
279: window.mid_points = [];
280: s = window.crossover_population.shift();
281: if (s['blob']) {
282: $('#crossover_playfield').svg('get').remove(s['blob']);
283: }
284: }
285:
286: // Add the new solutions to the mix
287: window.crossover_population.reverse();
288: window.crossover_population.push.apply(window.crossover_population, window.new_solutions);
289: window.crossover_population.reverse();
290:
291: window.setTimeout(function(){ crossover_step(); }, 10);
292: }
Generated by git2html.