Bases de l’informatique

Laurent George

Université de Rennes - ESIR

2024

Aperçu du module

  • Objectifs du module
    • Bases de l’algorithmique
    • Le langage JAVA
    • Bonnes pratiques et outils du développeur
  • Organisation du module
    • 6 cours magistraux
    • 16 séances de TD/TP/Projet
  • Évaluation
    • Compte rendu de projet
    • Évaluations individuelles sur table le 18/09/2024 (08:00 - 09:30)
    • Évaluations individuelles sur table le 24/10/2024 (09:45 - 11:15)

1 Introduction à l’algorithmique

1.1 Qu’est-ce qu’un algorithme ?

Algorithme
Suite d’instructions claires qui permet de résoudre un problème
Programme informatique
Énoncé d’un algorithme dans un langage bien défini

Exemples de problèmes:

  • Vérifier si un mot est présent dans un tableau
  • Calculer la racine carrée entière d’un entier naturel
  • Trouver le plus grand et le plus petit nombre dans un tableau

1.2 Analyser un algorithme

  • Est-ce qu’il est correct ?
  • Quelles sont les ressources nécessaires ?
    • complexité en temps (dans le pire des cas)
    • complexité en mémoire (dans le pire des cas)

1.2.1 Complexité

Décrire les ressources nécessaires en termes de temps d’exécution ou de utilisation mémoire, en fonction de la taille de l’entrée (n).

Notation “Big O”:
On dit qu’un algorithme est de complexité \(O(g(n))\) s’il requiert au maximum \(f(n)\) opérations pour une entrée de taille \(n\) et s’il existe une constante \(C\) et \(N_0\) tel que \(f(n)\le c\times g(n)) \forall n > N_0\).

Exemple:

  • \(c\)\(O(1)\), le nombre d’opérations ne dépend pas de n
  • \(n\)\(O(n)\)
  • \(3n + c\)\(O(n)\)
  • \(n^2\)\(O(n^2)\)

Exercice:

  • \(3 * nlog(n) + 22 n + 3 n^3 + 2 n^2 + 42\) → O(?)
  • \(n(n-1) + 5\) → O(?)

1.2.2 Complexité en temps

Sur un ordinateur rapide capable d’exécuter \(10^9\) opérations par seconde

n \(log(n)\) \(n\) \(n log(n)\) \(n^2\)
100 6 ns 99 ns 664 ns 10 us
1000 9 ns 1 us 9 us 1 ms
10 000 13 ns 10 us 132 us 100 ms
100 000 16 ns 100 us 1 ms 10 sec
1 000 000 19 ns 1 ms 19 ms 16 minute
10 000 000 23 ns 10 ms 232 ms 1 jour
100 000 000 26 ns 100 ms 2 sec 3 mois
1 000 000 000 29 ns 1 sec 29 sec 31 ans

1.3 Exemple: recherche dans la mémoire de l’ordinateur

Le but du problème est de découvrir si un mot se trouve dans un tableau de mots et de renvoyer sa position si c’est le cas.

Tableau en mémoire
0 1 2 3 4 5
Medtronic Abbott Siemens Biotronik Baxter MicroPort

1.3.1 Pseudocode

Convention pour écrire les algorithmes, sans faire référence à un langage de programmation en particulier

Entrée: tableau: String[n], clé: String
Sortie: ø
Début
    pour i de 0 à longueur(tableau) - 1
        si tableau[i] == clé
            afficher(i)
            terminer
    fin pour
    afficher -1
Fin
  • Quelle est la complexité de cet algorithme ?
    • Complexité en temps: O(n)
    • Complexité en mémoire: O(1)

1.3.2 Implémentation en Java

public static void recherche(String[] tableau, String cle) {
    // Parcourir le tableau du début à la fin
    for (int i = 0; i < tableau.length; i++) {
        if (tableau[i].equals(cle)) {
            System.out.println(i);
            return;
        }
    }
}

String[] tableau = {"medtronic", "abbott", "siemens", "biotronik", "baxter", "microport"};
recherche(tableau, "medtronic");
0
recherche(tableau, "siemens");
2
recherche(tableau, "esir");

2 Java

2.1 Histoire de Java

  • inventé en 1995
    • par James Gosling, Sun Microsystem
    • maintenu par Oracle
  • très populaire
    • applications android
    • data-pipelines
  • reconnu pour sa stabilité, sécurité et sa portabilité
  • spécification du langage: https://docs.oracle.com/javase/specs

java signifie café en argot américain

2.2 Premier programme

// fichier Hello.java
1public class Hello {

2    public static void main(String[] args) {
3        System.out.println("Bonjour à tous!");
    }

}
1
Nom de la classe
2
Bloc principal qui contient les instructions de l’algorithme principal
3
Affiche le message Bonjour à tous

2.3 La plateforme Java

flowchart TD
    A[Fichier .java] -->|Compilation| B[Bytecode .class]
    B -->|Interprétation par la JVM| C[Code Machine]
    C -->|Exécution par le CPU| D[Programme Exécuté]

# Compilation:
javac MonFichier.java

# Interprétation du byte code en code machine et execution
java MonFichier

2.4 Les Types en java

Toutes les variables, toutes les expressions ont un type connu du compilateur lors de la compilation

Deux sortes de type en java:

  • types primitifs (boolean, byte, short, int, long, char, float, double)
  • types références (un type référence fait référence à des objets en mémoire)

2.5 Les Variables en Java

Astuce

Les variables permettent de stocker deux choses différentes:

  • des valeurs de types primitifs : boolean, char, byte, short, int, long, float, double
  • des références vers des objets en mémoire

Exemple de variables qui stockent des types primitifs:

int a; // variable de type int, nom de la variable: a
int x = 8;  // variable de type int, nom de la variable: x, valeur: 8
boolean y = true;
byte v = -12;
long distanceToSun = 1_496_000_000_000L; 

Exemple de variables qui stockent des références:

Crepe myCrepe = new Crepe("sugar");
Crepe anotherCrepe = new Crepe("butter");

2.6 Types primitifs

type valeurs exemple
boolean valeur logique true, false
byte entier entre -128 et 127 -12
short entier -32768 à 32767
int entier signé sur 32 bits 0, -3311, -2147483648
long entier signé sur 64 bits 2882123498719247990L
char caractère (16-bit) 'a', 'b', 'Y'
float nombre à virgule (32-bit IEEE 754 standard) 221.38f
double nombre à virgule (64-bit IEEE 754 standard) 2.3, -391.38, 3.25e21

2.6.1 Assigner une valeur à une variable

int X;
int Y;
int Z;
  • Utiliser un littéral
X = 32;
  • Assigner la valeur d’une variable à une autre variable
Y = X;
  • Utiliser une expression
Z = X + Y + 1;
System.out.println("Z contient la valeur " + Z);
Z contient la valeur 65

2.6.2 Assigner une valeur à une variable

Le compilateur interdit de placer une valeur d’un grand type dans un petit type

int temp_var_1 = 34.5;
|   int temp_var_1 = 34.5;
incompatible types: possible lossy conversion from double to int
boolean temp_var_2 = 3 ;
|   boolean temp_var_2 = 3 ;
incompatible types: int cannot be converted to boolean

2.6.3 Assigner une valeur à une variable

Le compilateur autorise de placer une valeur d’un petit type dans un grand type, exemple: mettre un float dans un double.

float temp_var_3 = 32.1f;
double temp_var_4 = temp_var_3 ;

2.7 Une variable peut stocker une Référence vers un objet

  • Une référence agit comme un identifiant permettant d’accéder à un objet stocké en mémoire, sans avoir à manipuler des adresses mémoires.
Crepe myCrepe = new Crepe("sugar");
Crepe anotherCrepe = new Crepe("butter");
Crepe oldCrepe = myCrepe;

exemple références

2.7.1 Les objets créés à partir d’une classe

Classe:
Un plan qui définit la structure (les attributs) et le comportement d’un type d’objet
class Student {
    // Attributs
    String firstName;
    String lastName;
    int entryYear;

    // Constructeur
    public Student(String firstName, String lastName, int entryYear) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.entryYear = entryYear;
    }

    // Méthode
    public void displayInfo() {
        System.out.println("Student : "+ firstName + " " + lastName + " (entry year: " + entryYear + ")");
    }
}

2.7.2 Instancier une classe: mot clé new

Objet:
Une instance concrète créée à partir d’une classe
Student s1 = new Student("Jane", "Doe", 2024); // s1 est une reference vers un objet de classe Student
s1.displayInfo();
Student : Jane Doe (entry year: 2024)

2.8 Les tableaux

Tableau
Stocke des éléments du même type

Exemple tableau d’entier:

int size = 42;
int[] arrayOfInts = new int[size];

Exemple tableau d’objets:

int size = 2;
Student[] arrayOfStudents = new Student[size];
arrayOfStudents[0] = new Student("Jane", "Doe", 2024);
arrayOfStudents[1] = new Student("John", "Doe", 2022);
for (int i=0; i < arrayOfStudents.length; i++)
{
    arrayOfStudents[i].displayInfo();
}
Student : Jane Doe (entry year: 2024)
Student : John Doe (entry year: 2022)

Astuce

  • Tableaux Primitifs : Stockent des valeurs primitives (int, char, double, etc.).
  • Tableaux d’Objets : Stockent des références à des objets (Student[], String[], etc.).

2.9 Conditions

int value = 2;
if (value == 1) {
    System.out.println("La condition est vrai");
}
else {
    System.out.println("La condition est fausse");
}
La condition est fausse

2.9.1 Conditions : Switch

int value = 2;
switch(value){
    case 1:
    System.out.println(1);
    break;
    case 2:
    System.out.println("TADA");
    break;
    default:
    System.out.println("problem");
    break;
}
TADA

2.10 Boucles

Java possède de nombreuses structures de boucle: do/while, for, foreach

2.10.1 Boucles for

for (int i = 0; // Initialisation de la variable i à 0
i < 10; // Condition d'arrêt
i++) { // Instruction qui est executée après bloque de code
    System.out.println(i); // Bloque de code qui est executé à chaque tour de boucle
}
0
1
2
3
4
5
6
7
8
9

2.10.2 Boucles for-each

String[] companies = {"Medtronic", "Abbot", "Philips"};
for (String company: companies) {
System.out.println(company);
}
Medtronic
Abbot
Philips
// producce similar result 

for (int i = 0 ; i < companies.length; i++) {
System.out.println(companies[i]);
}
Medtronic
Abbot
Philips

2.11 Gestion des erreurs

try { 
    // code risqué, qui peut echouer

} catch (Exception e) {
    // on peut traiter l'erreur ici

} finally {
    // executer quoi qu'il arrive
}

2.12 Les commentaires

/* Un commnentaire sur 
        plusieurs 
        lignes
*/

// Un commnentaire sur une ligne
  • Javadoc
/**
* This class allows to ...
*
* @author laurent george
* @return ...
*/
class Random {}

3 Les outils du développeur

3.1 Environnements de Développement (IDE)

3.1.1 Fonctionnalités indispensables d’un IDE

  • Debugger
  • Navigation rapide dans le code
  • Auto-complétion et suggestions de code
  • Syntax highlighting
  • Refactoring automatique
  • Intégration avec Git et autres outils

3.1.2 IDE populaires

Eclipse

  • IDE open source
  • Support pour Java, C++, et plus
  • Plugins extensibles

JetBrains IntelliJ IDEA

  • Puissant, surtout pour Java
  • Nombreuses fonctionnalités avancées
  • Licence commerciale

3.1.3 Attention aux pièges des IDE

  • Ne pas comprendre les mécanismes sous-jacents
    • Ex : import automatique de dépendances
  • Perte de compétences sans IDE
    • Ex : incapacité à coder sans support visuel

3.1.4 Bonnes pratiques

  • Connaître les outils, mais aussi les concepts
  • Maîtriser un éditeur simple (ex : Vim, Emacs)
  • Équilibrer confort et compréhension technique

3.2 Systèmes de gestion de versions

3.2.1 Qu’est-ce qu’un système de gestion de versions ?

  • Outil permettant de suivre les modifications apportées au code au fil du temps
  • Permet de répondre à :
    • D’où vient cette ligne de code ? Qui l’a écrite et quand ?
    • Comment revenir à une version précédente du projet ?
    • Comment collaborer efficacement avec d’autres développeurs ?

3.2.2 Types de systèmes de gestion de versions

  • Systèmes centralisés :
    • Exemple : Subversion (SVN)
    • Un serveur central gère l’historique et les versions du projet.
  • Systèmes distribués :
    • Exemples : Mercurial, Git
    • Chaque développeur possède une copie du projet, y compris tout l’historique.

3.3 Focus sur Git

3.3.1 Qu’est-ce que Git ?

  • Système de gestion de version distribué.
  • Créé par Linus Torvalds en 2005.
  • Git enregistre des instantanés (snapshots) des fichiers, plutôt que de simplement enregistrer les différences.
  • Git est conçu pour être sécurisé : il est difficile de perdre du code.

3.3.2 Les Avantages de Git

  • Performance élevée même pour les projets de grande envergure (exemple: Linux > 30.34 millions de lignes de code).
  • Flexibilité dans la gestion des branches et des fusions (merge).
  • Sécurité : ajout de données sans risque de perte.
  • Communauté active et nombreuses ressources disponibles.

3.3.3 Commandes Git de Base

1git init
2git add Crepe.java
3git commit -m "feat(TECH-42): add Crepe class"
4git status
5git push
1
Initialise un nouveau dépôt Git.
2
Ajoute des fichiers au suivi de version.
3
Enregistre les changements avec un message descriptif.
4
Vérifie l’état actuel du dépot local.
5
Envoie les modifications vers un dépôt distant.

3.3.4 Écrire des messages de commit utiles

Bonnes pratiques :

feat: improve searching performance using binary search
fix(BUG-1234): handle missing patient in db

Mauvaises pratiques :

"update code"
""
"fix"

Recommandation : Suivre les conventions de commits standardisées comme Conventional Commits

3.3.5 Git ignore

Certains fichiers ne doivent pas être suivis par Git, par exemple :

  • Fichiers contenant des informations sensibles (mots de passe).
  • Fichiers binaires inutiles pour le projet (ex : .class).

Exemple : Ajouter un fichier .gitignore pour exclure ces fichiers.

.env
config/secrets.yml

# Fichiers binaires
*.class
*.exe

3.3.6 Autres systèmes de gestion de versions

  • Subversion (SVN)
    • Système de gestion de version centralisé.
    • Utilisé pour des projets nécessitant un contrôle strict depuis un serveur unique.
  • Mercurial
    • Système de gestion de version distribué, similaire à Git.
    • Focus sur la simplicité et la facilité d’utilisation.

3.3.7 Conclusion

  • Git est aujourd’hui le système de gestion de version le plus utilisé
  • Comprendre et savoir utiliser Git est essentiel pour tout développeur moderne
  • Les autres systèmes comme SVN et Mercurial ont leurs propres avantages, mais sont moins utilisés dans l’industrie actuelle

Ressource recommandée : Git Book

3.4 Tests unitaires

3.4.1 Qu’est-ce qu’un test unitaire ?

  • Définition : Vérification de l’exactitude d’une unité de code (ex. : fonction ou méthode).
  • Objectif : Assurer que chaque composant fonctionne comme prévu, indépendamment des autres.

3.4.2 Pourquoi écrire des tests unitaires ?

  • Fiabilité : Détecter les erreurs rapidement lors des modifications de code.
  • Régression : Prévenir la réintroduction de bugs déjà corrigés.
  • Documentation : Les tests servent de documentation vivante pour le code.

3.4.3 Bonnes pratiques

  • Granularité : Un test = un comportement spécifique à valider.
  • Isolation : Les tests doivent être indépendants les uns des autres.
  • Nommage clair : Faciliter la compréhension du test et de son objectif.

3.4.4 Exemple avec JUnit (Java)

3.4.5 Autres types de tests (aperçu)

  • Tests d’intégration : Vérifier les interactions entre plusieurs composants.
  • Tests fonctionnels : Valider le comportement global d’une application.
  • Tests de performance : Mesurer la rapidité d’exécution du code.

4 Aller plus loin

Aziz, Adnan, Tsung-Hsien Lee, et Amit Prakash. 2012. « Elements of programming interviews: the insidersguide ». https://books.google.com/books?hl=en&lr=&id=y6FLBQAAQBAJ&oi=fnd&pg=PP2&dq=the+programming+interview&ots=AvJrhMt3qi&sig=WX1FnCaoiX6K2vZi-Cmr1XH1UAQ.
Bhargava, Aditya Y. 2024. « Grokking algorithms ». https://books.google.com/books?hl=en&lr=&id=JIz2EAAAQBAJ&oi=fnd&pg=PR13&dq=Grokking+Algorithms,+Second+Edition&ots=036QT0Rrbd&sig=XC2albFkoaTajdUemcLJga2hmUU.
Cormen, Thomas H., Charles E. Leiserson, Ronald L. Rivest, et Clifford Stein. 2022. « Introduction to algorithms ». https://books.google.com/books?hl=en&lr=&id=RSMuEAAAQBAJ&oi=fnd&pg=PR13&dq=algorithm+book&ots=a3i5YV9MQL&sig=1fcn_acnkUxicRm8Fj2fNBFGn98.
Sierra, Kathy, et Bert Bates. 2005. « Head First Java: A Brain-Friendly Guide ». https://books.google.com/books?hl=en&lr=&id=KXQrAQAAQBAJ&oi=fnd&pg=PA1&dq=Head+first+java&ots=WP0hgXDGHo&sig=_9kTodmWQSQQJ_IICwiSWWIT-Vs.

Others

4.1 Assigner une valeur à une variable

Attention, les boolean ne peuvent être converti directement en entier

Variables

Avec les types primitifs, la valeur d’une variable est la valeur (par exemple en mémoire la variable x correspond à un entier sur 32 bits qui vaut 12).

Une variable qui stocke une référence contient des bits qui permettent d’identifier/récupérer un objet spécifique en mémoire.

Avec les réf primitifs -> la valeur d’une variable est la valeur