```
import math
import numpy as np
############################################
class Intervalle:
"""Classe intervalle.
Les attributs définissant les bornes inférieure et supérieure sont cachés
et en lecture seule.
"""
def __init__(self, inf, sup):
"""Concstructeur d'intervalle.
Parameters
----------
inf: float, optionel
Borne inférieure.
sup: float, optionel
Borne supérieure.
"""
self.__inf = inf
self.__sup = sup
@property
def inf(self):
"""Accès à la borne inférieure.
"""
return self.__inf
@property
def sup(self):
"""Accès à la borne supérieure.
"""
return self.__sup
def __str__(self):
"""Chaîne de caractères représentant un intervalle.
"""
return '[{}, {}]'.format(self.inf, self.sup)
def __eq__(self, y):
"""Teste l'égalité de deux intervalles (mêmes bornes).
Parameters
----------
y: Intervalle
Returns
-------
True si les itervalles sont égaux: boolean
"""
return self.inf == y.inf and self.sup == y.sup
def __neg__(self):
"""Retourne l'opposé d'un intervalle.
Returns
-------
Opposé de l'intervalle: Intervalle
"""
return Intervalle(-self.sup, -self.inf)
def __add__(self, y):
"""Somme de deux intervalles.
Parameters
----------
y: Intervalle
Returns
-------
Somme de deux intervalles: Intervalle
"""
return Intervalle(self.inf + y.inf, self.sup + y.sup)
def __sub__(self, y):
"""Différence de deux intervalles. S'appuie sur add et neg.
Parameters
----------
y: Intervalle
Returns
-------
Différence de deux intervalles: Interval
"""
return self + (-y)
def __mul__(self, y):
"""Produit de deux intervalles.
Parameters
----------
y: Intervalle
Returns
-------
Produit des deux intervalles: Interval
"""
lst = [a*b for a in (self.inf, self.sup) for b in (y.inf, y.sup)]
return Intervalle(min(lst), max(lst))
def __truediv__(self, y):
"""Quotient de deux intervals. Une exception est levée si le
dénominateur contient 0.
Parameters
----------
y: Intervalle
Returns
-------
Quotient des deux intervalles: Interval
"""
# If 0 in the interval, at least one bound is infinite
if y.inf * y.sup <= 0:
raise Exception('Attention division par un intervalle contenant zéro')
else:
# y ne contient pas 0. On peut donc diviser self par y
lst = [a/b for a in (self.inf, self.sup) for b in (y.inf, y.sup)]
return Intervalle(np.nanmin(lst), np.nanmax(lst))
def __pow__(self, n):
"""Puissance entière d'un intervalle.
Parameters
----------
n: integer
Returns
-------
Puissance de l'intervalle : Intervalle
"""
lst = [self.inf**n, self.sup**n]
# Si la puissance est paire, cas particulier des intervalles contenant 0
if n % 2 == 0:
if self.inf * self.sup <= 0:
return Intervalle(0, max(lst))
return Intervalle(min(lst), max(lst))
def __contains__(self, val):
"""Teste si une valeur fait partie d'un intervalle.
Parameters
----------
val: float
Returns
-------
Vrai si `val` est dans l'intervalle: boolean
"""
return self.inf <= val <= self.sup
def width(self):
"""Largeur d un intervalle.
Returns
-------
la largeur de l intervalle: float
"""
return self.sup - self.inf
def mid(self):
""" Milieu de l intervalle.
Return
------
Le mileu de l intervalle: float
"""
return (self.inf+self.sup)/2
def sqrt(x):
r"""Extension de la fonction sqrt à un intervalle.
Parameters
----------
x: Intervalle
Returns
-------
Racine carrée de l'intervalle `x`: Intervalle
:math:`\sqrt{x}`
"""
# Cas particulier d'un intervalle négatif
if x.sup < 0:
raise Exception('Attention racine d un intervalle contenant négatif')
# Cas particulier de la borne inf négative
elif x.inf < 0:
return Intervalle(0, x.sup**0.5)
else:
return Intervalle(x.inf**0.5, x.sup**0.5)
def exp(x):
r"""Extension de la fonction exponentielle à un intervalle.
Parameters
----------
x: Intervalle
Returns
-------
Exponentielle de l'intervalle: Intervalle
:math:`e^x`
"""
return Intervalle(math.exp(x.inf), math.exp(x.sup))
def log(x):
r"""Extension de la fonction logarithme à un intervalle.
Parameters
----------
x: Intervalle
Returns
-------
Logarithme de l'intervalle : Intervalle
:math:`\log{x}`
"""
# Cas particulier de la borne inf négative ou nulle
if x.inf <= 0:
raise Exception('Attention logarithme d un intervalle contenant zéro')
else:
return Intervalle(math.log(x.inf), math.log(x.sup))
############################################
def branchandBound(f, I):
lst = [I] # initialisation de la liste avec l'intervalle en entier
ftilde = float('inf')
while len(lst) > 0:
X = lst.pop() # extraction de la tete de lst
borne = f(X) # calcul d'un minorant et d'un majorant de f sur X
# on enleve 1e-6 pour être sur de trouver vraiment une meilleur solution
if borne.inf < ftilde - 1.e-6:
# il est possible de trouver une meilleur solution dans X
mid = X.mid() # calcul du milieu de X
test = f(Intervalle(mid, mid)) # evaluation de f au point milieu
# nous somme obligé de faire ainsi car f retourne un intervalle
# et non un nombre flottant
test = test.mid()
if test < ftilde:
# on a trouvé une meilleur solution
ftilde = test # on stocke la nouvelle solution
xtilde = mid
X1 = Intervalle(X.inf, mid) # on decoupe X en deux intervalles
X2 = Intervalle(mid, X.sup)
# on ajoute X1 et X2 à la liste des intervalles restant à traiter
lst.append(X1)
lst.append(X2)
return xtilde, ftilde
def mafonction(x):
return x**5 + x + exp(x**2) - x*exp(x)
############################################
if __name__ == '__main__':
x1 = Intervalle(1, 2)
x2 = Intervalle(2, 6)
print(x1 * x2**2 - exp(x1 + x2))
x_sol, f_sol = branchandBound(mafonction, Intervalle(-1, 1))
print("Le minimum est atteint en ", x_sol)
print("La valeur du minimum de f sur [-1,1] est : ", f_sol)
```