-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathwedding.py
327 lines (296 loc) · 10.5 KB
/
wedding.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
"""Group #16 --- Fichefet Pierrick --- Daubry Benjamin"""
#! /usr/bin/env python3
################################################################################
#
# Implementation of the wedding problem class
#
################################################################################
from search import *
import sys
import time
#################
# Problem class #
#################
class Wedding(Problem):
def __init__(self, init):
self.affinitiesTable = [] # The affinity matrix
self.numberOfPeople = 0
self.numberOfTable = 0
self.createTablesAssignment(init)
self.allPossibleActions = self.allPossibleActions() # Set of all possible actions
self.actionsAlreadyDone = [] # Set of all actions that have been done.
def successor(self, state):
successors = []
listOfSonsValues = []
count = 0
actionList = self.allPossibleActions
lastValue = state.value
lastAction = state.m
lastPeople = state.p
lastState = state.tables
for action in actionList:
peopleLine1 = action[0][0]
peopleCol1 = action[0][1]
peopleLine2 = action[1][0]
peopleCol2 = action[1][1]
swappedPeople1 = lastState[peopleLine1][peopleCol1]
swappedPeople2 = lastState[peopleLine2][peopleCol2]
#if(lastPeople != [swappedPeople1,swappedPeople2] and lastPeople != [swappedPeople2,swappedPeople1]):
#if ([swappedPeople1,swappedPeople2] not in self.actionsAlreadyDone) and ([swappedPeople2,swappedPeople1] not in self.actionsAlreadyDone):
newState = cloneState(lastState)
newState[peopleLine1][peopleCol1] = swappedPeople2
newState[peopleLine2][peopleCol2] = swappedPeople1
newState[peopleLine1].sort()
newState[peopleLine2].sort()
newValue = quickValue(state,newState,action)
if(count == 0):
listOfSonsValues.append(newValue)
elif(newValue > max(listOfSonsValues)):
listOfSonsValues.append(newValue)
if(newValue in listOfSonsValues):
yield (action,State(self.numberOfPeople,self.numberOfTable,action,[swappedPeople1,swappedPeople2],state,newState,newValue))
count += 1
def value(self, state):
affinitiesTable = self.affinitiesTable
tableMaxPeople = self.numberOfPeople/self.numberOfTable
value = 0
for table in state:
for people in table:
peopleIndex = 0
while(peopleIndex < tableMaxPeople):
value += int(float(affinitiesTable[people][table[peopleIndex]]))
peopleIndex += 1
return value
"""This function return a table representing the affinities between each people"""
def createAffinitiesTable(self, path):
affinitiesTable = []
f = open(path,'r')
numberOfLine = 0
numberOfColumn = 0
for line in f:
affinitiesColTable = []
minus = False # check Whether we have a negative value or not.
if(numberOfLine == 0):
self.numberOfPeople = line
elif(numberOfLine == 1):
self.numberOfTable = line
else:
for col in line:
if(col != " " and col != '\n' and col != "-"):
if(minus):
affinitiesColTable.append("-"+col)
minus = False
else:
affinitiesColTable.append(col)
elif(col == "-"):
minus = True
numberOfColumn += 1
else:
affinitiesTable.append(affinitiesColTable)
numberOfLine += 1
f.close
self.numberOfPeople = int(float(self.numberOfPeople))
self.numberOfTable = int(float(self.numberOfTable))
return affinitiesTable
"""This function return the initial repartition of people at each table."""
def createTablesAssignment(self, path):
self.affinitiesTable = self.createAffinitiesTable(path)
tableMaxPeople = self.numberOfPeople/self.numberOfTable # Maximum number of people et each table
tablesAssignment = []
tableIndex = 0
peopleLineIndex = 0
listOfSitPeople = [] # List of people who have been assigned to a table
for lineAffinities in self.affinitiesTable:
if(tableIndex >= self.numberOfTable): # All table are full, so we stop
break
dicoOfNoSitPeople = {}
keysAffinity = list(dicoOfNoSitPeople.keys()) # List of all the value used as key represanting an affinity.
peopleColIndex = 0
if (peopleLineIndex not in listOfSitPeople): # If peopleLineIndex have already been assigned don't need to assigne it again.
for Affinity in lineAffinities:
if(peopleLineIndex != peopleColIndex and peopleColIndex not in listOfSitPeople):
if(Affinity not in keysAffinity): # If this affinity already exist we need to add a new people
dicoOfNoSitPeople[Affinity]=[peopleColIndex] # in the dictionnary at this key Otherwise we add this people at
else: # this new key.
dicoOfNoSitPeople[Affinity].append(peopleColIndex)
peopleColIndex+=1
keysAffinity = list(dicoOfNoSitPeople.keys())
orderedAffinities = []
for elem in keysAffinity:
orderedAffinities.append(int(float(elem))) # We want to order int not string.
orderedAffinities.sort(reverse=True)
tablesAssignment.append([])
tablesAssignment[tableIndex].append(peopleLineIndex)
listOfSitPeople.append(peopleLineIndex)
numberOfPeople = 1 # peopleLineIndex have already been assigned so just need to find tableMaxPeople-1 peoples.
key = 0
while (numberOfPeople < tableMaxPeople): # taking all people having best affinities
for elem in dicoOfNoSitPeople[str(orderedAffinities[key])]: # with peopleLineIndex
if (numberOfPeople < tableMaxPeople):
tablesAssignment[tableIndex].append(elem)
listOfSitPeople.append(elem)
numberOfPeople += 1
key += 1
tablesAssignment[tableIndex].sort()
tableIndex += 1
peopleLineIndex += 1
self.initial = State(self.numberOfPeople,self.numberOfTable,[[0,0],[0,0]],[0,0],None,tablesAssignment, self.value(tablesAssignment))
"""Return all possible actions, An action is a 2-uple containing the row and column of peoples who are going to be swapped"""
"""action = [[row1,column1],[row2,column2]]"""
def allPossibleActions(self):
tableMaxPeople = self.numberOfPeople/self.numberOfTable
actionList = []
line1 = 0
while(line1<self.numberOfTable-1):
col1 = 0
while (col1 < tableMaxPeople):
line2 = line1+1
while(line2 < self.numberOfTable):
col2 = 0
while(col2 < tableMaxPeople):
action = [[line1,col1],[line2,col2]]
actionList.append(action)
col2 += 1
line2 += 1
col1 += 1
line1 += 1
return actionList
###############
# State class #
###############
class State:
def __init__(self, n, t, m, p, oldState, tables, value):
self.n = n
self.t = t
self.m = m
self.p = p
self.oldState = oldState
self.tables = tables
self.value = value
######################
# Auxiliary Function #
######################
def concatAllTables(tables):
compareList = []
for table in tables:
compareList += table
return compareList
def quickValue(oldState,newTables,action):
oldValue = oldState.value
oldTable1 = oldState.tables[action[0][0]]
oldTable2 = oldState.tables[action[1][0]]
newTable1 = newTables[action[0][0]]
newTable2 = newTables[action[1][0]]
oldTableValue = singleTableValue(oldTable1) + singleTableValue(oldTable2)
newTableValue = singleTableValue(newTable1) + singleTableValue(newTable2)
return oldValue - oldTableValue + newTableValue
def singleTableValue(table):
affinitiesTable = wedding.affinitiesTable
tableMaxPeople = wedding.numberOfPeople/wedding.numberOfTable
value = 0
for people in table:
peopleIndex = 0
while(peopleIndex < tableMaxPeople):
value += int(float(affinitiesTable[people][table[peopleIndex]]))
peopleIndex += 1
return value
def cloneState(List):
cloneList = []
for line in List:
cloneList.append(list(line))
return cloneList
def maxValueTable(listNodes):
listNodes2 = sorted(listNodes,key=lambda a:a.state.value,reverse = True)
firstElem = listNodes2[0]
compareTables1 = concatAllTables(firstElem.state.tables)
for node in listNodes2:
compareTables2 = concatAllTables(node.state.tables)
if( firstElem.state.value == node.state.value and compareTables1 > compareTables2):
firstElem = node
compareTables1 = compareTables2
elif(firstElem.state.value != node.state.value):
break
return firstElem
def fiveMaxValueTables(listNodes):
listNodes2 = sorted(listNodes,key=lambda a:a.state.value,reverse = True)
listElem = []
i = 0
j = 0
for node in listNodes2:
if(i < 5):
listElem.append(node)
if(listElem[i].state.value != listElem[j].state.value):
j = i
elif (listElem[4].state.value == node.state.value):
listElem.append(node)
else:
break
i+=1
if(len(listElem) == 5):
return listElem
bestTable1 = listElem[j:len(listElem)]
bestTable2 = sorted(bestTable1,key=lambda a:concatAllTables(a.state.tables),reverse = False)
return listElem[0:j]+bestTable2[0:5-j]
def printState(state):
print(state.value)
for table in state.tables:
table.sort()
for people in table:
sys.stdout.write(str(people))
sys.stdout.write(" ")
sys.stdout.write("\n")
################
# Local Search #
################
def randomized_maxvalue(problem, limit=100, callback=None):
current = LSNode(problem, problem.initial, 0)
best = current
random.seed(42)
for step in range(limit):
if callback is not None:
callback(current)
oldNode = current.state.oldState
#print("SSSSSSSSSSTEEEEEEEEEEEEEEEPPPPPPPPPPPPP = ",step)
#for elem in fiveMaxValueTables(list(current.expand())):
# print(elem.state.value)
# print(concatAllTables(elem.state.tables))
current = random.choice(fiveMaxValueTables(list(current.expand())))
if(step != 0):
if(current.state.tables == oldNode.tables):
break
wedding.actionsAlreadyDone.append(current.state.p)
wedding.actionsAlreadyDone.append(current.state.p.reverse())
if current.state.value > best.state.value:
best = current
return best
def maxvalue(problem, limit=100, callback=None):
current = LSNode(problem, problem.initial, 0)
best = current
first = True
for step in range(limit):
if callback is not None:
callback(current)
oldNode = current.state.oldState
current = maxValueTable(list(current.expand()))
if(step != 0):
if(current.state.tables == oldNode.tables):
print(current.step)
break
wedding.actionsAlreadyDone.append(current.state.p)
wedding.actionsAlreadyDone.append(current.state.p.reverse())
if (current.state.value > best.state.value):
best = current
return best
if __name__ == '__main__':
wedding = Wedding(sys.argv[1])
printState(wedding.initial)
start_time = time.time()
node = maxvalue(wedding)
interval = time.time()-start_time
printState(node.state)
print("step =",node.step)
print(interval)
#node2 = randomized_maxvalue(wedding, 100)
#printState(node2.state)
#state = node.state
#print(state)