diff --git a/examples/tsp.rb b/examples/tsp.rb index 1a22729..a94630c 100644 --- a/examples/tsp.rb +++ b/examples/tsp.rb @@ -10,23 +10,40 @@ require 'alns/select/roulette_wheel' require 'alns/stop/max_iterations' require 'alns/stop/max_runtime' +require 'alns/random/random' module TSP def self.solve write_dot = false + seed = 1234 + secure_random = false OptionParser.new do |parser| parser.banner = 'Usage: tsp.rb [options]' - parser.on('-d', '--dot', 'write a dot file with result') do |v| + parser.on('-d', '--dot', 'write a dot file with the result') do write_dot = true end + + parser.on('-sSEED', '--seed=SEED', Integer, 'specify seed') do |v| + seed = v + end + + parser.on('-r', '--secure-random', 'use secure random') do + secure_random = true + end end.parse! nodes = make_nodes(COORDS) dists = make_dists(COORDS) - solver = ALNS::Solver.new(Random.new(1234)) + rnd = if secure_random + ALNS::Random::Random.new + else + Random.new(seed) + end + + solver = ALNS::Solver.new(rnd) init_sol = TspState.new(nodes, {}, dists) init_sol = greedy_repair(init_sol, solver.rnd) @@ -256,17 +273,21 @@ def initialize(nodes, edges, dists) def initialize_copy(original) super - @nodes = original.nodes + # @nodes is frozen @edges = original.edges.dup - @dists = original.dists + # @dists is frozen + @objective = nil end def objective - total_dist = 0.0 - @edges.each do |from, to| - total_dist += @dists[from][to] + if @objective.nil? + total_dist = 0.0 + @edges.each do |from, to| + total_dist += @dists[from][to] + end + @objective = total_dist end - total_dist + @objective end end diff --git a/lib/alns/math.rb b/lib/alns/math.rb index f6d5492..e8d07eb 100644 --- a/lib/alns/math.rb +++ b/lib/alns/math.rb @@ -1,12 +1,6 @@ # frozen_string_literal: true module ALNS - EPSILON = 1e-9 - - def self.close?(a, b, epsilon = EPSILON) # rubocop:disable Naming/MethodParameterName - (a - b).abs <= epsilon - end - def self.weighted_random_index(rnd, weights) raise 'Invalid weights: Array is empty' if weights.empty? return 0 if weights.size == 1 @@ -17,9 +11,10 @@ def self.weighted_random_index(rnd, weights) weights.each_with_index do |weight, index| adjusted_value -= weight - return index if adjusted_value <= 0 || close?(adjusted_value, 0) + return index if adjusted_value <= 0 end - raise "Arithmetic error: sum=#{total_sum}, adjusted_value=#{adjusted_value}, weights=#{weights.inspect}" + # we will only be here when errors accumulate + weights.length - 1 end end diff --git a/lib/alns/random/random.rb b/lib/alns/random/random.rb new file mode 100644 index 0000000..cb95301 --- /dev/null +++ b/lib/alns/random/random.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require 'securerandom' + +module ALNS + module Random + class Random + def rand(max = nil) + if max.nil? + SecureRandom.random_number + else + SecureRandom.random_number(max) + end + end + end + end +end diff --git a/test/math_test.rb b/test/math_test.rb index 84a87dc..8eecb16 100644 --- a/test/math_test.rb +++ b/test/math_test.rb @@ -9,11 +9,6 @@ def setup @rnd = Random.new(123) end - def test_close - assert ALNS.close?(1, 1.0000000001) - assert !ALNS.close?(1, 1.0000001) - end - def test_weighted_random_index tests = [ { weights: [1.0], want: 0 }, # one element