cours général du module OBI2

Transcription

cours général du module OBI2
Module 2: Algorithmique et Programmation : Notions de
base pour les biologistes
Objectif:
Initiation à la programmation et à l'algorithmique. Faisabilité d'un algorithme : les problèmes
simples et les problèmes complexes. Présentation des algorithmes classiques pour l'optimisation
et le traitement des données.
Contenu :
Qu'est ce qu'un algorithme ? Analyse d'un problème.
Représentation et structures de données.
Liaisons entre les structures de données et les algorithmes.
Complexité d'un algorithme. Résolutions exactes et heuristiques.
La mise en oeuvre sera faite dans un langage de programmation interprété et souple (Python).
Jour 1:
cours:
notions générales d'algorithmique et de programmation (variables, structure de contrôle,
fonctions).
Voyageur de commerce – NP complet (factorielle)
Heuristique (planche à clous, savon – MonteCarlo)
Programmation dynamique – Camions (tranches)
Présentation du langage Python (Interprété/Interactif)
Variables: type implicite
liste, tuple et string sont des séquences.
Indentation délimite les blocs d'instructions – instruction vide "pass"
Listes:
a = [] # déclaration d'une liste
T-uples:
a = (4, ‘titi’,3.14159) # déclaration d'un t-uple (un t-uple est non
modifiable)
Les listes et t-uples commencent à 0, coordonnées négatives == permutations circulaires –1 ==
dernière case (maximum négatif = -longueur, dernière case +1 : erreur,, types différents
autorisés. Donc les coordonnées vont de –longueur à +(longueur –1)
a.append("toto")
a[3:10] # extrait éléments 3 à 9
for i in liste:
# i prend successivement les valeurs de la liste
for i in range(debut, fin, step):
# i prend les valeurs de "debut à ("fin"-1) par pas de "step".
while <test> :
def foo(arguments):
# indentation nécessaire
Fonctions : attention, passage par valeur, sauf les listes, strings et tuples.
Cela veut dire une variable est copiée dans la fonction : la fonction ne peut donc modifier la
variable de l’appelant.
Mais dans le cas des listes, strings et tuples, le nom de la variable est une référence, donc la
référence n’est pas modifiable, mais l’objet référencé peut l’être (puisqu’on a la référence !).
Références :
Voir l’exemple suivant :
>>>
>>>
[1,
>>>
>>>
[1,
>>>
>>>
[1,
>>>
[1,
x=[1,2,3]
x
2, 3]
y=x
y
2, 3]
x[2]=-1
x
2, -1]
y
2, -1]
y est donc bien une référence à x, y et x désignent un même objet (une liste)
Pour copier une liste, il faut l’expliciter, avec par exemple les index de début et de fin, comme
ici :
>>>
>>>
[1,
>>>
>>>
[1,
>>>
[1,
z=x[:]
z
2, -1]
x[2]=-2
x
2, -2]
z
2, -1]
Là, x a été copiée dans z, et si on touche à x, on ne touche pas à z.
Si on ne veut pas qu’une liste soit touchée dans une fonction, il faudrait alors faire :
>>> def foo(l):
... l[2]=-5
... print l
>>>
[1,
>>>
[1,
>>>
[1,
>>>
[1,
foo(x)
2, -5]
x
2, -5]
foo(z[:])
2, -5]
z
2, -1]
# là, x est touchée
# là, y n’est pas touchée
# lister les méthodes liées a un objet
range(4)
liste
[0, 1, 2, 3]
liste=dir(liste)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__',
'__eq__', '__ge__', '__getattribute__', '__getitem__', '__getslice__',
'__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__le__', '__len__',
'__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__repr__',
'__rmul__', '__setattr__', '__setitem__', '__setslice__', '__str__', 'append',
'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
map, reduce, filter
reduce(plus,liste) # f(f(f(liste[0],liste[1]),liste[2]), liste[3])..etc
6
map(carre,liste)
[0, 1, 4, 9]
map(plus, liste,liste)
[0, 2, 4, 6]
map(None, liste1,liste2): applique None aux doublets d'une liste constituée
de chacune des paires des éléments de liste1 et liste2
filter(f,liste): retourne la sous liste de liste constituée de chacun des
éléments pour lesquels f est vraie.
TP:
Utilisation des structures de données et des structures de contrôles de Python, fonctions.
Boucle Tests
Calcul moyenne
moyenne cumulative selon formule:
mi = mi-1 + vi*i/(i-1)
mi: moyenne au "temps" i, vi ième valeur
calcul variance
Faire avec programmation impérative (boucle) et fonctionnelle (reduce).
fichier python "fonctions"
def somme(a, b):
return a+b
def carre(n):
return n*n
def sommeseq(seq): # somme d'une séquence
s=0
for x in seq:
s=s+x
return s
# somme par reduce (programmation fonctionnelle)
def sum(liste):
# somme d'une liste
return reduce(somme,liste)
def moyenne(liste):
return float(sommeseq(liste))/len(liste)
# variance
def var(liste):
return (moyenne(map(carre,liste))carre(moyenne(liste))*(float(len(liste))/(len(liste)-1)))
def admis(s):
return (s[-1]>=10)
# retourne une liste de n-uplets qui sont les mots des lignes
# du fichier f
def readfile(f):
t= []
fil = open(f,'r')
tmp= fil.readlines()
for x in tmp:
t.append(x.split())
# fil.close()
return t
# retourne 0 si a==b, -1 si a<b, + 1 si a>b
def compare(a,b):
if a>b:
return 1
elif a<b:
return -1
else :
return 0
=================
fichier "notes":
Terrieur Alain 12 4 16
Potanpoche Jessica 0 12 4
Chemonfis Thierry 8.5 14 17
Bonnot Jean 18 12
Kiroul Pierre 10 4.5 15
...
#!/usr/local/bin/python2.1
import fonctions
f=open('notes','r')
ll=f.readlines()
t=[]
for x in ll:
t.append(x.split())
et=[]
etud=[]
for x in t:
et=x[0:2]
et.append(fonctions.moyenne(map(float,x[2:])))
etud.append(et)
w=open('moys','w')
for x in etud:
w.write(x[0]+" "+x[1]+" "+"%2.2f" % x[2]+"\n")
# afficher à l'écra les admis:
print "etudiants admis :
listeadmis = filter(fonctions.admis,etud)
for x in listeadmis :
print x[0], x[1], '%2.2f' % x[2]
w.close()
Jour 2 :
Cours:
Récursivité (Cf document recursif.pdf)
Algorithmes de tri. TP: tri (insertion, sélection, bulle, merge)
Sur le net : applications graphiques en java :
http://java.sun.com/applets/jdk/1.4/demo/applets/SortDemo/example1.html
http://www.cs.ubc.ca/spider/harrison/Java/
http://lwh.free.fr/pages/algo/tri/tri.htm
TP:
Recherche séquentielle dans une liste triée.
C’est l’algorithme de recherche le plus élémentaire qui parcourt la listejusqu’à ce qu’un objet soit
trouvé ou bien que la fin du tableau soit atteinte. Nous allons appliquer cela à la liste obtenue
après lecture du fichier notes en cherchant la ligne correspondant à un étudiant sur la base de son
nom de famille.
def rec_seq(tab, name):
cpt = 0
for i in tab:
test = compare(i[0], name)
cpt = cpt+1
if test ==0 :
return i, cpt
print "pas trouve"
Le nombre maximum de comparaisons d’objets effectuées par cette algorithme est N le nombre
d’éléments de la liste, ce maximum étant atteint lorsque l’élément n’est pas trouvé. En moyenne
il procède à N/2 comparaisons. Cela est très mauvais et on sait faire beaucoup mieux.
Recherche dichotomique dans une liste triée.
Un supplément d’information, permet souvent de réduire la complexité d’un problème.
L’algorithme de recherche séquentielle n’utilisait que le test d’égalité avec deux réponses
possibles (oui/non). Comme les éléments (les noms) sont triés par ordre croissant. On peut
réaliser un test avec trois résultats : égalité, inférieur, supérieur.
#!/prog/seq_analysis/python
import fonctions
# t est une liste de triplets (nom, prenom, notes) triée sur les noms
# v est le nom recherché
def dicho(t,v):
g=0
d=len(t)-1
while g<=d:
m=(g+d)/2
test = fonctions.compare(v,t[m][0])
if test == 1:
g=m+1
elif test == -1:
d=m-1
else:
print 'trouve en position',m
return
print 'pas trouve'
# 'notes' est le fichier du jour 1
note = fonctions.readfile('notes')
dicho (note,'Grange')
- Ouvrir un texte (nedit) pour le programme "tri.py"
# "driver" pour les tris en interactif
import tri # la première fois
reload(tri) # les autres fois
# création du fichier de nombres aléatoires
import random
f=open("random","w")
i = 0
while i < 100:
f.write("%f " % random.random())
i=i+1
f.close()
f=open("random","r") # lecture du fichier de nombres aléatoires
ll=f.readline() # ll est une string avec toutes les valeurs
t=[] # création du tableau de valeurs
for x in string.split(ll):
t.append(float(x))
(ensuite trier...)
====== tri.py
#!/usr/bin/python
import sys
from copy import deepcopy
import random
import pdb
#---------------def Compare(x,y):
p=0
if y < x :
p = -1
elif x < y :
p = 1
return p
#---------------def Echange(Tr,x,y):
#Copie en dur, par valeur pas par adresse
T=deepcopy(Tr)
temp=T[x]
T[x]=T[y]
T[y]=temp
return T
#---------------def RechSeq(T,x):
if T[0] == x :
return 0
else:
L=len(T)
i=0
while i < L and T[i] != x :
i=i+1
if i < L :
return i
else:
return -1
#---------------def RechDich(T,x):
trouve = -1
i = 0
j=len(T)
while i <= j and trouve == -1 :
mid=int((i+j)/2)
if x == T[mid] :
trouve = mid
else :
if x > T[mid] :
i = mid + 1
else:
j = mid - 1
return trouve
#---------------def PosMin(T,x,y):
mini=x
for i in xrange(x+1,y):
if Compare(T[i],T[min]) == 1 :
mini=i
return mini
#---------------def TriSeq(T):
i=0
L = len(T)
Trie=deepcopy(T)
while i < L-1 :
min = PosMin(i,L)
if i != min :
Trie=Echange(Trie,i,min)
i=i+1
return Trie
#---------------def TriBulle(Tr):
permut=1
L = len(Tr)
T=deepcopy(Tr)
i=ntot=0
comp=ech=0
while permut > 0 :
if i == L-1 :
print "Bulle ",ntot
if ntot == 0 :
permut = 0
else:
i = 0
ntot = 0
#pdb.set_trace()
else:
p=Compare(T[i],T[i+1])
comp+=1
if p == -1 :
ntot+=1
Trie=Echange(T,i,i+1)
ech+=1
i=i+1
return T,comp,ech
#---------------def TriIns(Tr):
i=1
L = len(Tr)
T=deepcopy(Tr)
while i < L :
p=Compare(T[i-1],T[i])
if p == -1 :
pos=0
j=0
while j < i and T[i] > T[j] :
j=j+1
pos = j
if pos == 0 :
temp=T[0:i]
temf=T[i+1:L]
T=[T[i]]+temp+temf
elif pos < i :
temp=T[0:pos]
temf=T[pos:i]
tem2=T[i+1:L]
T=temp+[T[i]]+temf+tem2
i=i+1
print "TriIns ",T
return T
#---------------def TriShell(Tr) :
T=deepcopy(Tr)
#Calcul du pas
h=0
while h < L :
h = 3*h + 1
print h
while h > 0 :
i=h
while i < L :
valeur=T[i]
j=i-h
while j >= 0 and T[j] > valeur :
T=Echange(T,j,j+h)
j=j-h
i+=1
print h," ** ",T
h = h/3
return T
#---------------def Fusion(T1,T2) :
L1=len(T1)
L2=len(T2)
Fus=[]
i=j=0
while i < L1 and j < L2 :
if T1[i] < T2[j] :
Fus.append(T1[i])
i+=1
else :
Fus.append(T2[j])
j+=1
if i == L1 :
Fus.extend(T2[j:])
else :
Fus.extend(T1[i:])
return Fus
#---------------def TriFusion(T) :
if len(T) == 1 :
return T
else:
mid=int(len(T)/2)
Tfus=Fusion( TriFusion(T[:mid]) , TriFusion(T[mid:]) )
return Tfus
#---------------def TriRapide(Tr,deb,fin) :
T=deepcopy(Tr)
if deb < fin :
#Definition du pivot au milieu
pivot = (deb+fin)/2
#On place le pivot au debut du tableau pour faire une
comparaison continue
T=Echange(T,deb,pivot)
#Comparaison des valeurs % pivot et range val inf au
debut
posinf=deb
for k in xrange(deb+1,fin) :
if Compare(T[k],T[deb]) == 1:
posinf+=1
T=Echange(T,k,posinf)
#Mettre le pivot à sa position, donc à la suite des
elements qui lui sont inferieurs
T=Echange(T,deb,posinf)
#Tri sous tableau gauche
T=TriRapide(T,deb,posinf)
#Tri sous tableau droit
T=TriRapide(T,posinf+1,fin)
return T
#---------------def main():
x=10
T=[]
for i in range(x) :
T.append(int(10*random.random()))
#T=[2, 3, 4, 3, 5, 9, 3, 4, 1, 6]
print T
T=TriRapide(T,0,x)
print T
main()
Jour 3 :
Cours
Cours optimisation (simplexe, monte carlo et méthodes dérivées (recuit simulé), gradients)
Optimisation numérique:
(Cf document Minimisation numérique.doc)
- monte carlo: exemple du calcul de π
Optimisation combinatoire (Pierre)
(Cf document Optimisation_combinatoire.doc)
TP: dénombrement des mono et dinucléotides (comparaison)–les dictionnaires
- comptage dans les séquences des mots de longueur 1 et 2
- calcul du CG skew le long d’un génome
séquence au format fasta:
>NC_002162 Ureaplasma urealyticum, complete genome.
atggctaataattatcaaactttatatgattcagcaataaaaaggattccatacgatctt
atttctgatcaagcttatgcaattctacaaaatgctaaaactcataaagtttgcgatggt
gttttatatataattgtagccaatgcctttgaaaaaagtattattaacggtaattttatt
...
Programme pour compter les mononucléotides, les dinucléotides, et calculer le GC Skew sur une
fenêtre de longueur w avec sortie dans un fichier (lignes : <position> <GCskew>) à plotter dans
gnuplot (avec plot "plotgc.txt" )
#!/usr/bin/python
# --------------------------------------------------------def readfasta(nf):
f=open("seq.fst","r")
nf="seq.fst"
seq=""
line=f.readline()
while (line!=""):
line=f.readline()
seq=seq+line[:-1]
return seq
# --------------------------------------------------------def dico(nf):
dico={}
for x in nf:
if dico.has_key(x):
dico[x]=dico[x]+1
else:
dico[x]=1
return dico
# --------------------------------------------------------def dicodi(nf):
dicodi={}
for i in range(len(seq[:-2])):
dicodi[seq[i:i+2]]=dicodi.get(seq[i:i+2],0)+1
for j in dicodi.keys():
dicodi[j]=float(dicodi[j])/float(len(seq))
return dicodi
# --------------------------------------------------------def compare(dico_mono,dico_dinuc):
for i in dico_dinuc.keys():
fmono=float(dico_mono[i[0]]*dico_mono[i[1]])
fdinuc=dico_dinuc[i]
print "%s %f %f" %(i,fmono,fdinuc)
# --------------------------------------------------------def calcgc(dicogc):
return
float((dicogc["G"]-dicogc["C"]))/(dicogc["G"]+dicogc["C"])
# --------------------------------------------------------def gc(seq,w):
gc={}
gc=dico(seq[0:w])
filout=open("plotgc.txt","w")
filout.write("%d %.3f\n" %(0, calcgc(gc)))
for i in range(1,len(seq)-w+1):
gc[seq[i-1]]=gc[seq[i-1]]-1
gc[seq[i+w-1]]=gc[seq[i+w-1]]+1
filout.write("%d %.3f\n" %(i, calcgc(gc)))
filout.close()
return
# --------- MAIN -----------------------------------------seq=readfasta("seq.fst")
m=dico(seq)
d=dicodi(seq)
compare(m,d)
print "nombre de bases total",len(seq)
countbases=dico(seq)
print countbases
dinucleotides=dicodi(seq)
print dinucleotides
gc(seq,1000)
TP: camion, programmation dynamique
camions (programmation dynamique). liens avec les séquences
prendre le fichier de villes sur le site de Joel
#--------------------------------------------------------------#!/usr/local/bin/python
#!/usr/bin/python
import sys
def lit_fichier(fname):
"""
A partir du nom de fichier contenant les distances entre les
paires de villes sous le format: ville1 ville2 distance, cette
fonction rend la ville de depart (premier champs de la premiere
ligne ainsi qu'un dictionnaire de distance ou les cles sont les
paires (ville1, ville2) et les valeurs sont les distances en reels.
"""
f=open(fname,'r')
l=f.readlines()
dico={}
for x in l:
t=x.split()
dico[t[0],t[1]]=float(t[2])
deb=l[0].split()[0]
return deb,dico
#--------------------------------------------------------------def tranche(debut,dict):
"""
Cette fonction a partir de la ville de depart et des cles du
dictionnaire dict rend une liste de listes ou le ieme element
correspond aux villes de la ieme trance a traverser
"""
tv=[]
dd=dict.keys()
temp=[debut]
while len(temp)!=0:
tv.append(temp)
temp=[]
debut=tv[-1][0]
for i in dict.keys():
if i[0]==debut:
temp.append(i[1])
return tv
#--------------------------------------------------------------def chemin(liste,dict, dep):
"""
Cette fonction determine le chemin minimum en passant par une ville
et une seule de chaque tranche de villes contenues dans liste en partant
de la ville dep et en fonction des distances contenues dans dict
cour correspond au meilleur chemin courrant (donc il est remis a rien a
chaque nouvelle tranche l etudiee). A chaque fois qu'une tranche est terminee,
je
mets dans chem1 le resultat de cour. Chem1 est donc le meilleur chemin
pour les villes de la lieme tranche -1.
"""
depart = [(dep), 0]
cour=[depart]
# Remplissage de cour pour la tranche 0
for l in liste[1:]:
# Pour chaque tranche de villes
chem1 = cour
cour=[]
for i in l:
# Pour chaque ville de la tranche l
# on fixe arbitrairement le minimum
# a la premiere distance
mini=chem1[0][-1]+dict[(chem1[0][-2],i)]
for j in chem1: # Pour chaque ville de la tranche l-1
v=j[-2],i
distemp=j[-1]+dict[v]
if distemp<=mini:
mini = distemp
cmini = j[:-1]
cmini.append(i)
cmini.append(mini)
cour.append(cmini)
return cour
#--------------------------------------------------------------def main():
if len(sys.argv)!=2:
print "entrez la commande sous la forme: camion.py fichier"
else:
fichier=sys.argv[1]
dep,donnees=lit_fichier(fichier)
tran=tranche(dep,donnees)
#print tran
print chemin(tran, donnees, dep)
main()
#---------------------------------------------------------------
Jour 4 :
TP: optimisation suite (recuit simulé à partir du montecarlo, gradient).
Taboo search: grille à 2 dimensions
#!/usr/bin/python
import random
import math
#fonction de rosenbroock
def rosen((x,y)):
return 100.0*(y-(x*x))*(y-(x*x))+(1-x)*(1-x)
# calcul du paysage de la fonction
# sauvergarde dans fichier "paysage"
# pour affichage dans gnuplot (sans "line")
# Dans gnuplot, il suffira d'entrer
# splot "paysage","recuit.xyz","taboo.xyz"
# pour voir en 3D les chemins differe
# (note: on peut aussi le faire directement
# dans gnuplot, donc facultatif)
def paysage(fun,xmin,xmax,ymin,ymax,pas):
f=open("paysage","w")
x=xmin
while x<=xmax:
y=ymin
while y>=ymax:
f.write("%f %f%f\n" %(y,y,fun((x,y))))
y=y+pas
#print x,y,fun((x,y))
x=x+pas
f.close()
return
#
# recuit simule (fichier "recuit.py")
# impression des points dans fichier "recuit.xyz" a
# a tracer dans gnuplot avec l'option "with line"
# arguments:
#
f: fonction
#
pstart: point 2D de depart (x,y)
#
tempinit: Temperature de depart
#
Tf: Temperature finale
#
npas: nombre de pas a chaque temperature
#
pas: longueur du pas
#
def recuit(f,(x,y),ti,tf,pas,npas):
t = ti
r= open("recuit.xyz",'w')
# xmin,ymin vont etre les minima courant
xmin,ymin=(x,y)
# X,Y va etre le minimum minimorum
X,Y = (xmin ,ymin)
while t>tf:
for i in range(0,npas):
x = xmin + (random.random()*2 - 1)*pas
y = ymin + (random.random()*2 - 1)*pas
d = f((x,y)) - f((xmin,ymin))
if (d < 0):
xmin ,ymin = x,y
r.write("%f %f %f\n" % (xmin,ymin,f((xmin,ymin))))
if f((xmin,ymin)) < f((X,Y)):
X,Y = (xmin ,ymin)
elif math.exp((-d)/t) > random.random():
xmin ,ymin = x,y
r.write("%f %f %f\n" %(xmin,ymin,f((xmin,ymin))))
if f((xmin,ymin)) < f((X,Y)):
X,Y = (xmin ,ymin)
t=t*0.7
return (X,Y)
#
#
#
#
#
#
#
#
taboo search
NotInList:
fonction verifiant que l'element el
n'est pas dans la liste (retourne 1
si c'est vrai)
def NotInList(el, liste):
# liste est une liste de 2-uples d'entiers
# qui sont les arrondis de 10 fois les coordonnees
# de maniere a ne pas repasser par un point proche
# puisque les reels ne retomberaient jamais juste
# el est le tuple des coordonnees reelles du point
# dont on veut savoir si il est dans la liste taboo
eli = (int(10*el[0]+5),int(10*el[1]+5))
for i in liste:
if eli == i:
return 0
return 1
#
# taboo search: routine principale
# impression des points dans fichier "taboo.xyz" a
# tracer dans gnuplot avec l'option "with line"
# arguments:
#
f: fonction
#
pstart: point 2D de depart (x,y)
#
npas: nombre de pas total a ne pas depasser
#
pas: longueur du pas
#
lentaboo: longueur de la liste tabou
#
def taboo(f,(x,y),npas,pas,lentaboo):
r= open("taboo.xyz",'w')
n = 0
listetaboo = [None]*lentaboo
listetaboo[0] = (int(10*x+5),int(10*y+5))
xmin,ymin = (x,y)
solutions = 1
while n < npas:
x = (random.random()*2 - 1)*pas + x
y = (random.random()*2 - 1)*pas + y
if NotInList((x,y),listetaboo) == 1 and f((x,y)) <f((xmin,ymin)) :
xmin,ymin = (x,y)
solutions = solutions + 1
r.write("%f %f %f\n" %(x,y,f((x,y))))
listetaboo[solutions % lentaboo] = (int(10*x+5),int(10*y+5))
n+=1
return (xmin,ymin)
s = recuit(rosen,(-2,2),1000,1,1.0,100)
print "recuit: meilleure solution: ",s, rosen(s)
s = taboo(rosen,(-2,2),10000,1,30)
print"taboo: meilleure solution: ", s, rosen(s)
Si on met les données du paysage dans « paysage », celle du trajet (x,y,f(x,y)) dans « recuit.xyz »
(ou dans « taboo.xyz »), il suffit pour tracer le chemin de minimisation dans le paysage de donner
les ordres suivant dans gnuplot :
splot "paysage","taboo.xyz" with line,"recuit.xyz" with line
# ou mieux, on peut créer le « paysage » directement avec :
set hidden3d
set isosamples 30
set view 60,30,1
splot [-2.0:2] [-2:2] 100 * (y - (x*x))*(y - (x*x))+(1-x)*(1-x), "recuit.xyz"
with line, "taboo.xyz" with line
(* ceci figure la fontion de Rosenbrook en 3D avec les trajets par dessus *)
ANNEXE
Affectation: Auction algorithm:
Personnes Voitures
i -> A[i] u[i,j] pour chaque j appartenant a A[i]
Si C = MAX{u[i,j]: (i,j) appartenant a A}
A chaque pas, il existe un prix price [j] pour les voitures.
Marginal utility de la personne i pour acheter j -> u[i,j] - price[j]
A chaque itération, une personne "non assignée" choisit un j tel que u[i,j]-price[j] est maximum
On associe a chaque personne i une value[i], plafond sur la plus haute "marginal utility",
cad value[i] >= MAX{u[i,j]: (i,j) appartenant a A}
On dit que:
Bid[i,j] est admissible si value[i] = u[i,j] - price[j]
Bid[i,j] est inadmissible si value[i] sinon
Il faut pour l'algorithme que chaque Bid soit "admissible".
Si la personne i est la prochaine à être traitée et qu'elle n'a pas de Bid "admissible",
alors value[i] est trop haute, et on décroît cette valeur a:
MAX{u[i,j]-price[j]: (i,j) appartenant a A}
Donc, l'algorithme procède en personnes "préemptant" des voitures.
Si une personne i préempte une voiture j, alors le prix de j augmente de 1F; ainsi les
préemptions suivantes seront plus élevées, et i est assigné à j.
La personne k qui avait une préemption sur j (si elle existe) devient "inassignée",
elle devra préempter une autre voiture par la suite.
Tant que le marché continue, le prix des voitures augmente et les valeurs marginales des
personnes décroissent.
L'algorithme s'arrête dès que chaque personne a une voiture.
On atteint un assignement optimum.
Depart: price[j] = 0 pour chaque voiture
value[i] = MAX{u[i,j]: (i,j) appartenant a A} (c'est suffisant
pour une version pseudo-polynomiale)
exemple d'affectation:
Min SOMME(i,j) (xiyj)
y
6
12
15
15
x
4
8
9
11
10
5
7
8
12
10
6
9
Resultat: (x1y1),(x2y4),(x3y2),(x4y3), SOMME=28