UP | HOME

Travaux Pratiques #6
Algo & Prog avec R

Table des matières

1. Recherche d’éléments dans un vecteur

Programmez des fonctions avec boucle répondant aux questions ci-dessous.

  1. Déterminez le nombre de 0 présent dans un vecteur.
  2. Déterminez l’indice du premier 0 d’un vecteur.
  3. Déterminez le plus petit élément non nul d’un vecteur.
  4. 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

  1. Programmez la fonction Max2(a, b) prenant en argument deux nombres a et b, et retournant le maximum de ces deux nombres.
  2. Programmez la fonction Max3(a, b, c) renvoyant le maximum de trois nombres a et b, et c.
  3. Programmez la fonction MaxF(x) prenant un vecteur numérique x et retournant le maximum de x.

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 et max.
    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 vecteur x numérique, et retournant le couple des deux plus grands éléments de x, 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 vecteur x numérique, et retournant les k plus grands éléments de x 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 et head.
    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. ?

  1. Estimez la probabilité par simulation. Indice: replicate
  2. 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.
    1. Au bout de combien de temps le lycée A comptera-t-il plus d’élèves que le lycée B ?
    2. Quelle est l’évolution de ce système dynamique ? Est-ce qu’il atteint un état stationnaire ? Si oui, caractèrisez-le.
    3. Tracer un graphique illustrant l’évolution de ce système dynamique. Que se passe t’il ? Expliquez.
    4. 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 !

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 avec k 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égorie n (à définir) pour la fonction func 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

  1. Utilisez la fonction sample pour créer une liste de 100 entiers compris entre 1 et 100 tirés au hasard avec remise.
  2. Calculer le nombre d’éléments compris entre 1 et 100 qui n’appartiennent pas à cette liste.
  3. Recommencer cette expérience un grand nombre de fois et calculer la moyenne du nombre d’absents.
  4. 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

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.

  1. 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.
  2. 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.

Created: 2023-12-11 lun. 13:39