frozen-swap #24

Open
bustikiller wants to merge 5 commits from frozen-swap into main
6 changed files with 76 additions and 45 deletions

View File

@ -2,39 +2,66 @@ require_relative '../../extensions/tree_node_extension'
module Tables module Tables
class Distribution class Distribution
attr_accessor :tables
def initialize(min_per_table:, max_per_table:) def initialize(min_per_table:, max_per_table:)
@min_per_table = min_per_table @min_per_table = min_per_table
@max_per_table = max_per_table @max_per_table = max_per_table
@tables = [] @tables = {}
end
def 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
end
end end
def random_distribution(people) 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?
@tables.freeze
freeze
end end
def discomfort def discomfort
@tables.map do |table| tables.map(&:discomfort).sum
local_discomfort(table)
end.sum
end end
def inspect def inspect
"#{@tables.count} tables, discomfort: #{discomfort}" "#{tables.count} tables, discomfort: #{discomfort}"
end end
def pretty_print def pretty_print
@tables.map.with_index do |table, i| 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.join("\n")
end end
def deep_dup def deep_dup
self.class.new(min_per_table: @min_per_table, max_per_table: @max_per_table).tap do |new_distribution| 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
end end
@ -55,11 +82,5 @@ module Tables
arrangement.update!(discomfort:) arrangement.update!(discomfort:)
end end
end end
private
def local_discomfort(table)
table.discomfort ||= DiscomfortCalculator.new(table).calculate
end
end end
end end

View File

@ -8,25 +8,23 @@ module Tables
def each def each
@initial_solution.tables.combination(2) do |table_a, table_b| @initial_solution.tables.combination(2) do |table_a, table_b|
table_a.product(table_b).each do |(person_a, person_b)| table_a.product(table_b).each do |(person_a, person_b)|
original_discomfort_a = table_a.reset new_solution = @initial_solution.dup
original_discomfort_b = table_b.reset
table_a.delete(person_a) new_table_a = table_a.dup
table_b.delete(person_b) new_table_b = table_b.dup
table_a << person_b new_solution.replace(new_table_a)
table_b << person_a new_solution.replace(new_table_b)
yield(@initial_solution) new_table_a.delete(person_a)
ensure new_table_b.delete(person_b)
table_a.delete(person_b)
table_b.delete(person_a)
table_a << person_a new_table_a << person_b
table_b << person_b new_table_b << person_a
table_a.discomfort = original_discomfort_a new_solution.freeze
table_b.discomfort = original_discomfort_b
yield(new_solution)
end end
end end
end end

View File

@ -1,15 +1,28 @@
module Tables module Tables
class Table < Array class Table < Array
attr_accessor :discomfort attr_writer :discomfort
attr_reader :id
def initialize(*args) def initialize(*args)
super super
reset reset
@id = SecureRandom.uuid
end end
def reset def reset
original_discomfort = discomfort original_discomfort = @discomfort
@discomfort = nil @discomfort = nil
original_discomfort original_discomfort
end end
def discomfort
@discomfort ||= DiscomfortCalculator.new(self).calculate
end
def dup
super.tap do |new_table|
new_table.discomfort = nil
end
end
end end
end end

View File

@ -26,7 +26,7 @@ module VNS
optimize(perturbation.new(@best_solution)) optimize(perturbation.new(@best_solution))
end end
@best_solution @best_solution
end end
private private

View File

@ -1,4 +1,4 @@
NUMBER_OF_GUESTS = 50 NUMBER_OF_GUESTS = (ENV['SEED_GUEST_COUNT'] || 50).to_i
TablesArrangement.delete_all TablesArrangement.delete_all
Expense.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: 'Invitations', amount: 200, pricing_type: 'fixed')
Expense.create!(name: 'Cake', amount: 500, pricing_type: 'fixed') Expense.create!(name: 'Cake', amount: 500, pricing_type: 'fixed')
samples = { samples = {
close_family_a: 10, close_family_a: 10,
close_family_b: 10, close_family_b: 10,

View File

@ -14,8 +14,8 @@ module Tables
context 'when there are two tables with two people each' do context 'when there are two tables with two people each' do
let(:initial_solution) do let(:initial_solution) do
Distribution.new(min_per_table: 2, max_per_table: 2).tap do |distribution| Distribution.new(min_per_table: 2, max_per_table: 2).tap do |distribution|
distribution.tables << %i[a b].to_table distribution << %i[a b]
distribution.tables << %i[c d].to_table distribution << %i[c d]
end end
end end
@ -32,8 +32,8 @@ module Tables
context 'when there are two tables with three people each' do context 'when there are two tables with three people each' do
let(:initial_solution) do let(:initial_solution) do
Distribution.new(min_per_table: 3, max_per_table: 3).tap do |distribution| Distribution.new(min_per_table: 3, max_per_table: 3).tap do |distribution|
distribution.tables << %i[a b c].to_table distribution << %i[a b c]
distribution.tables << %i[d e f].to_table distribution << %i[d e f]
end end
end end
@ -55,9 +55,9 @@ module Tables
context 'when there are three tables with two people each' do context 'when there are three tables with two people each' do
let(:initial_solution) do let(:initial_solution) do
Distribution.new(min_per_table: 2, max_per_table: 2).tap do |distribution| Distribution.new(min_per_table: 2, max_per_table: 2).tap do |distribution|
distribution.tables << %i[a b].to_table distribution << %i[a b]
distribution.tables << %i[c d].to_table distribution << %i[c d]
distribution.tables << %i[e f].to_table distribution << %i[e f]
end end
end end