Parution cette semaine d’un joli dossier intitulé Algorithmes: garder le contrôle, dans l’Actuariel.
Tag Archives: algorithme
Qui ne voit pas la présence du diable dans la loi ?
Je suis de plus en plus dérouté par le mélange des genres dans l’espace médiatique, de ces prétendus spécialistes qui nous expliquent des choses qu’ils ont du découvrir 10 minutes avant de commencer à taper un article. Ce matin, des éditorialistes se sentaient autoriser à fabuler sur un sondage conduit sur internet auprès de 800 personnes, et j’avoue que cela m’a agacé… J’ignore si j’ai davantage de légitimité, mais je me suis dit que la loi 78 adoptée vendredi (au Québec) pourrait être un prétexte intéressant pour écrire sur un sujet d’actualité (chose que j’hésite d’ordinaire à faire, car ce n’est pas mon métier, loin de là). Pourquoi ne pourrais-je pas, moi aussi, faire mon malin et analyser un texte de loi ? On va donc regarder cette loi dans le détail, à partir du texte en ligne sur http://profscontrelahausse.org/ (ou pour être complètement honnête, le début seulement de la loi)
L=paste("Dans la presente loi, a moins", "que le contexte nindique", "un sens different, on entend par:", "association d’etudiants}}:une", "association ou un regroupement ", "d’associations de niveau postsecondaire",
etc
"la qualite de l’enseignement, les ", "services requis de façon a ", "tenir compte des circonstances particulieres", "resultant de l’interruption ", "de la session d’hiver de l’annee 2012 ou ", "de la session d’ete de l’annee 2012.") L2=substring(toupper(L),1:nchar(L),1:nchar(L)) alphabet=c("A","B","C","D","E","F","G" ,"H","I","J","K","L","M","N","O", "P","Q","R","S","T","U","V","W","X","Y","Z") L2=L2[L2%in%alphabet] ML=t(matrix(L2,nc,nl))
On peut visualiser le début du texte ci-dessous (l’idée est de ne garder que les lettres, pour faire simple, mais on peut tenter plus largement en gardant la ponctuation, et les chiffres, ça ne change rien aux algorithmes que nous allons développer)
Commencons pas des trivialités: le texte de cette loi est fondamentalement inégalitaire: ce sont toujours les mêmes qui sortent gagnants, et les mêmes qui sortent perdants,
> sort(table(L))[c(1,2,3,4,5,20,21,22,23)] L Y J X H F N A S E 7 8 10 24 26 355 386 402 820
Maintenant, si on prend le temps de regarder en détail le contenu de la loi, force est de constater qu’on y voit la présence du Malin constamment ! Pour reprendre une idée de Bahye ben Asher ibn Halawa (רבינו בחיי) on peut tenter d’utiliser le code de la Thorah. Julien Prévieux avait utilisé cette technique il y a quelques temps pour retrouver toute une terminologie associée aux crashs boursiers dans une page du Capital de Karl Marx, prise au hasard, ou aux paradis fiscaux dans la Richesse des Nations d’Adam Smith. Les amateurs de la théorie de Ramsey prétendront que c’est une supercherie, et ils auront probablement raison. Mais après avoir joué l’autre jour sur la recherche de schémas graphiques dans des images (en l’occurrence des rayures rouges et blanches, afin de repérer Charlie), j’ai eu envie de jouer à chercher des mots dans une matrice de lettres.
MOT="DEMON"
lettrelaplusrare=function(mot=MOT){ lettres=substring(mot,2:(nchar(mot)-1), 2:(nchar(mot)-1)); fL=TL[lettres]; return(names(fL)[which.min(fL)])} rare=lettrelaplusrare()
dessinlettre=function(ltr="A",clr="yellow"){ which(L==ltr); CLR=rep(NA,length(ML)); CLR[which(as.vector(ML)==ltr)]=clr; dessin(ML,CLR,plt=FALSE)}
lettres=substring(MOT,1:(nchar(mot)), 1:(nchar(mot))) avant=lettres[which(lettres==rare)-1] apres=lettres[which(lettres==rare)+1] avant=avant[1] apres=apres[1] dessinlettre(rare,"yellow") dessinlettre(avant,"green") dessinlettre(apres,"pink")
lrr=which(as.vector(ML)==rare) lav=which(as.vector(ML)==avant) lap=which(as.vector(ML)==apres) LIGrr=(lrr-1)%%nrow(ML)+1 COLrr=(lrr-1)%/%nrow(ML)+1 LIGav=(lav-1)%%nrow(ML)+1 COLav=(lav-1)%/%nrow(ML)+1 LIGap=(lap-1)%%nrow(ML)+1 COLap=(lap-1)%/%nrow(ML)+1 ptrr=cbind(LIGrr,COLrr) ptav=cbind(LIGav,COLav) ptap=cbind(LIGap,COLap)
pointsalignes=function(x,y,z){ ((y[1]+2*(x[1]-y[1]))==z[1])&((y[2]+ 2*(x[2]-y[2]))==z[2])} LEQUEL=rep(NA,3) for(a in 1:length(LIGrr)){ for(b in 1:length(LIGav)){ for(c in 1:length(LIGap)){ if(pointsalignes(ptrr[a,],ptav[b,],ptap[c,])== TRUE){LEQUEL=cbind(LEQUEL,c(a,b,c))} }}}
V=LEQUEL for(k in 2:ncol(V)){ CLR=rep(NA,length(ML)); i1=ptrr[V[1,k],]; CLR[(i1[2]-1)*ncol(ML)+i1[1]]=clr; lettres=substring(MOT,1:(nchar(mot)), 1:(nchar(mot))) milieu=which(lettres==rare) obtenu=ML[i1[1],i1[2]] for(u in 1:(milieu-1)){ i2=ptav[V[2,k],]+(u-1)*(ptav[V[2,k],]-ptrr[V[1,k],]); indice=(i2[2]-1)*ncol(ML)+i2[1] if((i2[2]<=ncol(ML))&(i2[1]>=1)&(i2[1]<= nrow(ML))&(i2[1]>=1)){CLR[indice]=clr; obtenu=paste(ML[i2[1],i2[2]],obtenu,sep="")}} for(u in (milieu+1):nchar(MOT)){ i3=ptap[V[3,k],]+(u-milieu-1)*(ptap[V[3,k],]- ptrr[V[1,k],]); indice=(i3[2]-1)*ncol(ML)+i3[1] if((i3[2]<=ncol(ML))&(i3[1]>=1)&(i3[1]<= nrow(ML))&(i3[1]>=1)){CLR[indice]=clr; obtenu=paste(obtenu,ML[i3[1],i3[2]],sep="")}} if(obtenu==MOT) {print(paste(k," ",obtenu))}}
ou alors SATAN… ce dernier apparaît huit fois dans le début du texte !
Étonnant non…. en tous les cas, je me suis bien amusé à coder ce petit algorithme, et de voir qu’il fonctionnait aussi bien ! Les amateurs de probabilités pourront se lancer dans des calculs, ou consulter quelques articles qui évoquent cette technique, comme McKay,Bar-Natan, Bar-Hillel & Kalai (2001), et tous les articles évoqués dans les références… Quant au juge qui devra se prononcer sur ma condamnation pour blasphème et pour hérésie, il va de soi que ce billet est une blague. Le diable n’existe pas ! On n’est plus au moyen-age quand même….
De la vitesse de convergence, partie 1
Samedi après midi, à l’heure de la sieste pour certains, nous discutions avec Magali, Jean Baptiste et Florent des machines qui pouvaient trier les cartes, de là, la discussion a dévié sur les algorithmes de tri, et sur la difficulté d’expliquer l’importance de la vitesse d’un algorithme, ou plus généralement, la vitesse de convergence. Bref, avant de parler de vitesse de convergence en statistique – ou peut être avant sur les méthodes de calcul d’intégrale (ou d’espérance) – je voulais faire un petit billet rapide sur les algorithmes de tri. Je commence par des choses simples. De mémoire c’est des choses que je me souviens avoir vu au lycée dans les années 90, dans les cours d’informatique… Mais l’exemple peut être intéressant pour comprendre la différence, par exemple, entre , et .
Le premier algorithme est appelé tri à bulles. C’est probablement l’algorithme le plus simple, et sa vitesse est en . On va balayer autant de fois la base que l’on a d’observations, et à chaque passage, on récupère le maximum (ou le minimum, peu importe). L’algorithme ressemble à ça,
> n=25; s=1 > X=sample(1:n,size=n,replace=FALSE) > for(i in 1:(n-1)){ + for(j in 1:(n-1)){ + s=s+1 + if(X[j] > X[j + 1]){X[c(j,j+1)]=X[c(j+1,j)]} + }}
Si on l’implémente, voilà ce que ça donne (tous les graphiques partent de la même permutation initiale, et tous contiennent autant d’images, de l’ordre de )
Bref, un algorithme en .Bon, j’ai été un peu brutal dans mon algorithme. En fait, on voit que les derniers points sont rapidement bien classés, donc inutile que la seconde boucle aille jusqu’au bout… on peut donc faire du , en faisant
+ for(j in 1:(n-i)){
Bref, la constante dans les peut être relativement importante comme on le voit ici.
Mais on peut faire mieux, voire beaucoup mieux comme on le verra ensuite.
Le second algorithme est le tri par insertion. C’est aussi un algorithme très simple: on balaye l’ensemble en plaçant les valeurs rencontrées à leur place, un peu comme quand on tri ses cartes… L’algorithme est là aussi en . Ici l’algorithme converge un peu plus rapidement que le précédant, mais cela vient du fait que les données étaient partiellement ordonnées sur la fin. Autrement dit, un autre échantillon aurait pu donner autre chose… Mais généralement, c’est le cas.
> for(i in 1:n){ + x = X[i] + for(j in (i+1):2){ + s=s+1 + if(X[j - 1] < x){X[j] = X[j - 1]; j = j - 1; X[j] = x} + } + }
(le tri est fait à l’envers ici…. mais c’est un détail).
> for(i in 0:(trunc(n/2)-1)){ + Y=X[c(1,2)+2*i] + if(Y[1]>Y[2]){X[c(1,2)+2*i]=X[c(2,1)+2*i]} + }
Bref, en commençant par classer les paires (1,2) puis (3,4), etc, puis les listes (1,2) versus (3,4). Le code est alors de la forme
> k=4 > for(i in 0:(trunc(n/4)-1)){ + Y=X[1:k+k*i] + Y1=c(Y[1:(k/2)],rep(999999,k/2));Y2=c(Y[(k/2+1):k],rep(999999,k/2)); + for(j in 1:k+k*i){ + X[j]=min(c(Y1[1],Y2[1]),na.rm=TRUE) + if(X[j]==Y1[1]){Y1=Y1[ -1]} + if(X[j]==Y2[1]){Y2=Y2[-1]} + }}
(avec une petite correction pour le dernier bloc, qui ne compare pas forcément un bloc de taille 2 contre un autre de taille 2 (mais l’adaptation du code est infime). Puis on continue avec (1,2,3,4) versus (5,6,7,8), c’est relativement rapide de tout classer, comme le montre l’animation ci-dessous.
Il existe de très nombreux autres algorithmes de tri, le but étant de monter qu’un algorithme en va effectivement beaucoup plus vite qu’un algorithme en . J’ai tout fait ici en R, mais pour les adeptes d’autres langages, il existe plein de code en ligne, ici par exemple. Pour conclure, le graphique ci-dessous montre les différences entre les vitesses de convergence en (en noir), en (en rouge) et en (en bleu),
La suite sur la convergence des méthodes de monte carlo, puis des théorèmes limites en stats….