Initial version of VNS algorithm
This commit is contained in:
parent
8732dac0a3
commit
405e698a6f
2
Gemfile
2
Gemfile
@ -65,3 +65,5 @@ end
|
|||||||
|
|
||||||
gem "money"
|
gem "money"
|
||||||
gem 'acts-as-taggable-on'
|
gem 'acts-as-taggable-on'
|
||||||
|
|
||||||
|
gem "vns", path: "../vns"
|
@ -1,3 +1,9 @@
|
|||||||
|
PATH
|
||||||
|
remote: ../vns
|
||||||
|
specs:
|
||||||
|
vns (0.4.0.pre.rc.1)
|
||||||
|
activesupport
|
||||||
|
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
@ -276,6 +282,7 @@ DEPENDENCIES
|
|||||||
stimulus-rails
|
stimulus-rails
|
||||||
turbo-rails
|
turbo-rails
|
||||||
tzinfo-data
|
tzinfo-data
|
||||||
|
vns!
|
||||||
web-console
|
web-console
|
||||||
|
|
||||||
RUBY VERSION
|
RUBY VERSION
|
||||||
|
44
app/services/tables/distribution.rb
Normal file
44
app/services/tables/distribution.rb
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
module Tables
|
||||||
|
class Distribution
|
||||||
|
attr_reader :tables
|
||||||
|
|
||||||
|
def initialize(min_per_table:, max_per_table:)
|
||||||
|
@min_per_table = min_per_table
|
||||||
|
@max_per_table = max_per_table
|
||||||
|
end
|
||||||
|
|
||||||
|
def random_distribution(people)
|
||||||
|
@tables = []
|
||||||
|
|
||||||
|
@tables << people.slice!(0..rand(@min_per_table..@max_per_table)) while people.any?
|
||||||
|
end
|
||||||
|
|
||||||
|
def discomfort
|
||||||
|
@tables.map do |table|
|
||||||
|
local_discomfort(table)
|
||||||
|
end.sum
|
||||||
|
end
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
"#{@tables.count} tables, discomfort: #{discomfort}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def pretty_print
|
||||||
|
@tables.map.with_index do |table, i|
|
||||||
|
"Table #{i + 1} (#{table.count} ppl): (#{local_discomfort(table)}) #{table.map(&:full_name).join(', ')}"
|
||||||
|
end.join("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def local_discomfort(table)
|
||||||
|
10 * (number_of_groups(table) - 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
def number_of_groups(table)
|
||||||
|
table.map do |person|
|
||||||
|
person.affinity_groups
|
||||||
|
end.flatten.uniq.count
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
28
app/services/tables/swap.rb
Normal file
28
app/services/tables/swap.rb
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
module Tables
|
||||||
|
class Swap
|
||||||
|
private attr_reader :initial_solution
|
||||||
|
def initialize(initial_solution)
|
||||||
|
@initial_solution = initial_solution
|
||||||
|
end
|
||||||
|
|
||||||
|
def each
|
||||||
|
@initial_solution.tables.combination(2) do |table_a, table_b|
|
||||||
|
table_a.product(table_b).each do |(person_a, person_b)|
|
||||||
|
table_a.delete(person_a)
|
||||||
|
table_b.delete(person_b)
|
||||||
|
|
||||||
|
table_a << person_b
|
||||||
|
table_b << person_a
|
||||||
|
|
||||||
|
yield(@initial_solution)
|
||||||
|
ensure
|
||||||
|
table_a.delete(person_b)
|
||||||
|
table_b.delete(person_a)
|
||||||
|
|
||||||
|
table_a << person_a
|
||||||
|
table_b << person_b
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
16
db/seeds.rb
16
db/seeds.rb
@ -1,12 +1,4 @@
|
|||||||
# This file should ensure the existence of records required to run the application in every environment (production,
|
NUMBER_OF_GUESTS = 50
|
||||||
# development, test). The code here should be idempotent so that it can be executed at any point in every environment.
|
|
||||||
# The data can then be loaded with the bin/rails db:seed command (or created alongside the database with db:setup).
|
|
||||||
#
|
|
||||||
# Example:
|
|
||||||
#
|
|
||||||
# ["Action", "Comedy", "Drama", "Horror"].each do |genre_name|
|
|
||||||
# MovieGenre.find_or_create_by!(name: genre_name)
|
|
||||||
# end
|
|
||||||
|
|
||||||
Expense.delete_all
|
Expense.delete_all
|
||||||
Guest.delete_all
|
Guest.delete_all
|
||||||
@ -28,10 +20,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')
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
|
|
||||||
=======
|
|
||||||
>>>>>>> 8fd0b7c (Modify seeds file to make sure every guest is part of a group)
|
|
||||||
samples = {
|
samples = {
|
||||||
close_family: 10,
|
close_family: 10,
|
||||||
family_1_group_a: 5,
|
family_1_group_a: 5,
|
||||||
@ -50,7 +38,7 @@ samples = {
|
|||||||
count.times { acc << affinity_group }
|
count.times { acc << affinity_group }
|
||||||
end
|
end
|
||||||
|
|
||||||
300.times do
|
NUMBER_OF_GUESTS.times do
|
||||||
guest = Guest.create!(first_name: Faker::Name.first_name,
|
guest = Guest.create!(first_name: Faker::Name.first_name,
|
||||||
last_name: Faker::Name.last_name,
|
last_name: Faker::Name.last_name,
|
||||||
email: Faker::Internet.email,
|
email: Faker::Internet.email,
|
||||||
|
16
lib/tasks/vns.rake
Normal file
16
lib/tasks/vns.rake
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
namespace :vns do
|
||||||
|
task distribute_tables: :environment do
|
||||||
|
engine = VNS::Engine.new
|
||||||
|
|
||||||
|
engine.add_perturbation(Tables::Swap)
|
||||||
|
|
||||||
|
initial_solution = Tables::Distribution.new(min_per_table: 8, max_per_table: 10)
|
||||||
|
initial_solution.random_distribution(Guest.all.shuffle)
|
||||||
|
|
||||||
|
engine.initial_solution = initial_solution
|
||||||
|
|
||||||
|
engine.target_function(&:discomfort)
|
||||||
|
|
||||||
|
best_solution = engine.run
|
||||||
|
end
|
||||||
|
end
|
Loading…
x
Reference in New Issue
Block a user