Représentation du temps – Calculs sur les dates et heures

Pourquoi 60 ?

import math

def nb_diviseurs(n):
    count = 0
    for i in range(1, int(math.sqrt(n)) + 1):
        if n % i == 0:
            if n == i * i: count += 1
            else: count += 2
    return count

maxi = 0
for n in range(1, 100):
    t = nb_diviseurs(n)
    if t > maxi:
        print(f"{n} a {t} diviseurs")
        maxi = t

# Résultats :

1 a 1 diviseur
2 a 2 diviseurs
4 a 3 diviseurs
6 a 4 diviseurs
12 a 6 diviseurs
24 a 8 diviseurs
36 a 9 diviseurs
48 a 10 diviseurs
60 a 12 diviseurs

Nombre de jours entre 2 dates

mois_jours = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)

def est_bissextile(annee):
    return (annee % 4 == 0 and annee % 100 != 0) or annee % 400 == 0

def jours_dans_annee(jour, mois, annee):
    jours = jour
    for m in range(1, mois):
        jours += mois_jours[m - 1]
    if mois > 2 and est_bissextile(annee): jours += 1
    return jours

def jours_entre_dates(j1, m1, a1, j2, m2, a2):
    if (a1, m1, j1) > (a2, m2, j2):
        j1, m1, a1, j2, m2, a2 = j2, m2, a2, j1, m1, a1
    jours_total = 0
    if a1 == a2:
        return jours_dans_annee(j2, m2, a2) - jours_dans_annee(j1, m1, a1)  
    jours_total += (366 if est_bissextile(a1) else 365) - jours_dans_annee(j1, m1, a1)
    jours_total += jours_dans_annee(j2, m2, a2)  
    for annee in range(a1 + 1, a2):
        jours_total += 366 if est_bissextile(annee) else 365  
    return jours_total

# Exemple

>>> jours_entre_dates(15,10,1582,4,8,2024)
161366

Programme CULMINATION pour les ti-82/83/84

Disp "JJMM.AA ?"
Input D
Disp "LONGITUDE ?"
Input L
dbd(101+fPart(D),D→N
2π(N-81)/365→B
7.678sin(B+1.374)-9.87sin(2B)→E
Disp "CULMINATION"
12+E/60-L/15▸DMS

Programme LONGITUDE pour les ti-82/83/84

Disp "CULMINATION"
Disp "HH°MM'SS''?"
Input H
getDate→L₁
100L₁(3)+L₁(2)+remainder(L₁(1),100)/100→D
dbd(101+fPart(D),D→N
2π(N-81)/365→B
7.678sin(B+1.374)-9.87sin(2B)→E
Disp "LONGITUDE :"
15*(12+E/60-H)
Longitude trouvée pour le 2 août 2024

Représentation du temps : Calendriers

Phases de la lune

cycle_lunaire = 29.53058853
delta = cycle_lunaire / 8
phases = "Nouvelle lune","Premier croissant","Premier quartier", "Gibbeuse croissante","Pleine lune","Gibbeuse décroissante","Dernier quartier","Dernier croissant"
visuel = ['🌑','🌒','🌓','🌔','🌕','🌖','🌗','🌘']

def phase_lune(nb_jours):
 nb_cycles = int(nb_jours / cycle_lunaire)
 reste = nb_jours - nb_cycles * cycle_lunaire
 tranche = int(round(reste / delta, 0) % 8)
 return phases[tranche], visuel[tranche]

>>> phase_lune(365)
('Gibbeuse croissante', '🌔')

Calendrier chinois – signes du zodiaque

def zodiaque_chinois(annee):
 elements = "Bois", "Feu", "Terre", "Métal", "Eau"
 aspects = "Yang", "Yin"
 signes_zodiaque = (
        "Rat", "Buffle", "Tigre", "Lapin", "Dragon", "Serpent",
        "Cheval", "Chèvre", "Singe", "Coq", "Chien", "Cochon"
    )
 annee_ref = 1924 # Année du Rat, début d'un cycle de 60 ans
 diff_annees = annee - annee_ref
 ind_signe_zodiaque = diff_annees % 12
 ind_aspect = diff_annees % 2
 ind_element = (diff_annees % 10) // 2
 signe_zodiaque = signes_zodiaque[ind_signe_zodiaque]
 aspect = aspects[ind_aspect]
 element = elements[ind_element]
 annee_chinoise = annee_ref + (diff_annees % 60)
 return f"{annee_chinoise} : {signe_zodiaque} ({element} {aspect})"
 
>>> zodiaque_chinois(2024)
1964 : Dragon (Bois Yang)

Comparaison JULIEn | GREGORIEN | Personnel

def bissextile_julien(annee):
 return annee % 4 == 0
 
def bissextile_gregorien(annee):
 return (annee % 4 == 0 and annee % 100 != 0) or (annee % 400 == 0)
 
def bissextile_perso(annee):
 return annee % 4 == 0 and annee % 128 != 0
 
def compare(ans, methode):
 total = 0
 for an in range(ans):
  total += 365
  if methode(an): total += 1
 return total - 365.2421904482 * ans

>>> compare(10000, bissextile_julien)
78.09551799995825

>>> compare(10000, bissextile_gregorien)
3.095517999958247

>>> compare(10000, bissextile_perso)
-0.904482000041753

One-liners sur HP Prime

Réponses possibles aux 2 questions finales

ZIPSORT

cat(EXECON("CHAR(SORT({&1,&2}))",ASC(A),ASC(B)))

Exemple de déroulement avec zipsort(« oui », »non ») :

  • On transforme les 2 chaines A et B en listes de codes ASCII : {111,117,105} et {110,111,110}
  • On trie les couples de lettres SORT({&1,&2}) : SORT({111,110}) -> {110,111}
  • Transformation en chaine : CHAR({110,111}) -> « no »
  • On obtient la liste : {« no », »ou », »in »}
  • Concaténation finale : « noouin »

Lettres manquantes

cat(EXECON("CAS((&2≠1+&1)*char(1+&1))",ASC(T)))

Exemple de déroulement avec manque(« abcdfg ») :

  • On transforme la chaine en liste ASCII : {97,98,99,100,102,103}
  • Est-ce que l’élément suivant est différent de l’élément courant + 1 ?
  • EXECON(« &2≠1+&1 »,{97,98,99,100,102,103}) -> {0,0,0,1,0}
  • La lettre manquante a comme code ASCII la lettre courante + 1 : char(1+&1)
  • En multipliant le caractère par 0 on a une chaine vide, sinon on récupère le caractère manquant
  • Concaténation des chaines vides + lettres manquantes
  • manque(« abdefgijklmnpqrstv ») -> « chou »

Quelques challenges (proposition de solutions plus bas)

Autant de « x » que de « o »

Afficher 1 s’il y a autant de « x » que de « o » dans une chaine de caractère, sinon afficher 0.

xox("xxabcoo") -> 1
xox("xxooxoox") -> 1
xox("xaxoboxocccoox") -> 0

Au milieu

Ecrire une fonction qui prend en paramètres un caractère X et le place au milieu de Y répété N fois. Lorsque ce n’est pas possible, renvoyer X.

Exemples :

middle(10,"A","*") --> "*****A*****"  ("A" est au milieu de 10 "*")
middle(9,"A","*") --> "A"             ("A" ne peut pas être au milieu)
middle(2,"X","+") --> "+X+"

mot pur

Un mot pur est un mot dont la somme des positions dans l’alphabet de chaque lettre est divisible par la longueur totale du mot.

Par exemple, « abcb » est un mot pur car 1 + 2 + 3 + 2 = 8 et 8/4 = 2.

pure("ccc") -> 1
pure("bed") -> 0

Palindrome

Renvoyer 1 si une chaine est un palindrome, 0 sinon. La chaine pourra être écrite en minuscules et/ou majuscules.

palind("Laval") -> 1
palind("Angers") -> 0

Somme des N plus grands

Ecrire une fonction qui à partir d’une liste L et d’un entier N renvoie la somme des N entiers les plus grands de L.

sumgrand({4,9,2,3,7,1},2) --> 16    (Les 2 nombres les + grands sont 9 et 7)
sumgrand({-7,9,12,-1,-3},3) --> 20  (9 + 12 - 1 = 20)

Somme sans doublons

Ecrire une fonction qui fait la somme des éléments d’une liste, mais ignore ceux qui sont dupliqués.

Exemples : pour la liste [3, 4, 3, 6] la fonction devra renvoyer 10
et pour la liste [1, 10, 3, 10, 10] la fonction devra renvoyer 4.

Project Euler n°1

L’énoncé est ici

Exemples de solutions

Autant de « x » que de « o »

count_eq(120,ASC(T))=count_eq(111,ASC(T))

Au milieu

IFTE(N MOD 2,X,CAS(N/2*Y+X+N/2*Y))

mot pur

0=ΣLIST(ASC(T) MOD SIZE(T))

Palindrome

UPPER(T)==CHAR(revlist(ASC(UPPER(T))))

Remarquez que cette version fonctionne également (en décochant "L1") : 

revlist(sto(ASC(UPPER(T)),L1))==L1

Somme des N plus grands

ΣLIST(CAS.mid(CAS.SORT(L,"(x,y)->x>y"),1,N))

Somme sans doublons

ΣLIST(apply("x->x*(count_eq(x,L)==1)",L))

Project euler n°1

Décochez "v" :

ΣLIST(remove("x->(x MOD 3)*(x MOD 5)>0",MAKELIST(v,v,1,N)))-N

PROLOG

Tous les codes de la vidéo

Les collectionneurs

possede(cret,calc(hp71b,1984)).
possede(eric,ordi(zx81,1981)).
possede(ledudu,ordi(c64,1982)).
possede(eric,calc(hp41C,1979)).
possede(nicole,calc(pb100,1983)).
possede(audrey,ordi(apple2,1977)).
possede(cret,ordi(c64,1982)).
possede(ledudu,calc(ti57,1977)).
collection(P):-
 possede(P,ordi(_,_)),
 possede(P,calc(_,_)).

annee(B,H,X):-
    B > H,
    annee(H,B,X).

annee(B,H,X):-
    possede(_,calc(X,A)),
    A =< H,
    A >= B.

annee(B,H,X):-
    possede(_,ordi(X,A)),
    A =< H,
    A >= B. 

?- possede(P,ordi(_,1977)).
?- possede(P,T).
?- possede(_,calc(N,_)).
?- possede(X,calc(_,T)),T<1980.

Nombre d’or

Version 1 :

nombreOr(N, R) :- fc(N, 1, R).
fc(1, A, R) :- R = A.
fc(N, A, R) :-
    S is 1 + 1 / A,
    N1 is N - 1,
    fc(N1, S, R). 

Version 2 :

nombreOr(N, Num, Den) :- fc(N, 1, 1, Num, Den).
 
fc(1, Num, Den, Num, Den).
 
fc(N, NumP, DenP, Num, Den) :-
    N1 is N - 1,
    Num2 is NumP + DenP,
    fc(N1, Num2, NumP, Num, Den).

Taille d’une liste

length([],0).

length([H|T],N):-
    length(T,N1), N is N1+1. 

Triplets pythagoriciens

Version 1 :

valide(A, B, C) :-
    C2 is A*A + B*B,
    C*C =:= C2.

cherche(A, B, C, Max) :-
    between(1, Max, A),
    between(A, Max, B),
    between(B, Max, C),
    valide(A, B, C).

pythagore(Max, R) :-
    findall((A,B,C), cherche(A, B, C, Max), R). 

Version 2 :

entre(B, H, B).
entre(B, H, X) :-
    B < H,
    B1 is B + 1,
    entre(B1, H, X).

valide(A, B, C) :-
    C2 is A*A + B*B,
    C*C =:= C2.

cherche(A, B, C, Max) :-
    entre(1, Max, A),
    entre(A, Max, B),
    entre(B, Max, C),
    valide(A, B, C).

pythagore(Max) :-
    cherche(A, B, C, Max),
    L = [A,B,C],
    write(L), nl,
    fail.

Syracuse

Version 1 :

syr(N):- syr(N,0,N).
syr(1,T,M):- write(T),nl,write(M).
syr(N,T,M):-
 T2 is T + 1,
 suiv(N,T2,M).

suiv(N,T,M):-
 0 is N mod 2,
 N2 is N/2,
 syr(N2,T,M).

suiv(N,T,M):-
 N2 is 3*N+1,
 maxisuiv(N2,T,M).

maxisuiv(N,T,M):- N > M, syr(N,T,N).
maxisuiv(N,T,M):- syr(N,T,M). 

Version 2 :

syr(N):- syr(N,0,N).
syr(1,T,M):- write(T),nl,write(M).
syr(N,T,M):-
 T2 is T + 1,
 suiv(N,N2),
 M2 is max(N,M),
 syr(N2,T2,M2).

suiv(N, N2):-
 0 is N mod 2,
 N2 is N/2,!.

suiv(N, 3 * N + 1).

RPN : Recherche de permutations

On trouve, dans certains ouvrages sur les calculatrices HP, des tables donnant les combinaisons de touches pour réordonner les éléments de la pile XYZT :

Par exemple ci-dessus, ligne n°9, si ABCD sont sur la pile XYZT, la combinaison x><y, CLX et ENTER permettra d’avoir 00AC sur la pile.

L’idée m’est venue de faire la même chose pour les calculatrices HP-48/49/50g qui ont d’autres commandes pour manipuler la pile.

A partir d’éléments (e1 au niveau 1, e2 au niveau 2 etc.), on voudrait les réordonner en utilisant les commandes de base du langage RPN : DUP, SWAP, DROP, OVER, ROT, DUP2, DROP2 et PICK3.

Exemples :

La pile contient uniquement 2 éléments e1 (niveau 1) et e2 (niveau 2)et on veut les inverser. Réponse : SWAP

La pile contient 3 éléments e1 : e2 : e3 et on veut inverser les éléments des niveaux 2 et 3 pour obtenir e1 : e3 : e2. Réponse : ROT SWAP

Je vous propose un petit programme Python qui fait une recherche brute de toutes les possibilités (en partant des combinaisons à 1 commandes, puis à 2 commandes etc. Avec répétitions, par exemple OVER OVER). Il recherche jusqu’à une combinaison de 7 commandes ce qui peut demander du temps !

Tester le programme en ligne

from itertools import *

keys = "DUP", "SWAP", "DROP", "OVER", "ROT", "DUP2", "DROP2", "PICK3"

def DUP(s): return [s[0]] + s if len(s)>0 else -1
def SWAP(s): return [s[1], s[0]] + s[2:] if len(s) > 1 else -1
def DROP(s): return s[1:] if len(s) > 0 else -1
def OVER(s): return [s[1]] + s if len(s) > 1 else -1
def ROT(s): return [s[2]] + s[:2] +s[3:] if len(s) > 2 else -1
def DUP2(s): return [s[0], s[1]] + s if len(s) > 1 else -1
def DROP2(s): return s[2:] if len(s) > 2 else -1
def PICK3(s): return [s[2]] + s if len(s) > 2 else -1

def find(target, n):
 rr = [1 + v for v in range(n)]
 tr = [int(v) for v in target]
 for n in range(1, 8):
  for op in combinations_with_replacement(keys, n):
   for p in permutations(op):
    r = rr
    for c in p:
     t = eval(c)(r)
     if t == -1: break
     r = t
    if r == tr:
     rs = ':'.join([str(v) for v in rr])
     return '{} -> {} = {}'.format(rs, ':'.join(list(target)), " / ".join(p))

 return "No solution"

# 5 levels on the stack (e1:e2:e3:e4:e5). Goal : e2:e2
>>> find("22", 5)
'1:2:3:4:5 -> 2:2 = ROT / DROP2 / SWAP / ROT / DROP2 / DUP'

# 3 levels on the stack (e1:e2:e3). Goal : e1:e2:e3:e1:e2:e3
>>> find("123123", 3)
'1:2:3 -> 1:2:3:1:2:3 = PICK3 / PICK3 / PICK3'

# 3 levels on the stack. Goal : e1:e1:e2:e2:e3:e3
>>> find("112233", 3)
'1:2:3 -> 1:1:2:2:3:3 = PICK3 / ROT / ROT / DUP2 / ROT'

# 4 levels on the stack. Goal : e2:e2:e4:e4
>>> find("2244", 4)
'1:2:3:4 -> 2:2:4:4 = ROT / DROP2 / DUP2 / ROT'

Version pour la HP-50g

Cette calculatrice a plus de commandes que les HP-48G pour manipuler la pile, j’ajoute DUPDUP, UNROT et 4 ROLL (comme ROT mais avec le niveau 4)

from itertools import *

# Version HP-50g

keys = "DUP", "DUPDUP","SWAP", "DROP", "OVER", "ROT", "UNROT", "DUP2", "DROP2", "PICK3", "_4_ROLL"

def DUP(s): return [s[0]] + s if len(s)>0 else -1
def DUPDUP(s): return [s[0], s[0]] + s if len(s)>0 else -1
def SWAP(s): return [s[1], s[0]] + s[2:] if len(s) > 1 else -1
def DROP(s): return s[1:] if len(s) > 0 else -1
def OVER(s): return [s[1]] + s if len(s) > 1 else -1
def ROT(s): return [s[2]] + s[:2] +s[3:] if len(s) > 2 else -1
def UNROT(s): return s[1:3] + [s[0]] +s[3:] if len(s) > 2 else -1
def DUP2(s): return [s[0], s[1]] + s if len(s) > 1 else -1
def DROP2(s): return s[2:] if len(s) > 2 else -1
def PICK3(s): return [s[2]] + s if len(s) > 2 else -1
def _4_ROLL(s): return [s[3]] + s[:3] +s[4:] if len(s) > 3 else -1

def find(target, n):
 rr = [1 + v for v in range(n)]
 tr = [int(v) for v in target]
 for n in range(1, 8):
  for op in combinations_with_replacement(keys, n):
   for p in permutations(op):
    r = rr
    for c in p:
     t = eval(c)(r)
     if t == -1: break
     r = t
    if r == tr:
     rs = ':'.join([str(v) for v in rr])
     return '{} -> {} = {}'.format(rs, ':'.join(list(target)), " / ".join(p))

 return "No solution"

>>> find("231", 3)
'1:2:3 -> 2:3:1 = UNROT'

>>> find("4123", 4)
'1:2:3:4 -> 4:1:2:3 = _4_ROLL'

>>> find("135", 5)
'1:2:3:4:5 -> 1:3:5 = SWAP / _4_ROLL / DROP2'

>>> find("4321", 4)
'1:2:3:4 -> 4:3:2:1 = SWAP / ROT / _4_ROLL'

Version en PROLOG (en cours de réalisation)

% Règles pour les opérations sur la pile

% SWAP : Inverse les niveaux 1 et 2
swap([A,B|T], [B,A|T]).

% DUP : Duplique le niveau 1
dup([A|T], [A,A|T]).

% DROP : Supprime le niveau 1
drop([_|T], T).

% OVER : Récupère le niveau 2 et le met au niveau 1
over([A,B|T], [B,A,B|T]).

% ROT
rot([A,B,C|T], [C,A,B|T]).


% Règle pour arrêter la récursivité lorsque la pile est déjà dans l'état voulu
trouve([], Stack, Stack).

% Règles pour trouver la séquence d'opérations 

trouve([swap|Actions], Stack, FinalState) :-
    swap(Stack, NewStack),
    trouve(Actions, NewStack, FinalState).

trouve([dup|Actions], Stack, FinalState) :-
    dup(Stack, NewStack),
    trouve(Actions, NewStack, FinalState).

trouve([drop|Actions], Stack, FinalState) :-
    drop(Stack, NewStack),
    trouve(Actions, NewStack, FinalState).

trouve([over|Actions], Stack, FinalState) :-
    over(Stack, NewStack),
    trouve(Actions, NewStack, FinalState).

trouve([rot|Actions], Stack, FinalState) :-
    rot(Stack, NewStack),
    trouve(Actions, NewStack, FinalState).

Quelques tests

rot([1,4,3],X).
Réponse : X = [3, 1, 4]

swap([1,4,3],X).
Réponse : X = [4, 1, 3]

drop([1,4,3],X).
Réponse : X = [4, 3]

over([1,4,3],X).
Réponse : X = [4, 1, 4, 3]

trouve(X, [1,4,3], [4,1,3]).
X = [swap]

Version 2 proposée par chatGPT

trouve(Actions, Stack, FinalState) :-
    trouver_sequence(Actions, [], Stack, FinalState).

% Cas où l'état actuel est l'état voulu
trouver_sequence([], _, Stack, Stack).

% Cas où l'état actuel est différent de l'état voulu, continue à chercher
trouver_sequence([Action|Actions], History, CurrentState, FinalState) :-
    \+ member(CurrentState, History), % Vérifie si l'état actuel a déjà été visité pour éviter les boucles infinies
    apply_action(Action, CurrentState, NextState),
    trouver_sequence(Actions, [CurrentState|History], NextState, FinalState).

% Applique une action sur l'état actuel de la pile
apply_action(swap, [A,B|T], [B,A|T]).
apply_action(dup, [A|T], [A,A|T]).
apply_action(drop, [_|T], T).
apply_action(over, [A,B|T], [B,A,B|T]).
apply_action(rot, [A,B,C|T], [C,A,B|T]).

GolfScript

Programmes à tester ici : https://tio.run/#golfscript

Programmes de la vidéo

Somme des entiers

10 .)*2/
donne 55

Syracuse version 1

27 .2%{3*)}{2/}if
donne 82

16 3*).2%6\?/
donne 8

Maximum d’un tableau

[2 5 1 3] $-1=
donne 5

Rentre Avec Tes Pieds

['RENTRE' 'AVEC' 'TES' 'PIEDS'] {[0=]}%'.'*
donne "R.A.T.P"

Fractions continues et nombre d’or

1{-1?).p}10*;

Somme des chiffres d’un entier

{0 {\.}{.10/\10%@+}while;}:sdc;

{.{.10%\10/sdc}0if+}:sdc;

Syracuse : Temps de vol

{0{\.(}{.2%\.3*)\2/if\)}while;}:syr;

{0{)\.2%\.3*)\2/if.(@\}do\;}:syr;

{{(}{3*).2%6\?/}/,}:syr;

Exemples supplémentaires (Project Euler)

P1 / Multiples de 3 ou 5 : https://projecteuler.net/problem=1

1000,.{3%},{5%},-{+}*

1000, : Création de la liste 0 à 999
. : On duplique la liste
{3%}, : On filtre ceux qui ont un reste non nul en les divisant par 3
{5%}, : Et ceux qui ont un reste non nul en les divisant par 5
- : On garde les multiples de 3 ou 5
{+}* : Réduction par la somme

P2 / Nombres de Fibonacci pairs : https://projecteuler.net/problem=2

0 1{2000.*<}{.@+}/\;.{2%},-{+}*

0 1 : On place 0 et 1 sur la pile
{2000.*<}{}/ : Tant que l'on ne dépasse pas 4 millions, ajout ds tableau
.@+ : Nombre Fibonacci suivant (a b -> a b b -> b b a -> b b+a)
\; : On garde uniquement le tableau
.{2%},- : On duplique le tableau et on ne garde que les nombres pairs
{+}* : Réduction par la somme

P6 / Sum square difference : https://projecteuler.net/problem=6

101,(;.{+}*2?\{2?}%{+}*-

101, : [0 ... 100]
( : supprime le 1er élément et le met à la fin [1 ... 100] 0
;. : supprime le 0 et duplique le tableau
{+}*2? : réduction par la somme de l'autre tableau puis carré
\{2?}%{+}* : SWAP, met chaque élément au carré puis réduction par la somme
- : Différence des 2 valeurs

Recherches personnelles

Palindrome

{.,1>{(\)@={pal}0if}1if\;}:pal;

"ABBA" pal
donne 1
"ABBCA" pal
donne 0

Python & Cryptographie partie 5 : Autoclave

def grille(a, b):
    return chr(65 + (ord(a) + ord(b)) % 26)

def invGrille(a, b):
    return chr(65 + (ord(a) - ord(b)) % 26)

def chiffreAutoclaveV1(phrase:str, cle:str):
    phrase = phrase.upper()
    chiffre = ''
    for c in phrase:
        cle = grille(c, cle)
        chiffre += cle
    return chiffre

def dechiffreAutoclaveV1(chiffre:str, cle:str):
    clair = ''
    for c in chiffre:
        clair += invGrille(c, cle)
        cle = c
    return clair    
  
phrase = 'OPERATIONURGENTE'
cle = 'K'
chiffre = chiffreAutoclaveV1(phrase, cle)
print('Chiffrée :', chiffre)
print('Déchiffrée :', dechiffreAutoclaveV1(chiffre, cle))


def chiffreAutoclaveV2(phrase:str, mot:str):
    phrase = phrase.upper()
    cle = mot.upper() + phrase
    chiffre = ''
    for i, c in enumerate(phrase): chiffre += grille(c, cle[i])
    return chiffre

def dechiffreAutoclaveV2(chiffre:str, mot:str):
    clair = ''
    cle = mot
    for i, c in enumerate(chiffre):
        lettre = invGrille(c, cle[i])
        clair += lettre
        cle += lettre
    return clair

phrase = 'MESSAGEIMPORTANTPOURNESTORLAGIRAFEAUNLONGCOU'
mot = 'POIRIER'
chiffre = chiffreAutoclaveV2(phrase, mot)
print('Chiffrée :', chiffre)
print('Déchiffrée :', dechiffreAutoclaveV2(chiffre, mot))

Python & Cryptographie partie 4 : Chiffre de Pollux

from random import choice

# Initialisation des variables
codes = ".-+-...+-.-.+-..+.+..-.+--.+....+..+.---+-.-+.-..+--+-.+---+.--.+--.-+.-.+...+-+..-+...-+.--+-..-+-.--+--..".split("+")
chiffres = [chr(v) for v in range(48, 58)]
alphabet = [chr(v) for v in range(65,91)]
alphanum = chiffres + alphabet
morse = dict(zip(alphabet, codes))

def dicoChiffrement():
 dico = {'.':[], '-':[], '+':[]}
 cles = '.-+'
 possibilites = list(alphanum)  # Copie de alphanum
 for i in range(36):
  valeur = choice(possibilites)
  possibilites.remove(valeur)
  dico[cles[i % 3]].append(valeur)
 return dico

def chiffrePollux(phrase:str):
 phrase = phrase.upper()
 dico = dicoChiffrement()     # Création du dictionnaire
 chiffre = ''
 for c in phrase:
  if 'A' <= c <= 'Z':
   codeMorse = morse[c] + '+'
   codageLettre = ''
   for v in codeMorse:
    codageLettre += choice(dico[v])
   chiffre += codageLettre
 return chiffre, dico    
  
def dechiffrePollux(chiffre:str, dico):
 # Traduction chiffré vers Morse
 enMorse = chiffre.upper()
 for k, v in dico.items():
  for c in v: enMorse = enMorse.replace(c, k)
 # Traduction Morse vers message clair
 clair = ''
 cleMorse = list(morse.keys())
 valMorse = list(morse.values())
 for m in enMorse.split('+'):
   if m != '':
    clair += cleMorse[valMorse.index(m)]
 return clair 

def enColonne(chiffre):
 for i in range(0, len(chiffre), 5):
  if i % 4 == 0 and i > 0: print()
  print(chiffre[i:i + 5], end='\t')
  
message = 'rendezvouscesoiravingtheuresplacevictorhugo' 
chiffre, dico = chiffrePollux(message)
print('Texte chiffré :')
enColonne(chiffre)
print('\nDictionnaire utilisé :')
print(dico)
print('Déchiffrement :')
print(dechiffrePollux(chiffre, dico))

Python & Cryptographie niveau lycée : Utilisation d’une grille

Codes de la vidéo

from random import randint

def grilleVide():
 grille = []
 for i in range(6):
  ligne = []
  for j in range(6): ligne.append('⬛')
  grille.append(ligne)
 return grille

def rotate(m, n):
 for _ in range(n): m = list(zip(*m))[::-1]
 return m

def afficher(g):
 for l in range(6): print(''.join(g[l]))

def creerGrille():
    grille = grilleVide()
    m = [[6 * l + c for c in range(6)] for l in range(6)]
    for c in range(3):
     for l in range(3):
      m = rotate(m, randint(0, 3))
      pos = m[l][c]
      x, y = pos % 6, pos // 6      
      grille[y][x] = '⬜'
    return grille
    
def completeEtoiles(phrase):
 return phrase + "*" * (9 - len(phrase) % 9)

def listeTrous(g):
 trous = []
 for l in range(6):
  for c in range(6):
   if g[l][c] == '⬜': trous.append(6 * l + c)
 return trous  

def chiffrer(phrase, grille):
 phrase = completeEtoiles(phrase)
 chiffre = grilleVide()
 trous = listeTrous(grille)
 for i, v in enumerate(phrase):
  pos = i % 9 
  if pos == 0 and i > 0:
   grille = rotate(grille, 1)
   trous = listeTrous(grille)
  x, y = trous[pos] % 6, trous[pos] // 6
  if v == "*": v = chr(randint(65, 90))
  chiffre[y][x] = v
 return chiffre 

def dechiffrer(crypte, grille):
 clair = ''
 trous = listeTrous(grille)
 for i in range(36):
  pos = i % 9 
  if pos == 0 and i > 0:
   grille = rotate(grille, 1)
   trous = listeTrous(grille)
  x, y = trous[pos] % 6, trous[pos] // 6
  clair += crypte[y][x]
 return clair
 
grille = creerGrille()        
afficher(grille)
phrase='RENDEZVOUSAQUINZEHEURESPLACELECLERC'
crypte = chiffrer(phrase, grille)
afficher(crypte)
clair = dechiffrer(crypte, grille)
print(clair)
  

Python & Cryptographie niveau lycée : Chiffrement par substitution

# Substitution simple

def subSimple(phrase:str, decalage:int):
    phrase = phrase.upper()
    chiffrement = ''
    for c in phrase:
        if 'A' <= c <= 'Z':
            position = (ord(c) - 65 + decalage) % 26
            chiffrement += chr(65 + position)
        else:
            chiffrement += c
    return chiffrement

# Substitution double (avec clé numérique)

def subDouble(phrase:str, decalage):
    phrase = phrase.upper()
    chiffrement = ''
    longueur = len(decalage)
    for i,c in enumerate(phrase):
        if 'A' <= c <= 'Z':
            position = (ord(c) - 65 + decalage[i % longueur]) % 26
            chiffrement += chr(65 + position)
        else:
            chiffrement += c
    return chiffrement            

# Substitution avec mot-clé

def fabriqueAlphabet(cle:str):
    lettres = list(cle.upper()) + [chr(v) for v in range(65, 91)]
    alphabet = ''
    for c in lettres:
        if c not in alphabet: alphabet += c
    tailleCle = len(set(cle))
    alphabetFinal = ''
    for col in range(tailleCle):
        suiv = col
        while suiv < 26:
         alphabetFinal += alphabet[suiv]
         suiv += tailleCle       
    return alphabetFinal

def subCleSimple(phrase:str,cle:str):
    alphabet = fabriqueAlphabet(cle)
    phrase = phrase.upper()
    chiffrement = ''
    for c in phrase:
        if 'A' <= c <= 'Z':
            chiffrement += alphabet[ord(c) - 65]
        else:
            chiffrement += c
    return chiffrement 

Python & Cryptographie niveau lycée : Déchiffrement des substitutions

def decryptSimple(phrase:str):
    freq = [0] * 26
    for c in phrase:
     if 'A' <= c <= 'Z':
         freq[ord(c) - 65] += 1
    decal = 4 - freq.index(max(freq))
    return subSimple(phrase, decal)

def decryptDouble(phrase:str, taille:int):
    clair = ''
    res = []
    for i in range(taille):
     res.append(decryptSimple(phrase[i::taille]))
    return ''.join(''.join(v) for v in zip(*res))

def inverseAlphabet(chiffre:str, clair:str):
    alphabetDecode = ''
    for i in range(65, 91):
      c = chr(i)
      if c in chiffre: code = clair[chiffre.index(c)]
      else: code = '?'
      alphabetDecode += code     
    return alphabetDecode 

def decryptEnPartie(phrase:str,alphabet:str):
    alphabet = alphabet.upper()
    phrase = phrase.upper()
    chiffrement = ''
    for c in phrase:
        if 'A' <= c <= 'Z':
            chiffrement += chr(65 + alphabet.index(c))
        else:
            chiffrement += c
    return chiffrement 

Exemples de textes :

clair = 'CHOISISSEZUNTRAVAILQUEVOUSAIMEZETVOUSNAUREZPASATRAVAILLERUNSEULJOURDEVOTREVIE'
# Clé numérique [3, 2, 1, 0]
crypte = 'FJPIVKTSHBVNWTBVDKMQXGWOXUBIPGAEWXPUVPBUUGAPDUBTUCWALNMEUWOSHWMJRWSDHXPTUGWIH'

# Extrait du livre "Le petit Prince"

princeClair = 'LESGRANDESPERSONNESMONTCONSEILLEDELAISSERDECOTELESDESSINSDESERPENTSBOASOUVERTSOUFERMESETDEMINTERESSERPLUTOTALAGEOGRAPHIEALHISTOIREAUCALCULETALAGRAMMAIRECESTAINSIQUEJAIABANDONNEALAGEDESIXANSUNEMAGNIFIQUECARRIEREDEPEINTREJAVAISETEDECOURAGEPARLINSUCCESDEMONDESSINNUMERO1ETDEMONDESSINNUMEROLESGRANDESPERSONNESNECOMPRENNENTJAMAISRIENTOUTESSEULESETCESTFATIGANTPOURLESENFANTSDETOUJOURSETTOUJOURSLEURDONNERDESEXPLICATIONSJAIDONCDUCHOISIRUNAUTREMETIERETJAIAPPRISAPILOTERDESAVIONSJAIVOLEUNPEUPARTOUTDANSLEMONDEETLAGEOGRAPHIECESTEXACTMABEAUCOUPSERVIJESAVAISRECONNAITREDUPREMIERCOUPDŒILLACHINEDELARIZONACESTTRESUTILESILONESTEGAREPENDANTLANUITJAIAINSIEUAUCOURSDEMAVIEDESTASDECONTACTSAVECDESTASDEGENSSERIEUXJAIBEAUCOUPVECUCHEZLESGRANDESPERSONNESJELESAIVUESDETRESPRESCANAPASTROPAMELIOREMONOPINIONQUANDJENRENCONTRAISUNEQUIMEPARAISSAITUNPEULUCIDEJEFAISAISLEXPERIENCESURELLEDEMONDESSINNUMERO1QUEJAITOUJOURSCONSERVEJEVOULAISSAVOIRSIELLEETAITVRAIMENTCOMPREHENSIVEMAISTOUJOURSELLEMEREPONDAITCESTUNCHAPEAUALORSJENELUIPARLAISNIDESERPENTSBOASNIDEFORETSVIERGESNIDETOILESJEMEMETTAISASAPORTEEJELUIPARLAISDEBRIDGEDEGOLFDEPOLITIQUEETDECRAVATESETLAGRANDEPERSONNEETAITBIENCONTENTEDECONNAITREUNHOMMEAUSSIRAISONNABLE'

princeChiffre = 'OGTGUCODHUQEUUPNQGTMRPUCRPTELNMEGGMALUTEUFFCRVFLHUEEVUJNVFFSHTQEQVTBRCTOXXFRWUPUIGSMHUFTGGNIQVFRHUTEURMUWQUAOCHERISASJJEDNIIVVPIUGBUFCMCXNFTDNBGUCNMDKSEFGTTDKOSLSVEMCJAECODRPOEDNBGHFFSLZBNVWOEPCHNLHJQXGDAUTJEUGEESGJNWTFJDXBIVGUEGGDOXTBGHRBROKOSXEDEVFFMRPEEVUJNQWNEUQ1EWFFMRPEEVUJNQWNEUQMEVISAQFFSSGSSRPOEVPFCROQRHPOEQVKAPCJSUKFNWQVTHUTEXNFSHVDEVVGAWKHAQVQOXTMEVGOFDPUSGGUOXLPUUUFTWQVJRWSSOGVRGQONHTEEVGYPOKDAWKPNVLBIGQOCGWDHRKTIUWOAXVSEPGUIHTFTMCJASRSIVCQIOQUEUFFSDXJOQUKALXPLHWOPHWQAUVPUWFBNVNFMRPEEHVMAJGPGUCQHLGDEVVFXDEUMDDFAXEPUSUFRYKKEVCWALUSEFQONDKURHFVPUGNIHTDOXREŒLNMAFJJNHFFLDTJZRPBCHUUTUGTUWKMEVKMOQGTTHIBRHRFNGCOTOCOULVKALCJNVKFUDWDOXTTDHOBVLGEEVVBSGGDOQVBCWUBVHEEEVVBSGGHEQUTEUKFUALBIEGBUFQVPYGDUFJFZOGTGUCODHUQEUUPNQGTJHNFSDKWUHUEEWTFSSTFSFCOASCTTUQQAPGMIRTFMRPPPLPJOQSVAQFKEQTFNFQOTUCJSXPFQXKNESCSALUTALVVNSGVLXEJDHLFFDKTALUMEARFRLGOCHUVRHNMEGGNOQFFSVKONXOFRR1RUHLBIWQVJRWSSFQOSHTWEMGWOXNBIVUBVRKSSLGMLHGUALVWRDKNEQVDOPRSEKGOSLXFMDKTTRWKOXTTEONFMHTFPRPEALVDEVVVNFJBPHCVAOQSSMGOEOWJPDTMALUOIGGTEURFNWUCODUOIGGGOUGUSYKFRJGTNLFFTRKMEVLFMHOFTWCJSDUBPRTUEHLFLXKQAUNBIVFFBUKEGHFFGRNGDHRPLLVJQXGFTGGDRDXBTHUFTOCHRDPEESGSSRPOEHVBIWDJEQEPNWGOTHFFCRPOALVSEXPIOPOFAXUTIUCJSRPOAENF'