之前一直用Python來寫優化演算法,為了增強 JS 的熟練程度,開始將原有的程式碼改寫成 JS。採用的工具包括 node.js + Grunt + nodeunit + github + npm + travis-ci。
最初的版本採用過程式的方式實現,沒有采用物件導向或事件驅動的模式。
#!/usr/bin/env node --harmony // Random Search "use strict"; var util = require("util"); function objective_function(v) { return v.map(function(x) { return x*x; }).reduce(function(a, b) { return a+b; }); } function random_vector(min_max) { return min_max.map(function(x) { return x[0] + (x[1] - x[0]) * Math.random(); }); } function search(search_space, max_iteration) { var best = {}; for (var iteration = 0; iteration < max_iteration; iteration++) { var candidate = { 'vector': random_vector(search_space) }; candidate['cost'] = objective_function(candidate['vector']); //console.log(candidate); if (iteration === 0 || candidate['cost'] < best['cost']) { best = candidate; } console.log(' > iteration=' + (iteration+1) + ', best=' + best['cost']); } return best; } function generate_array(element, repeat) { return new Array(repeat+1).join(1).split('').map(function(){return element;}); } function run () { var problem_size = 2; var search_space = generate_array([-5, 5], problem_size); var max_iteration = 100; var best = search(search_space, max_iteration); console.log("Done. Best Solution: " + util.inspect(best)); } exports.objective_function = objective_function; exports.random_vector = random_vector; exports.generate_array = generate_array; exports.search = search; exports.run = run;
呼叫方式很簡單。
var rs = require('clever_algorithms_js').random_search; rs.run();
單元測試:
var random_search = require('../../lib/stochastic/random_search'); exports['objective'] = function (test) { test.equal(random_search.objective_function([1, 2]), 5); test.done(); }; exports['random_vector'] = function (test) { var rv = random_search.random_vector([[1, 2], [2, 3]]); test.equal(rv.length, 2); test.ok(1 <= rv[0] && rv[0] <= 2); test.ok(2 <= rv[1] && rv[1] <= 3); test.done(); }; exports['generate_array'] = function (test) { var a = random_search.generate_array([-5, 5], 2); test.equal(a.length, 2); test.deepEqual(a, [[-5,5], [-5,5]]); test.done(); }; exports['search'] = function (test) { var problem_size = 2, search_space = random_search.generate_array([-5, 5], problem_size), max_iter = 100; var best = random_search.search(search_space, max_iter); test.notEqual(best, {}); test.ok(-5 <= best['cost'] && best['cost'] <= 5); test.done(); };
如果採用CoffeeScript進行改寫的話,程式碼會更簡潔一些:
# Random Search util = require("util"); objective_function = (v) -> v.reduce (x,y) -> x*x + y*y random_vector = (min_max) -> min_max.map (rx) -> rx[0] + (rx[1] - rx[0]) * Math.random() generate_array = (element, repeat) -> (element for [1..repeat]) search = (search_space, max_iteration) -> best = {} for iteration in [0..max_iteration-1] candidate = { 'vector': random_vector(search_space) } candidate['cost'] = objective_function(candidate['vector']) best = candidate if iteration == 0 || candidate['cost'] < best['cost'] console.log ' > iteration=' + (iteration+1) + ' best=' + best['cost']; best run = () -> problem_size = 2 search_space = generate_array([-5, 5], problem_size) max_iteration = 100 best = search(search_space, max_iteration) console.log "Done. Best Solution: " + util.inspect(best); return exports.objective_function = objective_function; exports.random_vector = random_vector; exports.generate_array = generate_array; exports.search = search; exports.run = run;
編譯出的JavaScript程式碼,看起來是這個樣子:
(function() { var generate_array, objective_function, random_vector, run, search, util; util = require("util"); objective_function = function(v) { return v.reduce(function(x, y) { return x * x + y * y; }); }; random_vector = function(min_max) { return min_max.map(function(rx) { return rx[0] + (rx[1] - rx[0]) * Math.random(); }); }; generate_array = function(element, repeat) { var _i, _results; _results = []; for (_i = 1; 1 <= repeat ? _i <= repeat : _i >= repeat; 1 <= repeat ? _i++ : _i--) { _results.push(element); } return _results; }; search = function(search_space, max_iteration) { var best, candidate, iteration, _i, _ref; best = {}; for (iteration = _i = 0, _ref = max_iteration - 1; 0 <= _ref ? _i <= _ref : _i >= _ref; iteration = 0 <= _ref ? ++_i : --_i) { candidate = { 'vector': random_vector(search_space) }; candidate['cost'] = objective_function(candidate['vector']); if (iteration === 0 || candidate['cost'] < best['cost']) { best = candidate; } console.log(' > iteration=' + (iteration + 1) + ' best=' + best['cost']); } return best; }; run = function() { var best, max_iteration, problem_size, search_space; problem_size = 2; search_space = generate_array([-5, 5], problem_size); max_iteration = 100; best = search(search_space, max_iteration); console.log("Done. Best Solution: " + util.inspect(best)); }; exports.objective_function = objective_function; exports.random_vector = random_vector; exports.generate_array = generate_array; exports.search = search; exports.run = run; }).call(this);