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
On retrouve ici reproduites les inégalités observées dans presque tous les textes (sauf peut être La Disparition, mais c’est une autre histoire): N, A, S et E en tête, et toujours les mêmes petites lettres qui se trouvent honteusement oubliées…

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 ThorahJulien 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.

Commençons par nous donner un mot, au hasard, e.g.`
MOT="DEMON"
Le but est de voir si ce mot figure, ou pas, dans la matrice de lettre, suivant un schéma simple (c’est l’idée du code de la Thorah). Ma stratégie est ici assez simple: il faut commencer par chercher la lettre la moins présente dans la matrice, qui figure dans le mot. En fait, dans DEMON, je vais me focaliser sur les lettres du centre (en excluant la première et la dernière), i.e. 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()
On trouve ainsi la lettre qui va servir de base dans notre algorithme. A la rigueur, je peux faire une petite fonction qui va colorer une lettre dans la matrice,
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)}
mais pour le moment, ça ne sert à rien. Dans un second temps, on regarde les lettres qui entourent la lettre la plus rare (c’est pour ça que j’ai exclus les lettres au bord, afin d’être certain d’avoir toujours une lettre avant, et une après),
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")
On a ainsi trois lettres importantes: une relativement rare au centre, en jaune, une avant, en vert, et une après en rose. C’est ce qu’on peut visualiser sur le dessin ci-dessous,
Bon, ensuite on rentre dans l’algorithme, un peu lourd à mon gout: on va étudier tous les triplets possibles de ces trois lettres, en identifiants ceux pour lesquels la lettre au centre est précisément au milieu, coincée entre les deux autres lettres. C’est le principe de ce code de la Thorah. La première étape est de récupérer les coordonnées (dans la matrice) de ces trois lettres,
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)
Ensuite, on balaye l’ensemble des triplets (et ça peut être long),
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))}
}}}
Parmi les triplets, on retient seulement ceux pour lesquels la lettre du milieu est précisément au centre, entre les deux autres. Enfin arrive la dernière étape pénible: regarder suivant la suite logique ce que sont les mots que l’on obtient (une fois placées ces trois lettres, la récurrence est complètement décrite). On retient alors les quintuplets correspondant au mot DEMON,
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))}}
Reste à faire tourner cette boucle. Et ca ne manque pas, DEMON apparaît effectivement dans le texte,
On peut aller vérifier, pour ceux qui douteraient,

Coïncidence ? Peut-être…. alors tentons un autre mot, comme MALIN, qui apparait aussi,

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 https://perso.univ-rennes1.fr/arthur.charpentier/latex/tri-02.pnghttps://perso.univ-rennes1.fr/arthur.charpentier/latex/tri-03.png et https://perso.univ-rennes1.fr/arthur.charpentier/latex/tri-04.png.
Le premier algorithme est appelé tri à bulles. C’est probablement l’algorithme le plus simple, et sa vitesse est en https://perso.univ-rennes1.fr/arthur.charpentier/latex/tri-01.png. 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 https://perso.univ-rennes1.fr/arthur.charpentier/latex/tri-01.png)

Bref, un algorithme en https://perso.univ-rennes1.fr/arthur.charpentier/latex/tri-05.png.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 https://perso.univ-rennes1.fr/arthur.charpentier/latex/tri-06.png, en faisant

 

+          for(j in 1:(n-i)){

Bref, la constante dans les https://perso.univ-rennes1.fr/arthur.charpentier/latex/tri-05.png 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 https://perso.univ-rennes1.fr/arthur.charpentier/latex/tri-05.png. 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).

Enfin, le tri fusion est un autre algorithme en https://perso.univ-rennes1.fr/arthur.charpentier/latex/tri-07.png. L’idée est de couper, de trier, puis de fusionner. L’idée étant que trier un tableau dont la première moitié des éléments sont ordonnés, et où la dernière moitié est ordonné doit être plus rapide que quand tout est dans le désordre. A chaque étape, l’algorithme est simple, on compare le minimum des deux listes, puis on le vire à l’étape suivante. Sous R, ça donne, quand on commence

 

> 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 https://perso.univ-rennes1.fr/arthur.charpentier/latex/tri-07.png va effectivement beaucoup plus vite qu’un algorithme en https://perso.univ-rennes1.fr/arthur.charpentier/latex/tri-05.png. 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 https://perso.univ-rennes1.fr/arthur.charpentier/latex/tri-08.png (en noir), en https://perso.univ-rennes1.fr/arthur.charpentier/latex/tri-09.png (en rouge) et en https://perso.univ-rennes1.fr/arthur.charpentier/latex/tri-10..png (en bleu),

La suite sur la convergence des méthodes de monte carlo, puis des théorèmes limites en stats….