From 1d28cfc527dd5311494c2a9b9cfe77766d1195f4 Mon Sep 17 00:00:00 2001 From: Manuel Bustillo Date: Fri, 2 Aug 2024 17:37:50 +0200 Subject: [PATCH 1/4] Refactor how the local discomfort is stored --- app/services/tables/distribution.rb | 12 ++---------- app/services/tables/table.rb | 11 ++++++++--- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/app/services/tables/distribution.rb b/app/services/tables/distribution.rb index 33cb130..4965346 100644 --- a/app/services/tables/distribution.rb +++ b/app/services/tables/distribution.rb @@ -17,9 +17,7 @@ module Tables end def discomfort - @tables.map do |table| - local_discomfort(table) - end.sum + @tables.map(&:discomfort).sum end def inspect @@ -28,7 +26,7 @@ module Tables def pretty_print @tables.map.with_index do |table, i| - "Table #{i + 1} (#{table.count} ppl): (#{local_discomfort(table)}) #{table.map(&:full_name).join(', ')}" + "Table #{i + 1} (#{table.count} ppl): (#{table.discomfort}) #{table.map(&:full_name).join(', ')}" end.join("\n") end @@ -55,11 +53,5 @@ module Tables arrangement.update!(discomfort:) end end - - private - - def local_discomfort(table) - table.discomfort ||= DiscomfortCalculator.new(table).calculate - end end end diff --git a/app/services/tables/table.rb b/app/services/tables/table.rb index 1dde7f6..253c200 100644 --- a/app/services/tables/table.rb +++ b/app/services/tables/table.rb @@ -1,15 +1,20 @@ module Tables class Table < Array - attr_accessor :discomfort + attr_writer :discomfort + def initialize(*args) super reset end def reset - original_discomfort = discomfort + original_discomfort = @discomfort @discomfort = nil original_discomfort end + + def discomfort + @discomfort ||= DiscomfortCalculator.new(self).calculate + end end -end \ No newline at end of file +end -- 2.47.1 From 60b96ab82dd7987ee7afddeaf559b6b4d37f0364 Mon Sep 17 00:00:00 2001 From: Manuel Bustillo Date: Fri, 2 Aug 2024 18:17:22 +0200 Subject: [PATCH 2/4] Assign an ID to every table and use a hash to store them --- app/services/tables/distribution.rb | 28 +++++++++++++++++++--------- app/services/tables/table.rb | 2 ++ spec/services/tables/swap_spec.rb | 14 +++++++------- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/app/services/tables/distribution.rb b/app/services/tables/distribution.rb index 4965346..5fe0911 100644 --- a/app/services/tables/distribution.rb +++ b/app/services/tables/distribution.rb @@ -2,37 +2,47 @@ require_relative '../../extensions/tree_node_extension' module Tables class Distribution - attr_accessor :tables - def initialize(min_per_table:, max_per_table:) @min_per_table = min_per_table @max_per_table = max_per_table - @tables = [] + @tables = {} + end + + def tables + @tables.values.freeze + end + + def <<(array) + Table.new(array).tap do |table| + @tables[table.id] = table + end end def random_distribution(people) - @tables = [] + @tables = {} - @tables << Table.new(people.slice!(0..rand(@min_per_table..@max_per_table))) while people.any? + self << people.slice!(0..rand(@min_per_table..@max_per_table)) while people.any? end def discomfort - @tables.map(&:discomfort).sum + tables.map(&:discomfort).sum end def inspect - "#{@tables.count} tables, discomfort: #{discomfort}" + "#{tables.count} tables, discomfort: #{discomfort}" end def pretty_print - @tables.map.with_index do |table, i| + tables.map.with_index do |table, i| "Table #{i + 1} (#{table.count} ppl): (#{table.discomfort}) #{table.map(&:full_name).join(', ')}" end.join("\n") end def deep_dup self.class.new(min_per_table: @min_per_table, max_per_table: @max_per_table).tap do |new_distribution| - new_distribution.tables = @tables.map(&:dup) + tables.each do |table| + new_distribution << table.dup + end end end diff --git a/app/services/tables/table.rb b/app/services/tables/table.rb index 253c200..0c9dc2b 100644 --- a/app/services/tables/table.rb +++ b/app/services/tables/table.rb @@ -1,10 +1,12 @@ module Tables class Table < Array attr_writer :discomfort + attr_reader :id def initialize(*args) super reset + @id = SecureRandom.uuid end def reset diff --git a/spec/services/tables/swap_spec.rb b/spec/services/tables/swap_spec.rb index fff181f..b31779f 100644 --- a/spec/services/tables/swap_spec.rb +++ b/spec/services/tables/swap_spec.rb @@ -14,8 +14,8 @@ module Tables context 'when there are two tables with two people each' do let(:initial_solution) do Distribution.new(min_per_table: 2, max_per_table: 2).tap do |distribution| - distribution.tables << %i[a b].to_table - distribution.tables << %i[c d].to_table + distribution << %i[a b] + distribution << %i[c d] end end @@ -32,8 +32,8 @@ module Tables context 'when there are two tables with three people each' do let(:initial_solution) do Distribution.new(min_per_table: 3, max_per_table: 3).tap do |distribution| - distribution.tables << %i[a b c].to_table - distribution.tables << %i[d e f].to_table + distribution << %i[a b c] + distribution << %i[d e f] end end @@ -55,9 +55,9 @@ module Tables context 'when there are three tables with two people each' do let(:initial_solution) do Distribution.new(min_per_table: 2, max_per_table: 2).tap do |distribution| - distribution.tables << %i[a b].to_table - distribution.tables << %i[c d].to_table - distribution.tables << %i[e f].to_table + distribution << %i[a b] + distribution << %i[c d] + distribution << %i[e f] end end -- 2.47.1 From c2feb229d28c6ef63e5e83f1c423c5476bbc12fb Mon Sep 17 00:00:00 2001 From: Manuel Bustillo Date: Fri, 2 Aug 2024 18:42:24 +0200 Subject: [PATCH 3/4] Use immutable data structures to calculate variations --- app/services/tables/distribution.rb | 19 +++++++++++++++++++ app/services/tables/swap.rb | 26 ++++++++++++-------------- app/services/tables/table.rb | 6 ++++++ app/services/vns/engine.rb | 2 +- db/seeds.rb | 3 +-- 5 files changed, 39 insertions(+), 17 deletions(-) diff --git a/app/services/tables/distribution.rb b/app/services/tables/distribution.rb index 5fe0911..9b5f4ba 100644 --- a/app/services/tables/distribution.rb +++ b/app/services/tables/distribution.rb @@ -12,6 +12,22 @@ module Tables @tables.values.freeze end + def deep_freeze + @tables.each_value(&:freeze) + @tables.freeze + freeze + end + + def replace(table) + @tables[table.id] = table + end + + def dup + super.tap do |new_distribution| + new_distribution.instance_variable_set(:@tables, @tables.dup) + end + end + def <<(array) Table.new(array).tap do |table| @tables[table.id] = table @@ -22,6 +38,9 @@ module Tables @tables = {} self << people.slice!(0..rand(@min_per_table..@max_per_table)) while people.any? + + @tables.freeze + freeze end def discomfort diff --git a/app/services/tables/swap.rb b/app/services/tables/swap.rb index 631b49b..d77e2a4 100644 --- a/app/services/tables/swap.rb +++ b/app/services/tables/swap.rb @@ -8,25 +8,23 @@ module Tables def each @initial_solution.tables.combination(2) do |table_a, table_b| table_a.product(table_b).each do |(person_a, person_b)| - original_discomfort_a = table_a.reset - original_discomfort_b = table_b.reset + new_solution = @initial_solution.dup - table_a.delete(person_a) - table_b.delete(person_b) + new_table_a = table_a.dup + new_table_b = table_b.dup - table_a << person_b - table_b << person_a + new_solution.replace(new_table_a) + new_solution.replace(new_table_b) - yield(@initial_solution) - ensure - table_a.delete(person_b) - table_b.delete(person_a) + new_table_a.delete(person_a) + new_table_b.delete(person_b) - table_a << person_a - table_b << person_b + new_table_a << person_b + new_table_b << person_a - table_a.discomfort = original_discomfort_a - table_b.discomfort = original_discomfort_b + new_solution.freeze + + yield(new_solution) end end end diff --git a/app/services/tables/table.rb b/app/services/tables/table.rb index 0c9dc2b..54fd8cc 100644 --- a/app/services/tables/table.rb +++ b/app/services/tables/table.rb @@ -18,5 +18,11 @@ module Tables def discomfort @discomfort ||= DiscomfortCalculator.new(self).calculate end + + def dup + super.tap do |new_table| + new_table.discomfort = nil + end + end end end diff --git a/app/services/vns/engine.rb b/app/services/vns/engine.rb index 5c260aa..d5474b9 100644 --- a/app/services/vns/engine.rb +++ b/app/services/vns/engine.rb @@ -26,7 +26,7 @@ module VNS optimize(perturbation.new(@best_solution)) end - @best_solution + @best_solution end private diff --git a/db/seeds.rb b/db/seeds.rb index 162a80f..eb08d7a 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,4 +1,4 @@ -NUMBER_OF_GUESTS = 50 +NUMBER_OF_GUESTS = ENV['SEED_GUEST_COUNT'].presence.to_i || 50 TablesArrangement.delete_all Expense.delete_all @@ -21,7 +21,6 @@ Expense.create!(name: 'Transportation', amount: 3000, pricing_type: 'fixed') Expense.create!(name: 'Invitations', amount: 200, pricing_type: 'fixed') Expense.create!(name: 'Cake', amount: 500, pricing_type: 'fixed') - samples = { close_family_a: 10, close_family_b: 10, -- 2.47.1 From 5f50c1fe111bca83891934295abe79db5550809d Mon Sep 17 00:00:00 2001 From: Manuel Bustillo Date: Fri, 2 Aug 2024 18:45:28 +0200 Subject: [PATCH 4/4] Fix seedfile --- db/seeds.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/seeds.rb b/db/seeds.rb index eb08d7a..194fa88 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,4 +1,4 @@ -NUMBER_OF_GUESTS = ENV['SEED_GUEST_COUNT'].presence.to_i || 50 +NUMBER_OF_GUESTS = (ENV['SEED_GUEST_COUNT'] || 50).to_i TablesArrangement.delete_all Expense.delete_all -- 2.47.1