Algorithmically 0.1.5 → 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +6 -6
- data/lib/Algorithmically/Evolutionary/genetic.rb +63 -61
- data/lib/Algorithmically/Neural/perceptron.rb +58 -56
- data/lib/Algorithmically/Stochastic/adaptive_random_search.rb +65 -63
- data/lib/Algorithmically/Stochastic/guided_local_search.rb +89 -87
- data/lib/Algorithmically/Stochastic/hill_climbing.rb +33 -31
- data/lib/Algorithmically/Stochastic/random_search.rb +28 -26
- data/lib/Algorithmically/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c6b37344d80011486740e722946df13475c44ba5
|
4
|
+
data.tar.gz: 46afbc8274889d7660acf6fd42f5e3e78260851e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1831736b94dc14f2234ebfe1c2ca1c3c33f9d1ff301dd81f070c97bce781e517e38929ba6579467f60fda7093f3d267aa71fc28736850f97ae42c9775c4c75e0
|
7
|
+
data.tar.gz: a68582cd9aebbb76d27b047643d5160ba6ce1e352e45299512088dfcfbc326d5a8a2f22b58d21006098b1af2b9cdc7701210c7eb481294af92b99504e71e7066
|
data/README.md
CHANGED
@@ -22,20 +22,20 @@ Or install it yourself as:
|
|
22
22
|
|
23
23
|
### Evolutionary Algorithms
|
24
24
|
|
25
|
-
Algorithmically::Genetic.new(100, 64, 100, 0.98)
|
25
|
+
Algorithmically::Evolutionary::Genetic.new(100, 64, 100, 0.98)
|
26
26
|
|
27
27
|
### Neural Algorithms
|
28
28
|
|
29
|
-
Algorithmically::Perceptron.new([[0,0,0], [0,1,1], [1,0,1], [1,1,1]], 2, 20, 0.1)
|
29
|
+
Algorithmically::Neural::Perceptron.new([[0,0,0], [0,1,1], [1,0,1], [1,1,1]], 2, 20, 0.1)
|
30
30
|
|
31
31
|
### Stochastic Algorithms
|
32
32
|
|
33
|
-
Algorithmically::RandomSearch.new(2, 50)
|
33
|
+
Algorithmically::Neural::RandomSearch.new(2, 50)
|
34
34
|
|
35
|
-
Algorithmically::AdaptiveRandomSearch.new(1000, 2, 0.05, 1.3, 3.0, 10, 30)
|
35
|
+
Algorithmically::Neural::AdaptiveRandomSearch.new(1000, 2, 0.05, 1.3, 3.0, 10, 30)
|
36
36
|
|
37
|
-
Algorithmically::HillClimbing.new(2, 1000)
|
37
|
+
Algorithmically::Neural::HillClimbing.new(2, 1000)
|
38
38
|
|
39
|
-
Algorithmically::GuidedLocalSearch.new(150, [[565,575],[25,185],[345,750],[945,685]], 20, 0.3)
|
39
|
+
Algorithmically::Neural::GuidedLocalSearch.new(150, [[565,575],[25,185],[345,750],[945,685]], 20, 0.3)
|
40
40
|
|
41
41
|
|
@@ -1,77 +1,79 @@
|
|
1
|
-
module
|
1
|
+
module Algorithmically
|
2
|
+
module Evolutionary
|
2
3
|
|
3
|
-
|
4
|
+
class Genetic
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
def onemax(bitstring)
|
12
|
-
sum = 0
|
13
|
-
bitstring.size.times { |i| sum+=1 if bitstring[i].chr=='1' }
|
14
|
-
sum
|
15
|
-
end
|
6
|
+
def initialize(max_gens, num_bits, pop_size, p_crossover)
|
7
|
+
p_mutation = 1.0/num_bits
|
8
|
+
best = search(max_gens, num_bits, pop_size, p_crossover, p_mutation)
|
9
|
+
puts "done! Solution: f=#{best[:fitness]}, s=#{best[:bitstring]}"
|
10
|
+
end
|
16
11
|
|
17
|
-
|
18
|
-
|
19
|
-
|
12
|
+
def onemax(bitstring)
|
13
|
+
sum = 0
|
14
|
+
bitstring.size.times { |i| sum+=1 if bitstring[i].chr=='1' }
|
15
|
+
sum
|
16
|
+
end
|
20
17
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
(pop[i][:fitness] > pop[j][:fitness]) ? pop[i] : pop[j]
|
25
|
-
end
|
18
|
+
def random_bitstring(num_bits)
|
19
|
+
(0...num_bits).inject("") { |s, i| s<<((rand<0.5) ? "1" : "0") }
|
20
|
+
end
|
26
21
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
child << ((rand()<rate) ? ((bit=='1') ? "0" : "1") : bit)
|
22
|
+
def binary_tournament(pop)
|
23
|
+
i, j = rand(pop.size), rand(pop.size)
|
24
|
+
j = rand(pop.size) if j==i
|
25
|
+
(pop[i][:fitness] > pop[j][:fitness]) ? pop[i] : pop[j]
|
32
26
|
end
|
33
|
-
child
|
34
|
-
end
|
35
27
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
28
|
+
def point_mutation(bitstring, rate=1.0/bitstring.size)
|
29
|
+
child = ""
|
30
|
+
bitstring.size.times do |i|
|
31
|
+
bit = bitstring[i].chr
|
32
|
+
child << ((rand()<rate) ? ((bit=='1') ? "0" : "1") : bit)
|
33
|
+
end
|
34
|
+
child
|
35
|
+
end
|
41
36
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
p2 = selected[0] if i == selected.size-1
|
47
|
-
child = {}
|
48
|
-
child[:bitstring] = crossover(p1[:bitstring], p2[:bitstring], p_cross)
|
49
|
-
child[:bitstring] = point_mutation(child[:bitstring], p_mutation)
|
50
|
-
children << child
|
51
|
-
children.size >= pop_size
|
37
|
+
def crossover(parent1, parent2, rate)
|
38
|
+
""+parent1 if rand()>=rate
|
39
|
+
point = 1 + rand(parent1.size-2)
|
40
|
+
parent1[0...point]+parent2[point...(parent1.size)]
|
52
41
|
end
|
53
|
-
children
|
54
|
-
end
|
55
42
|
|
56
|
-
|
57
|
-
|
58
|
-
|
43
|
+
def reproduce(selected, pop_size, p_cross, p_mutation)
|
44
|
+
children = []
|
45
|
+
selected.each_with_index do |p1, i|
|
46
|
+
p2 = (i.modulo(2)==0) ? selected[i+1] : selected[i-1]
|
47
|
+
p2 = selected[0] if i == selected.size-1
|
48
|
+
child = {}
|
49
|
+
child[:bitstring] = crossover(p1[:bitstring], p2[:bitstring], p_cross)
|
50
|
+
child[:bitstring] = point_mutation(child[:bitstring], p_mutation)
|
51
|
+
children << child
|
52
|
+
children.size >= pop_size
|
53
|
+
end
|
54
|
+
children
|
59
55
|
end
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
56
|
+
|
57
|
+
def search(max_gens, num_bits, pop_size, p_crossover, p_mutation)
|
58
|
+
population = Array.new(pop_size) do |i|
|
59
|
+
{:bitstring => random_bitstring(num_bits)}
|
60
|
+
end
|
61
|
+
population.each { |c| c[:fitness] = onemax(c[:bitstring]) }
|
62
|
+
best = population.sort { |x, y| y[:fitness] <=> x[:fitness] }.first
|
63
|
+
max_gens.times do |gen|
|
64
|
+
selected = Array.new(pop_size) { |i| binary_tournament(population) }
|
65
|
+
children = reproduce(selected, pop_size, p_crossover, p_mutation)
|
66
|
+
children.each { |c| c[:fitness] = onemax(c[:bitstring]) }
|
67
|
+
children.sort! { |x, y| y[:fitness] <=> x[:fitness] }
|
68
|
+
best = children.first if children.first[:fitness] >= best[:fitness]
|
69
|
+
population = children
|
70
|
+
puts " > gen #{gen}, best: #{best[:fitness]}, #{best[:bitstring]}"
|
71
|
+
break best[:fitness] == num_bits
|
72
|
+
end
|
73
|
+
best
|
71
74
|
end
|
72
|
-
|
75
|
+
|
73
76
|
end
|
74
77
|
|
75
78
|
end
|
76
|
-
|
77
79
|
end
|
@@ -1,78 +1,80 @@
|
|
1
|
-
module
|
1
|
+
module Algorithmically
|
2
|
+
module Neural
|
2
3
|
|
3
|
-
|
4
|
+
class Perceptron
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
6
|
+
def initialize(or_problem, inputs, iterations, learning_rate)
|
7
|
+
execute(or_problem, inputs, iterations, learning_rate)
|
8
|
+
end
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
10
|
+
def random_vector(minmax)
|
11
|
+
Array.new(minmax.size) do |i|
|
12
|
+
minmax[i][0] + ((minmax[i][1] - minmax[i][0]) * rand())
|
13
|
+
end
|
12
14
|
end
|
13
|
-
end
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
def initialize_weights(problem_size)
|
17
|
+
minmax = Array.new(problem_size + 1) { [-1.0, 1.0] }
|
18
|
+
random_vector(minmax)
|
19
|
+
end
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
21
|
+
def update_weights(num_inputs, weights, input, out_exp, out_act, l_rate)
|
22
|
+
num_inputs.times do |i|
|
23
|
+
weights[i] += l_rate * (out_exp - out_act) * input[i]
|
24
|
+
end
|
25
|
+
weights[num_inputs] += l_rate * (out_exp - out_act) * 1.0
|
23
26
|
end
|
24
|
-
weights[num_inputs] += l_rate * (out_exp - out_act) * 1.0
|
25
|
-
end
|
26
27
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
def activate(weights, vector)
|
29
|
+
sum = weights[weights.size-1] * 1.0
|
30
|
+
vector.each_with_index do |input, i|
|
31
|
+
sum += weights[i] * input
|
32
|
+
end
|
33
|
+
sum
|
31
34
|
end
|
32
|
-
sum
|
33
|
-
end
|
34
35
|
|
35
|
-
|
36
|
-
|
37
|
-
|
36
|
+
def transfer(activation)
|
37
|
+
(activation >= 0) ? 1.0 : 0.0
|
38
|
+
end
|
38
39
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
40
|
+
def get_output(weights, vector)
|
41
|
+
activation = activate(weights, vector)
|
42
|
+
transfer(activation)
|
43
|
+
end
|
44
|
+
|
45
|
+
def train_weights(weights, domain, num_inputs, iterations, lrate)
|
46
|
+
iterations.times do |epoch|
|
47
|
+
error = 0.0
|
48
|
+
domain.each do |pattern|
|
49
|
+
input = Array.new(num_inputs) { |k| pattern[k].to_f }
|
50
|
+
output = get_output(weights, input)
|
51
|
+
expected = pattern.last.to_f
|
52
|
+
error += (output - expected).abs
|
53
|
+
update_weights(num_inputs, weights, input, expected, output, lrate)
|
54
|
+
end
|
55
|
+
puts "> epoch=#{epoch}, error=#{error}"
|
56
|
+
end
|
57
|
+
end
|
43
58
|
|
44
|
-
|
45
|
-
|
46
|
-
error = 0.0
|
59
|
+
def test_weights(weights, domain, num_inputs)
|
60
|
+
correct = 0
|
47
61
|
domain.each do |pattern|
|
48
|
-
|
49
|
-
output = get_output(weights,
|
50
|
-
|
51
|
-
error += (output - expected).abs
|
52
|
-
update_weights(num_inputs, weights, input, expected, output, lrate)
|
62
|
+
input_vector = Array.new(num_inputs) { |k| pattern[k].to_f }
|
63
|
+
output = get_output(weights, input_vector)
|
64
|
+
correct += 1 if output.round == pattern.last
|
53
65
|
end
|
54
|
-
puts "
|
66
|
+
puts "Finished test with a score of #{correct}/#{domain.size}"
|
67
|
+
correct
|
55
68
|
end
|
56
|
-
end
|
57
69
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
correct += 1 if output.round == pattern.last
|
70
|
+
def execute(domain, num_inputs, iterations, learning_rate)
|
71
|
+
weights = initialize_weights(num_inputs)
|
72
|
+
train_weights(weights, domain, num_inputs, iterations, learning_rate)
|
73
|
+
test_weights(weights, domain, num_inputs)
|
74
|
+
weights
|
64
75
|
end
|
65
|
-
puts "Finished test with a score of #{correct}/#{domain.size}"
|
66
|
-
correct
|
67
|
-
end
|
68
76
|
|
69
|
-
def execute(domain, num_inputs, iterations, learning_rate)
|
70
|
-
weights = initialize_weights(num_inputs)
|
71
|
-
train_weights(weights, domain, num_inputs, iterations, learning_rate)
|
72
|
-
test_weights(weights, domain, num_inputs)
|
73
|
-
weights
|
74
77
|
end
|
75
78
|
|
76
79
|
end
|
77
|
-
|
78
80
|
end
|
@@ -1,82 +1,84 @@
|
|
1
|
-
module
|
1
|
+
module Algorithmically
|
2
|
+
module Stochastic
|
2
3
|
|
3
|
-
|
4
|
+
class AdaptiveRandomSearch
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
6
|
+
def initialize(max_iter, bounds, init_factor, s_factor, l_factor, iter_mult, max_no_impr)
|
7
|
+
problem_size = bounds
|
8
|
+
@bounds1 = Array.new(problem_size) { |i| [-5, +5] }
|
9
|
+
@max_iter = max_iter
|
10
|
+
@init_factor = init_factor
|
11
|
+
@s_factor = s_factor
|
12
|
+
@l_factor = l_factor
|
13
|
+
@iter_mult = iter_mult
|
14
|
+
@max_no_impr = max_no_impr
|
15
|
+
@best = search(@max_iter, @bounds1, @init_factor, @s_factor, @l_factor, @iter_mult, @max_no_impr)
|
16
|
+
puts "Done. Best Solution: c=#{@best[:cost]}, v=#{@best[:vector].inspect}"
|
17
|
+
end
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
19
|
+
def objective_function(vector)
|
20
|
+
vector.inject(0) { |sum, x| sum + (x ** 2.0) }
|
21
|
+
end
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
|
23
|
+
def rand_in_bounds(min, max)
|
24
|
+
min + ((max-min) * rand())
|
25
|
+
end
|
25
26
|
|
26
|
-
|
27
|
-
|
28
|
-
|
27
|
+
def random_vector(minmax)
|
28
|
+
Array.new(minmax.size) do |i|
|
29
|
+
rand_in_bounds(minmax[i][0], minmax[i][1])
|
30
|
+
end
|
29
31
|
end
|
30
|
-
end
|
31
32
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
33
|
+
def take_step(minmax, current, step_size)
|
34
|
+
position = Array.new(current.size)
|
35
|
+
position.size.times do |i|
|
36
|
+
min = [minmax[i][0], current[i]-step_size].max
|
37
|
+
max = [minmax[i][1], current[i]+step_size].min
|
38
|
+
position[i] = rand_in_bounds(min, max)
|
39
|
+
end
|
40
|
+
position
|
38
41
|
end
|
39
|
-
position
|
40
|
-
end
|
41
42
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
def large_step_size(iter, step_size, s_factor, l_factor, iter_mult)
|
44
|
+
step_size * l_factor if iter > 0 and iter.modulo(iter_mult) == 0
|
45
|
+
step_size * s_factor
|
46
|
+
end
|
46
47
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
48
|
+
def take_steps(bounds, current, step_size, big_stepsize)
|
49
|
+
step, big_step = {}, {}
|
50
|
+
step[:vector] = take_step(bounds, current[:vector], step_size)
|
51
|
+
step[:cost] = objective_function(step[:vector])
|
52
|
+
big_step[:vector] = take_step(bounds, current[:vector], big_stepsize)
|
53
|
+
big_step[:cost] = objective_function(big_step[:vector])
|
54
|
+
return step, big_step
|
55
|
+
end
|
55
56
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
57
|
+
def search(max_iter, bounds, init_factor, s_factor, l_factor, iter_mult, max_no_impr)
|
58
|
+
step_size = (bounds[0][1]-bounds[0][0]) * init_factor
|
59
|
+
current, count = {}, 0
|
60
|
+
current[:vector] = random_vector(bounds)
|
61
|
+
current[:cost] = objective_function(current[:vector])
|
62
|
+
max_iter.times do |iter|
|
63
|
+
big_stepsize = large_step_size(iter, step_size, s_factor, l_factor, iter_mult)
|
64
|
+
step, big_step = take_steps(bounds, current, step_size, big_stepsize)
|
65
|
+
if step[:cost] <= current[:cost] or big_step[:cost] <= current[:cost]
|
66
|
+
if big_step[:cost] <= step[:cost]
|
67
|
+
step_size, current = big_stepsize, big_step
|
68
|
+
else
|
69
|
+
current = step
|
70
|
+
end
|
71
|
+
count = 0
|
67
72
|
else
|
68
|
-
|
73
|
+
count += 1
|
74
|
+
count, step_size = 0, (step_size/s_factor) if count >= max_no_impr
|
69
75
|
end
|
70
|
-
|
71
|
-
else
|
72
|
-
count += 1
|
73
|
-
count, step_size = 0, (step_size/s_factor) if count >= max_no_impr
|
76
|
+
puts " > iteration #{(iter+1)}, best=#{current[:cost]}"
|
74
77
|
end
|
75
|
-
|
78
|
+
current
|
76
79
|
end
|
77
|
-
|
80
|
+
|
78
81
|
end
|
79
82
|
|
80
83
|
end
|
81
|
-
|
82
84
|
end
|
@@ -1,107 +1,109 @@
|
|
1
|
-
module
|
1
|
+
module Algorithmically
|
2
|
+
module Stochastic
|
2
3
|
|
3
|
-
|
4
|
+
class GuidedLocalSearch
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
def euc_2d(c1, c2)
|
18
|
-
Math.sqrt((c1[0] - c2[0])**2.0 + (c1[1] - c2[1])**2.0).round
|
19
|
-
end
|
6
|
+
def initialize(max_iterations, berlin52, max_no_improv, lambda)
|
7
|
+
@berlin52 = berlin52
|
8
|
+
@max_iterations = max_iterations
|
9
|
+
@max_no_improv = max_no_improv
|
10
|
+
alpha = 0.3
|
11
|
+
local_search_optima = 12000.0
|
12
|
+
lambda = alpha * (local_search_optima/berlin52.size.to_f)
|
13
|
+
@lambda = lambda
|
14
|
+
best = search(@max_iterations, @berlin52, @max_no_improv, lambda)
|
15
|
+
puts "Done. Best Solution: c=#{best[:cost]}, v=#{best[:vector].inspect}"
|
16
|
+
end
|
20
17
|
|
21
|
-
|
22
|
-
|
23
|
-
perm.each_index do |i|
|
24
|
-
r = rand(perm.size-i) + i
|
25
|
-
perm[r], perm[i] = perm[i], perm[r]
|
18
|
+
def euc_2d(c1, c2)
|
19
|
+
Math.sqrt((c1[0] - c2[0])**2.0 + (c1[1] - c2[1])**2.0).round
|
26
20
|
end
|
27
|
-
perm
|
28
|
-
end
|
29
21
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
perm[c1...c2] = perm[c1...c2].reverse
|
39
|
-
perm
|
40
|
-
end
|
22
|
+
def random_permutation(cities)
|
23
|
+
perm = Array.new(cities.size) { |i| i }
|
24
|
+
perm.each_index do |i|
|
25
|
+
r = rand(perm.size-i) + i
|
26
|
+
perm[r], perm[i] = perm[i], perm[r]
|
27
|
+
end
|
28
|
+
perm
|
29
|
+
end
|
41
30
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
31
|
+
def stochastic_two_opt(permutation)
|
32
|
+
perm = Array.new(permutation)
|
33
|
+
c1, c2 = rand(perm.size), rand(perm.size)
|
34
|
+
exclude = [c1]
|
35
|
+
exclude << ((c1==0) ? perm.size-1 : c1-1)
|
36
|
+
exclude << ((c1==perm.size-1) ? 0 : c1+1)
|
37
|
+
c2 = rand(perm.size) while exclude.include?(c2)
|
46
38
|
c1, c2 = c2, c1 if c2 < c1
|
47
|
-
|
48
|
-
|
49
|
-
augmented += d + (lambda * (penalties[c1][c2]))
|
39
|
+
perm[c1...c2] = perm[c1...c2].reverse
|
40
|
+
perm
|
50
41
|
end
|
51
|
-
[distance, augmented]
|
52
|
-
end
|
53
42
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
43
|
+
def augmented_cost(permutation, penalties, cities, lambda)
|
44
|
+
distance, augmented = 0, 0
|
45
|
+
permutation.each_with_index do |c1, i|
|
46
|
+
c2 = (i==permutation.size-1) ? permutation[0] : permutation[i+1]
|
47
|
+
c1, c2 = c2, c1 if c2 < c1
|
48
|
+
d = euc_2d(cities[c1], cities[c2])
|
49
|
+
distance += d
|
50
|
+
augmented += d + (lambda * (penalties[c1][c2]))
|
51
|
+
end
|
52
|
+
[distance, augmented]
|
53
|
+
end
|
58
54
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
candidate = {:vector => stochastic_two_opt(current[:vector])}
|
64
|
-
cost(candidate, penalties, cities, lambda)
|
65
|
-
count = (candidate[:aug_cost] < current[:aug_cost]) ? 0 : count+1
|
66
|
-
current = candidate if candidate[:aug_cost] < current[:aug_cost]
|
67
|
-
end until count >= max_no_improv
|
68
|
-
current
|
69
|
-
end
|
55
|
+
def cost(cand, penalties, cities, lambda)
|
56
|
+
cost, acost = augmented_cost(cand[:vector], penalties, cities, lambda)
|
57
|
+
cand[:cost], cand[:aug_cost] = cost, acost
|
58
|
+
end
|
70
59
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
60
|
+
def local_search(current, cities, penalties, max_no_improv, lambda)
|
61
|
+
cost(current, penalties, cities, lambda)
|
62
|
+
count = 0
|
63
|
+
begin
|
64
|
+
candidate = {:vector => stochastic_two_opt(current[:vector])}
|
65
|
+
cost(candidate, penalties, cities, lambda)
|
66
|
+
count = (candidate[:aug_cost] < current[:aug_cost]) ? 0 : count+1
|
67
|
+
current = candidate if candidate[:aug_cost] < current[:aug_cost]
|
68
|
+
end until count >= max_no_improv
|
69
|
+
current
|
77
70
|
end
|
78
|
-
utilities
|
79
|
-
end
|
80
71
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
72
|
+
def calculate_feature_utilities(penal, cities, permutation)
|
73
|
+
utilities = Array.new(permutation.size, 0)
|
74
|
+
permutation.each_with_index do |c1, i|
|
75
|
+
c2 = (i==permutation.size-1) ? permutation[0] : permutation[i+1]
|
76
|
+
c1, c2 = c2, c1 if c2 < c1
|
77
|
+
utilities[i] = euc_2d(cities[c1], cities[c2]) / (1.0 + penal[c1][c2])
|
78
|
+
end
|
79
|
+
utilities
|
80
|
+
end
|
81
|
+
|
82
|
+
def update_penalties!(penalties, cities, permutation, utilities)
|
83
|
+
max = utilities.max()
|
84
|
+
permutation.each_with_index do |c1, i|
|
85
|
+
c2 = (i==permutation.size-1) ? permutation[0] : permutation[i+1]
|
86
|
+
c1, c2 = c2, c1 if c2 < c1
|
87
|
+
penalties[c1][c2] += 1 if utilities[i] == max
|
88
|
+
end
|
89
|
+
penalties
|
87
90
|
end
|
88
|
-
penalties
|
89
|
-
end
|
90
91
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
92
|
+
def search(max_iterations, cities, max_no_improv, lambda)
|
93
|
+
current = {:vector => random_permutation(cities)}
|
94
|
+
best = nil
|
95
|
+
penalties = Array.new(cities.size) { Array.new(cities.size, 0) }
|
96
|
+
max_iterations.times do |iter|
|
97
|
+
current=local_search(current, cities, penalties, max_no_improv, lambda)
|
98
|
+
utilities=calculate_feature_utilities(penalties, cities, current[:vector])
|
99
|
+
update_penalties!(penalties, cities, current[:vector], utilities)
|
100
|
+
best = current if best.nil? or current[:cost] < best[:cost]
|
101
|
+
puts " > iter=#{(iter+1)}, best=#{best[:cost]}, aug=#{best[:aug_cost]}"
|
102
|
+
end
|
103
|
+
best
|
101
104
|
end
|
102
|
-
|
105
|
+
|
103
106
|
end
|
104
107
|
|
105
108
|
end
|
106
|
-
|
107
109
|
end
|
@@ -1,42 +1,44 @@
|
|
1
|
-
module
|
1
|
+
module Algorithmically
|
2
|
+
module Stochastic
|
2
3
|
|
3
|
-
|
4
|
+
class HillClimbing
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
def initialize(max_iterations, num_bits)
|
7
|
+
best = search(max_iterations, num_bits)
|
8
|
+
puts "Done. Best Solution: c=#{best[:cost]}, v=#{best[:vector].join}"
|
9
|
+
end
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
11
|
+
def onemax(vector)
|
12
|
+
vector.inject(0.0) { |sum, v| sum + ((v=="1") ? 1 : 0) }
|
13
|
+
end
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
def random_bitstring(num_bits)
|
16
|
+
Array.new(num_bits) { |i| (rand<0.5) ? "1" : "0" }
|
17
|
+
end
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
19
|
+
def random_neighbor(bitstring)
|
20
|
+
mutant = Array.new(bitstring)
|
21
|
+
pos = rand(bitstring.size)
|
22
|
+
mutant[pos] = (mutant[pos]=='1') ? '0' : '1'
|
23
|
+
mutant
|
24
|
+
end
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
26
|
+
def search(max_iterations, num_bits)
|
27
|
+
candidate = {}
|
28
|
+
candidate[:vector] = random_bitstring(num_bits)
|
29
|
+
candidate[:cost] = onemax(candidate[:vector])
|
30
|
+
max_iterations.times do |iter|
|
31
|
+
neighbor = {}
|
32
|
+
neighbor[:vector] = random_neighbor(candidate[:vector])
|
33
|
+
neighbor[:cost] = onemax(neighbor[:vector])
|
34
|
+
candidate = neighbor if neighbor[:cost] >= candidate[:cost]
|
35
|
+
puts " > iteration #{(iter+1)}, best=#{candidate[:cost]}"
|
36
|
+
break if candidate[:cost] == num_bits
|
37
|
+
end
|
38
|
+
candidate
|
36
39
|
end
|
37
|
-
|
40
|
+
|
38
41
|
end
|
39
42
|
|
40
43
|
end
|
41
|
-
|
42
44
|
end
|
@@ -1,37 +1,39 @@
|
|
1
|
-
module
|
1
|
+
module Algorithmically
|
2
|
+
module Stochastic
|
2
3
|
|
3
|
-
|
4
|
+
class RandomSearch
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
def initialize(size, max_iter)
|
7
|
+
problem_size = size
|
8
|
+
search_space = Array.new(problem_size) { |i| [-5, +5] }
|
9
|
+
maximum_iterations = max_iter
|
10
|
+
best_solution = self.search(search_space, maximum_iterations)
|
11
|
+
puts "Done. Best Solution: c = #{best_solution[:cost]}, v = #{best_solution[:vector].inspect}"
|
12
|
+
end
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
14
|
+
def objective_function(vector)
|
15
|
+
vector.inject(0) { |sum, x| sum + (x ** 2.0) }
|
16
|
+
end
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
18
|
+
def random_vector(minmax)
|
19
|
+
Array.new(minmax.size) do |i|
|
20
|
+
minmax[i][0] + ((minmax[i][1]) - minmax[i][0] * rand())
|
21
|
+
end
|
20
22
|
end
|
21
|
-
end
|
22
23
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
24
|
+
def search(search_space, maximum_iterations)
|
25
|
+
best_solution = nil
|
26
|
+
maximum_iterations.times do |iterate|
|
27
|
+
search_candidate = {}
|
28
|
+
search_candidate[:vector] = random_vector(search_space)
|
29
|
+
search_candidate[:cost] = objective_function(search_candidate[:vector])
|
30
|
+
best_solution = search_candidate if best_solution.nil? or search_candidate[:cost] < best_solution[:cost]
|
31
|
+
puts " > iteration = #{(iterate + 1)}, best = #{best_solution[:cost]}"
|
32
|
+
end
|
33
|
+
best_solution
|
31
34
|
end
|
32
|
-
|
35
|
+
|
33
36
|
end
|
34
37
|
|
35
38
|
end
|
36
|
-
|
37
39
|
end
|