Travaux Pratiques #6
Algo & Prog avec R
Table des matières
- 1. Recherche d’éléments dans un vecteur
- 2. Recherche de maximum
- 3. La baguette chanceuse
- 4. Flux d’élèves KEY
- 5. Nombres premiers KEY
- 6. Vectorisation ou fonction prédéfinie
Filter
- 7. Nombres aléatoires absents
- 8. Compression d’un vecteur
- 9. Masque d’une chaîne
- 10. Recherche d’un motif HOME
- 11. Produit de nombres premiers HOME
1. Recherche d’éléments dans un vecteur
Programmez des fonctions avec boucle répondant aux questions ci-dessous.
- Déterminez le nombre de 0 présent dans un vecteur.
- Déterminez l’indice du premier 0 d’un vecteur.
- Déterminez le plus petit élément non nul d’un vecteur.
- Déterminez la valeur médiane d’un vecteur.
Le code ci-dessous répond aux questions avec des fonctions prédéfinies.
vec <- sample(0:10,12, replace=TRUE) print(vec) sum(vec == 0) head(which(vec == 0),1) min(vec[ vec != 0]) median(vec)
2. Recherche de maximum
Recherche simple
Vous ne devez pas utiliser les fonctions prédéfinies max
ou which.max
dans cette partie
- Programmez la fonction
Max2(a, b)
prenant en argument deux nombresa
etb
, et retournant le maximum de ces deux nombres. - Programmez la fonction
Max3(a, b, c)
renvoyant le maximum de trois nombresa
etb
, etc
. - Programmez la fonction
MaxF(x)
prenant un vecteur numériquex
et retournant le maximum dex
.
Recherche à partir d’une position
Programmez la fonction MaxFrom(x,i)
prenant un vecteur numérique x
et retournant le maximum de x
à partir de l’indice i
inclus.
- Avec une boucle.
MaxFrom <- function(x, i = 1) { vmax <- -Inf j <- max(i,1) while(j <= length(x)) { if(x[j] > vmax) { vmax <- x[j] } j <- j + 1 } return(vmax) }
print(MaxFrom(c(), 1)) x <- sample(1:100, 6, replace = TRUE) print(x) print(MaxFrom(x,0)) print(MaxFrom(x,4)) print(MaxFrom(x,7))
[1] -Inf [1] 51 57 98 30 19 75 [1] 98 [1] 75 [1] -Inf
- Avec des fonctions prédéfinies. Indices :
tail
etmax
.MaxFrom <- function(x, i = 1) { if(i > 1) { x <- tail(x,-i+1) } return(max(x)) }
print(MaxFrom(c(), 1)) print(MaxFrom(x,0)) print(MaxFrom(x,4)) print(MaxFrom(x,7))
[1] -Inf Warning message: In max(x) : aucun argument pour max ; -Inf est renvoyé [1] 98 [1] 75 [1] -Inf Warning message: In max(x) : aucun argument pour max ; -Inf est renvoyé
Recherche de la valeur et de la position
Programmer une fonction MaxAndIdx(x)
prenant en argument un vecteur x
numérique, et retournant un vecteur contenant le plus grand élément et sa position.
- Avec une boucle.
MaxAndIdx <- function(x) { imax=0; vmax=-Inf for (i in seq_along(x)) { if(x[i] > vmax) { imax=i; vmax=x[i]; } } return (c(vmax, imax)) }
x <- runif(6); print(x) print(MaxAndIdx(x))
[1] 0.2560613 0.7291801 0.5234866 0.2992518 0.3504035 0.4288227 [1] 0.7291801 2.0000000
- Avec des fonctions prédéfinies. Indice :
which.max
.MaxAndIdx <- function(x) c(max(x), which.max(x))
print(MaxAndIdx(x))
[1] 0.7291801 2.0000000
- Attention, l’affichage facilite la lecture, mais peut induire en erreur sur le type des données.
x <- c(0, 0.25, 0.5) print(x) print(x[-2]) print(x[1]) print(typeof(x[1]))
[1] 0.00 0.25 0.50 [1] 0.0 0.5 [1] 0 [1] "double"
Recherche des k plus grands éléments.
- Programmez une fonction avec une boucle
Max2(x)
prenant un vecteurx
numérique, et retournant le couple des deux plus grands éléments dex
, le maximum étant en première position.Max2 <- function(x) { v1=-Inf v2=-Inf for (v in x) { if(v > v1) { v2 <- v1 v1 <- v } else if(v > v2) { v2 <- v } } return(c(v1,v2)) }
print(Max2(c())) x <- runif(6); print(x) print(Max2(x))
[1] -Inf -Inf [1] 0.6031135 0.1285228 0.3853113 0.7070992 0.7411141 0.9772133 [1] 0.9772133 0.7411141
- Programmez une fonction avec une boucle
Kmax(x, k)
prenant un vecteurx
numérique, et retournant lesk
plus grands éléments dex
triés par ordre non croissant. Indices : inspirez-vous du tri par sélection. - Reprogrammez une fonction
Kmax(x, k)
avec des fonctions prédéfinies. Indices :sort
ethead
.Kmax <- function(x, k) { k <- max(0,k) return( head( sort(x, decreasing = TRUE), k) ) }
x <- sample(1:100, 6, replace = TRUE) print(x) print(Kmax(x, 0)) print(Kmax(x, 2)) print(Kmax(x, 4)) print(Kmax(x, 7))
[1] 40 25 25 37 97 56 integer(0) [1] 97 56 [1] 97 56 40 37 [1] 97 56 40 37 25 25
3. La baguette chanceuse
J’ai dans ma poche 2 pièces de 10 centimes, 1 pièce de 20 centimes, 3 pièces de 50 centimes, 1 pièce de 1 euro et 2 pièces de 2 euro. Quelle est la probabilité pour qu’en sortant deux pièces au hasard de ma poche, je puisse payer une baguette à 1 euro. ?
- Estimez la probabilité par simulation. Indice:
replicate
- Calculez la probabilité théorique.
money <- c(10, 10, 20, 50, 50, 50, 100, 200, 200) CanPay <- function(take, money, amount) { sum(sample(money, take, replace=FALSE)) >= amount } n <- 10**5 proba <- mean(replicate(n, CanPay(2, money, 100))) cat("Simulation probability :", round(proba,4),"\n") ## Theory : ## Winning combinations : ## 100 or 200 x 1 of the 8 remaining coins wins <- choose(3,1)*choose(8,1) ## Combinations with only 100 or 200 are counted twice wins <- wins - choose(3,2) ## 2 x 50. wins <- wins + choose(3,1) total <- choose(length(money), 2) cat("Theoritical probability:", round(wins/total,4),"\n")
Simulation probability : 0.6701 Theoritical probability: 0.6667
4. Flux d’élèves KEY
En l’an 2000, le lycée A compte 2 000 élèves et le lycée B compte 8 000 élèves. Une étude montre que, chaque année :
- 10% des élèves du lycée A quittent leur lycée pour aller au lycée B ;
- 15% des élèves du lycée B quittent leur lycée pour aller au lycée A.
- Au bout de combien de temps le lycée A comptera-t-il plus d’élèves que le lycée B ?
- Quelle est l’évolution de ce système dynamique ? Est-ce qu’il atteint un état stationnaire ? Si oui, caractèrisez-le.
- Tracer un graphique illustrant l’évolution de ce système dynamique. Que se passe t’il ? Expliquez.
- Que se passe t’il si 15% des élèves du lycée A quittent leur lycée pour aller au lycée B ? Que remarquez-vous ?
FluxEleves <- function(n, nA, nB, probAB, probBA) { na <- numeric(n) na[1] <- nA nab <- nA + nB for(i in seq(2,n)) { da <- round( probAB * na[i-1] ) db <- round( probBA*(nab-na[i-1])) na[i] <- na[i-1] - da + db if(na[i] == na[i-1]) { na <- head(na, i) break } } return(na) } nA <- 2000 nB <- 8000 na <- FluxEleves(50, nA, nB, 0.1, 0.15) nb <- nA + nB - na print(na)
[1] 2000 3000 3750 4313 4735 5051 5288 5466 5599 5699 5774 5831 5873 5905 5929 [16] 5947 5960 5970 5977 5982 5987 5990 5993 5995 5996 5997 5997
plot(na, type = 'b', xlab = "Année", ylab = "Effectif", lty = 1, pch = 1) lines(nb, type = 'b', lty = 2, pch = 2)
Attention, il est possible que vous deviez spécifier les paramètres xlim
et ylim
. Cherchez dans la documentation.
5. Nombres premiers KEY
Reprenons le cours sur les nombres premiers. La fonction EstPremier(n)
retourne False si elle trouve un diviseur non trivial (dans [2,n-1]) de n. Nous voudrions connaître ce diviseur !
- Programmez avec une boucle une fonction
PlusPetitDiv(n)
prenant un entiern
≥ 2 et retournant son plus petit diviseur \(\geq 2\). Par exemplePlusPetitDiv(35)
renvoie la valeur 5. Notez au passage que le résultat dePlusPetitDiv(n)
est le plus petit facteur premier den
(pourquoi ?).PlusPetitDiv <- function(n) { ## le plus petit diviseur de n dans [2,n-1], avec n >= 2 if (n %% 2 == 0) return(2) # je regle le cas pair en premier ## donc je peux maintenant monter de 3 vers n, sur les impairs uniquement ! d <- 3 while(n %% d != 0) { ## deux fois plus rapide que le d=d+1 du cours. Bof... d <- d + 2 } ## d == n dans le pire des cas, coût = au plus n operations return(d) } cat('PlusPetitDiv(77) =',PlusPetitDiv(77), '\n') cat('PlusPetitDiv(29) =',PlusPetitDiv(29), '\n') p <- 1000003 cat('PlusPetitDiv(',p,') Version O(n)\n') system.time(replicate(5,PlusPetitDiv(p)))
PlusPetitDiv(77) = 7 PlusPetitDiv(29) = 29 PlusPetitDiv( 1000003 ) Version O(n) utilisateur système écoulé 0.481 0.000 0.481
- En déduire une nouvelle version en une ligne de la fonction
EstPremier(n)
.EstPremier <- function(n) return((n >= 2) && (PlusPetitDiv(n) == n)) cat('EstPremier(77) =',EstPremier(77), '\n') cat('EstPremier(29) =',EstPremier(29), '\n')
EstPremier(77) = FALSE EstPremier(29) = TRUE
- Faites afficher tous les nombres premiers jusqu’à 100.
cat("Voici les nombres premiers compris entre 3 et 100 :\n") n <- 3 while (n <= 100) { if (EstPremier(n)) { cat(n,"") } n <- n + 2 } cat("\n")
Voici les nombres premiers compris entre 3 et 100 : 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
- Il est souvent intéressant d’accélérer un algorithme. Accélérons donc
PlusPetitDiv
. Montrez par un raisonnement mathématique très simple que s’il n’existe aucun diviseur de n dans \([2,\sqrt{n}]\) alorsPlusPetitDiv(n) == n
, ce qui évite de chercher dans le gros intervalle \([\sqrt{n},n]\). Implémentez cette optimisation. Au passage, accélérez encore en évitant de considérer les nombres pairs qui à part 2, ne sont pas premiers.PlusPetitDiv <- function(n) { ## avec au plus sqrt(n) operations if (n %% 2 == 0 ) return(2) d <- 3 ## acc to compute the d*d acc <- 9 while (n %% d != 0 ) { ## <==> d > sqrt(n) mais plus rapide ! if(acc > n) return(n) d <- d + 2 acc <- acc + 4*(d-1) } return(d) } cat('PlusPetitDiv(77) =',PlusPetitDiv(77), '\n') cat('PlusPetitDiv(84201097) =',PlusPetitDiv(84201097),'donc il est premier !\n') cat('PlusPetitDiv(',p,') Version O(sqrt(n))\n') system.time(replicate(5,PlusPetitDiv(p)))
PlusPetitDiv(77) = 7 PlusPetitDiv(84201097) = 84201097 donc il est premier ! PlusPetitDiv( 1000003 ) Version O(sqrt(n)) utilisateur système écoulé 0.003 0.000 0.003
6. Vectorisation ou fonction prédéfinie Filter
Vous ne devez utiliser aucune boucle.
Nombres non multiples d’un entier k
Programmez une fonction NoMult(k,a,b)
retournant le vecteur des entiers non multiples de k
dans l’intervalle [a,b]
. Essayez de programmer une fonction sans boucle, en la vectorisant ou utilisant la fonction filter
.
NoMult <- function(k,a,b) { x <- a:b; return(x[ x %% k != 0]) }
NoMult(5,10,30)
[1] 11 12 13 14 16 17 18 19 21 22 23 24 26 27 28 29
NoMult <- function(k,a,b) Filter(function(n) n %% k != 0 , a:b)
NoMult(2,11,31)
[1] 11 13 15 17 19 21 23 25 27 29 31
Nombres premiers avec un entier k HARD
- Programmez une fonction
CoPremiers(k,a,b)
retournant le vecteur des entiers copremiers aveck
dans l’intervalle[a,b]
.CoPremiers <- function(k,a,b) { k <- abs(k) ## Le plus simple est de créer un vecteur des diviseurs possibles allant de 2 à k. if(k < 2) return(numeric(0)) ## Mais, on va éliminer les nombres multiples de 2 ou 3. divk <- c(2, 3) ## En utilisant la congruence à 6, ils ont la forme 5 + 6k if(k >= 5) divk <- c(divk, seq(5, k, 6)) ## ou 1 + 6k if(k >= 7) divk <- c(divk, seq(7, k, 6)) ## On trouve les diviseurs de k parmi les candidats de divk. divk <- divk[ k %% divk == 0] ## Un nombre n est copremiers avec k si il n'a aucun diviseur commun avec k. Filter(function(n) all(n %% divk != 0), a:b) }
CoPremiers(15,10,30)
[1] 11 13 14 16 17 19 22 23 26 28 29
- Programmez une fonction
CoPremiersPGCD(k,a,b)
avec les mêmes spécifications, mais utilisant une fonction auxiliaire calculant le pgcd de deux entiers de manière itérative.PGCD <- function(a,b) { while ( b != 0 ) { tmp = a %% b a = b b = tmp } return(a); } CoPremiersPGCD <- function(k,a,b) Filter(function(n) PGCD(n,k) == 1 , a:b)
- Programmez une fonction
TestCoPremiers(func, n)
qui affiche le résultat des tests de performance de catégorien
(à définir) pour la fonctionfunc
passée en paramètre. Comparer les performances des deux fonctions. Quelle est la plus efficace en théorie ? En pratique ?TestCoPremiers <- function(func, n) { for(k in 10**seq(n)) { for(a in 10**seq(n)) { func(k, a, 10 * a) } } } for(n in seq(3)) { cat("n=", n, "\n") cat("Avec vectorisation\n") print(system.time(TestCoPremiers(CoPremiers, n))) cat("Avec pgcd\n") print(system.time(TestCoPremiers(CoPremiersPGCD, n))) }
n= 1 Avec vectorisation utilisateur système écoulé 0.009 0.000 0.009 Avec pgcd utilisateur système écoulé 0.002 0.000 0.003 n= 2 Avec vectorisation utilisateur système écoulé 0.001 0.000 0.002 Avec pgcd utilisateur système écoulé 0.005 0.000 0.005 n= 3 Avec vectorisationy utilisateur système écoulé 0.025 0.000 0.025 Avec pgcd utilisateur système écoulé 0.054 0.000 0.054
7. Nombres aléatoires absents
- Utilisez la fonction
sample
pour créer une liste de 100 entiers compris entre 1 et 100 tirés au hasard avec remise. - Calculer le nombre d’éléments compris entre 1 et 100 qui n’appartiennent pas à cette liste.
- Recommencer cette expérience un grand nombre de fois et calculer la moyenne du nombre d’absents.
- Comparer à la valeur théorique.
n <- 100 xp <- 1000 ## Réalise l'expérience et renvoie le nombre d'absents CountMissing <- function(n) { x <- sample.int(n, n, replace=TRUE) missing <- !(1:n %in% x) return(sum(missing)) } ## Répétons l'expérience avec une boucle absents <- 0 for(i in 1:xp) { absents <- absents + CountMissing(n) } print(absents/xp) ## Répétons l'expérience par vectorisation print(mean(replicate(xp, CountMissing(n))))
[1] 36.578 [1] 36.514
8. Compression d’un vecteur
- Programmez une fonction
Compacter(x)
prenant un vecteurx
et remplaçant toute suite d’éléments consécutifsx
,x
,…,x
par le seul élémentx
.Compacter <- function(x) { i <- 1 cx <- c() while (i <= length(x) ) { y <- x[i] i <- i + 1 cx <- append(cx, y) ## ou ## cx[length(cx)+1] <- x while (i <= length(x) && x[i]==y) { i <- i + 1 } } return(cx) }
x <- c(3,3,3,8,5,5,5,7,7) Compacter(x) Compacter(c(1,x,9)) Compacter(c(x,x))
[1] 3 8 5 7 [1] 1 3 8 5 7 9 [1] 3 8 5 7 3 8 5 7
- Programmez une fonction
Compacter(x)
vectorisée. Indice : utiliser les fonctionsdiff
etas.logical
.Compacter <- function (x) x[c(TRUE, as.logical(diff(x)))] Compacter(x) Compacter(c(1,x,9))
[1] 3 8 5 7 [1] 1 3 8 5 7 9
- Modifiez la fonction
Compacter
de sorte qu’elle indique en plus la longueur de chaque sous-suite d’éléments consécutifs. HARDLa fonction renvoie une liste nommée contenant deux vecteurs.
Compacter <- function(x) { i <- 1 cx <- c() cl <- c() while (i <= length(x) ) { y <- x[i] j <- i + 1 cx <- append(cx, y) while (j <= length(x) && x[j]==y) { j <- j + 1 } cl <- append(cl, j-i) i <- j } return(list(values = cx, lengths = cl )) }
Compacter(x)
$values [1] 3 8 5 7 $lengths [1] 3 1 3 2
Compacter(c(1,x,9))
$values [1] 1 3 8 5 7 9 $lengths [1] 1 3 1 3 2 1
9. Masque d’une chaîne
Programmez une fonction masque(s,x)
prenant un vecteur x
numérique et une chaîne s
, et retournant une nouvelle chaîne contenant les caractères dont les positions dans s
figurent dans la liste x
.
Masque <- function(str, pos) intToUtf8(utf8ToInt(str)[pos])
Masque("CAGCTACCTA",c(2,5,3,8))
[1] "ATGC"
Attention : les chaînes de caractères ne sont pas des vecteurs.
10. Recherche d’un motif HOME
Nous poursuivons l’exercice complémentaire du TP3, dont nous supposons que vous avez programmé la fonction CountSubstringMatch(s1,s2)
dont vous allez vous inspirer. Programmez une fonction SubstringMatchExact(s1,s2)
retournant le vecteur contenant les positions successives de l’apparition de s2
dans s1
.
SubstringMatchExact <- function(str, pattern) { res <- c(); r <- regexpr(pattern,str) i <- 0 while(r > 0) { i <- i + r res <- append(res, i) str <- substr(str,start=r+1,stop=nchar(str)) r <- regexpr(pattern,str) ## On ne peut pas utilisr gregexpr, car on autorise le chevauchement entre les motifs } return(res) }
SubstringMatchExact('atatata','ata')
[1] 1 3 5
SubstringMatchExact('atgacatgcacaagtatgcat','atgc')
[1] 6 16
11. Produit de nombres premiers HOME
D’après le cours Python du MIT.
La théorie des nombres sait démontrer que si \(P_n\) dénote le produit de tous les nombres premiers jusqu’à \(n\), alors : \[ \lim_{n\rightarrow +\infty} \frac{P_n}{e^n}=1 \quad (\text{en maths } P_n \sim e^n) \] Mais calculer le produit d’un grand nombre d’entiers premiers va produire un très grand nombre ce qui peut nous occasionner des problèmes de calcul. Une manière d’y remédier est de remplacer les multiplications par des additions. Les logarithmes existent précisément dans ce but. Nous allons donc remplacer le produit des nombres premiers par la somme \(S_n\) de leur logarithme.
- Montrez que le théorème précédent s’exprime alors sous la forme : \(S_n \sim n\). Attention, ne croyez pas que \(f \sim g\) entraîne toujours \(\log(f) \sim \log(g)\). Mais c’est vrai ici car elles tendent vers \(+\infty\). Tâchez de faire une petite démonstration si possible, ou parlez-en à votre prof de maths.
- La fonction logarithme (népérien) de R est
log
. Comment utiliseriez-vous l’ordinateur et le langage R pour vous convaincre du bien-fondé de ce théorème ?
S <-function(n) { ## somme des logarithmes des nombres premiers <= n p = 2 ; acc = 0 while (p <= n){ acc = acc + log(p) p = Suivant(p) ## je saute de p au nombre premier suivant. Voir en-dessous... } return(acc) } Suivant <- function(p) { ## retourne le plus petit nombre premier apres p essai=ifelse(p %% 2 == 0, p+1, p+2) while ( ! EstPremier(essai)) { ## je suis certain d'en trouver, ok ? essai = essai + 2 } return(essai) } cat('Suivant(13) =',Suivant(13), '\n') cat('Je regarde si S(n)/n tend bien vers 1 :\n') for (i in 1:5) { n = 10**i somme = S(n) cat('n =',n,':',somme/n, '\n') } cat('Mmmm. Oui, on dirait bien que la limite vaut 1.\n')
Suivant(13) = 17 Je regarde si S(n)/n tend bien vers 1 : n = 10 : 0.5347108 n = 100 : 0.8372839 n = 1000 : 0.9562453 n = 10000 : 0.9895991 n = 1e+05 : 0.9968539 Mmmm. Oui, on dirait bien que la limite vaut 1.