shadchan 0.0.2 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,22 @@
1
+ h1. Shadchan
2
+
3
+ This gem provides a class for solving the "Stable Marriage problem":http://en.wikipedia.org/wiki/Stable_marriage_problem and "Stable Roommates problem":http://en.wikipedia.org/wiki/Stable_roommates_problem.
4
+
5
+
6
+ h2. Installation
7
+ <pre>gem install shadchan</pre>
8
+
9
+ h2. Usage
10
+
11
+ <pre>require 'shadchan'
12
+ # stable marriage example
13
+ shadchan = Shadchan::Shadchan.new [0, 2, 1], [2, 0, 1], [0, 2, 1], [1, 0, 2], [0, 2, 1], [0, 1, 2]
14
+ shadchan.match #=> [[0, 2], [1, 0], [2, 1]]
15
+ shadchan.match_men #=> [1, 2, 0]
16
+ shadchan.match_women #=> [2, 0, 1]
17
+
18
+ # stable roommates example
19
+ roomie = Shadchan::Roomie.new [2,3,1,5,4],[5,4,3,0,2],[1,3,4,0,5],[4,1,2,5,0],[2,0,1,3,5],[4,0,2,3,1]
20
+ roomie.match #=> [5,4,3,1,2,0]
21
+ </pre>
22
+
@@ -1,4 +1,6 @@
1
1
  module Shadchan
2
2
  libdir = File.join(File.dirname(File.expand_path(__FILE__)), self.name.downcase, '')
3
- require libdir + 'shadchan.rb' # Your code goes here...
3
+ require libdir + 'not_solvable.rb'
4
+ require libdir + 'shadchan.rb'
5
+ require libdir + 'roomie.rb'
4
6
  end
@@ -0,0 +1,4 @@
1
+ module Shadchan
2
+ class NotSolvable < StandardError
3
+ end
4
+ end
@@ -0,0 +1,158 @@
1
+ module Shadchan
2
+ =begin rdoc
3
+ Solves Stable Roommates problem. From n 'roommates', who all have a preference about each other,
4
+ it creates such pairing that no roommate would be happier with other AND the other would be
5
+ happier with the roommate.
6
+
7
+ http://en.wikipedia.org/wiki/Stable_roommates_problem
8
+
9
+ Problem is not always solvable. In that case it raises Shadchan::NotSolvable exception.
10
+ =end
11
+ class Roomie
12
+ =begin rdoc
13
+ Initialize problem. Takes preference lists in form [most_prefered_index, second_most_prefered_index, ...].
14
+ Example:
15
+ Shachdan::Roomie.new [2,3,1,5,4],[5,4,3,0,2],[1,3,4,0,5],[4,1,2,5,0],[2,0,1,3,5],[4,0,2,3,1]
16
+ or
17
+ a = [[2,3,1,5,4],[5,4,3,0,2],[1,3,4,0,5],[4,1,2,5,0],[2,0,1,3,5],[4,0,2,3,1]]
18
+ Shachdan::Roomie.new *a
19
+
20
+ Will raise Shachdan::NotSolvable if solution cannot be found.
21
+ =end
22
+ def initialize *args
23
+ check_input args
24
+ @preferences = args.map(&:dup)
25
+ @size = @preferences.size
26
+ @match = []
27
+ stable_matching
28
+ end
29
+ =begin rdoc
30
+ Returns array of matches. Numbering corresponds to that used to initialize this instance.
31
+ shachdan = Shachdan::Roomie.new *a
32
+ shachdan.match #=> [5,4,3,1,2,0]
33
+
34
+ In this example, roommate 0 was matched with roommate 5, roommate 1 with roommate 4 etc.
35
+ =end
36
+ def match
37
+ @reversed_matches.map(&:dup)
38
+ end
39
+
40
+ private
41
+ def stable_matching
42
+ propose
43
+ return true if is_matching?
44
+ reverse_matches
45
+ # puts @matches.inspect
46
+ # puts @reversed_matches.inspect
47
+ # puts @modified_preferences.inspect
48
+ rotate_solutions
49
+ end
50
+
51
+ def reverse_matches
52
+ @reversed_matches = Array.new(@size)
53
+ @matches.each_with_index do |e, i|
54
+ @reversed_matches[e] = i
55
+ end
56
+ end
57
+
58
+
59
+ def rotate_solutions
60
+ until is_matching?
61
+ rot = generate_rotation
62
+ #puts rot.inspect
63
+ apply_rotation(rot)
64
+ # puts @reversed_matches.inspect
65
+ end
66
+ end
67
+
68
+ def apply_rotation(rot)
69
+ rot.each do |r|
70
+ @reversed_matches[r.first] = r.last
71
+ @matches[r.last] = r.first
72
+ end
73
+ end
74
+
75
+ def generate_rotation
76
+ rotation = []
77
+ @reversed_matches.each_with_index do |e, i|
78
+ unless @reversed_matches[e] == i then
79
+ current = i
80
+ begin
81
+ # get second favorite
82
+ second_favorite = @modified_preferences[current].shift
83
+ rotation << [current, second_favorite]
84
+ current = @reversed_matches.find_index(second_favorite)
85
+ #puts rotation.inspect
86
+ # puts @modified_preferences.inspect
87
+ # puts "sf = #{second_favorite}"
88
+ # puts "e = #{e}"
89
+ # puts "i = #{i}"
90
+ # puts "current = #{current}"
91
+ end while second_favorite != e
92
+ return rotation
93
+ end
94
+ end
95
+ rotation
96
+ end
97
+
98
+ def propose
99
+ @matches = Array.new(@size)
100
+ @modified_preferences = @preferences.map(&:dup)
101
+
102
+ while r = free_roommate
103
+ desired = @modified_preferences[r].shift
104
+ raise NotSolvable, 'Problem has no solution' if desired.nil?
105
+ if @matches[desired] then
106
+ if @preferences[desired].find_index(r) < @preferences[desired].find_index(@matches[desired]) then
107
+ @matches[desired] = r
108
+ end
109
+ else
110
+ @matches[desired] = r
111
+ next
112
+ end
113
+ end
114
+ end
115
+
116
+ def is_matching?
117
+ @matches.each_with_index do |e, i|
118
+ return false unless @matches[e] == i
119
+ end
120
+ true
121
+ end
122
+
123
+ def free_roommate
124
+ @size.times do |i|
125
+ not_proposed = !@matches.include?(i)
126
+ if not_proposed then
127
+ return i
128
+ end
129
+ end
130
+ return nil
131
+ end
132
+
133
+ def check_input args
134
+ raise ArgumentError, 'No arguments given' if args.empty?
135
+ size = args.first.size
136
+
137
+ unless args.all?{|w| w.size == size} then
138
+ raise ArgumentError, 'Incorrectly specified input - inconsistent preference list size. Check your input.'
139
+ end
140
+
141
+ unless args.size == size+1 then
142
+ raise ArgumentError, 'Incorrectly specified input - preference lists too small or big. Check your input.'
143
+ end
144
+
145
+ unless args.size.even?
146
+ raise ArgumentError, 'Not valid problem - number of roommates must be even.'
147
+ end
148
+
149
+ args.each_with_index do |e, i|
150
+ size.times do |j|
151
+ unless i==j then
152
+ raise ArgumentError, 'Error in input - each roommate must state preference for all other except himself' unless e.include? j
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
@@ -1,3 +1,3 @@
1
1
  module Shadchan
2
- VERSION = "0.0.2"
2
+ VERSION = "0.1.0"
3
3
  end
metadata CHANGED
@@ -5,9 +5,9 @@ version: !ruby/object:Gem::Version
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
+ - 1
8
9
  - 0
9
- - 2
10
- version: 0.0.2
10
+ version: 0.1.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Vojtech Salbaba
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-01-15 00:00:00 +01:00
18
+ date: 2011-01-16 00:00:00 +01:00
19
19
  default_executable:
20
20
  dependencies: []
21
21
 
@@ -31,8 +31,11 @@ extra_rdoc_files: []
31
31
  files:
32
32
  - .gitignore
33
33
  - Gemfile
34
+ - README.textile
34
35
  - Rakefile
35
36
  - lib/shadchan.rb
37
+ - lib/shadchan/not_solvable.rb
38
+ - lib/shadchan/roomie.rb
36
39
  - lib/shadchan/shadchan.rb
37
40
  - lib/shadchan/version.rb
38
41
  - shadchan.gemspec