GBlog -Everything is possible

Lundi 25 Juillet 2005

[Autres] - Anciens jeux

Le premier ordinateur qui entra chez moi fut un macintosh à l'époque où mon frère entrait au CP. Cela date de 1990 (Mince, cela ne me rajeunit pas...).

À cette époque, les rares fois ou je jouais, car je n'y avais pas le droit :), c'était à deux jeux. Dedalus et Spin doctor.

Dedalus

Une histoire dans l'espace où les personnages étaient de vrais acteurs filmés sur un bitmap. Si la qualité graphique ferait tache aujourd'hui, les énigmes étaient géniales. Je n'ai jamais pu le finir à cause d'un CD rayé...

Spin doctor

Un jeu trés simple dans le contexte et que je coderais surement en Python un de ces 4 si je ne trouve pas quelqu'un qui l'aurait déjà fait avant.

Vous êtes une barre qui peut tourner autours de points et qui peut changer de point quand elle passe près d'un autre (ou rebondir et ainsi de suite).

Le but est d'aller du début à la fin. Or le problème c'est qu'il y a des mines, des murs, des bombes, des barres ennemies...

Si vous avez des nouvelles de ces jeux, surtout de Dedalus, et que vous savez où je pourrais les retrouver, dites le moi.

[ 4 commentaires ]

Jeudi 21 Juillet 2005

[Python] - Vers l'infinie et au delà

Aujourd'hui Python nous démontre que à l'égal de Buzz l'eclair, il peut se rendre au delà de l'infinie.

Petit rappels mathématiques

La fonction exponentielle de base e (noté exp) n'est en fait que le nombre e à la puissance x. Ce nombre e est environ égal à 2.71 mais il s'agit d'un nombre irrationnel, donc sa forme exacte n'est pas connue (du même style que pi ou racine de 2).

Mais comment e est-t-il définie alors ? Il s'agit de la limite en l'infinie d'une suite croissante définie par :

u_n = (1 + 1 / n) ^ n

Comme je suis dans mes bonnes heures, je vous le démontre, les non-matheux peuvent passer ce paragraphe.

(1 + 1/n) ^ n = exp(ln((1 + 1/n) ^ n)) = exp(nln(1+1/n))

Or quand n tant vers l'infinie, 1/n tant vers 0. On a donc ln(1+u) avec u qui tend vers 0. On peux donc remplacer ln(1+u) par son développement limité en 0 que l'on gardera à l'ordre 1. ln(1+u) = u + quelque chose de négligeable devant u.

Bref, on obtient donc exp(nu). Or u = 1/n donc on a exp(1) = e

Là où Python pèche

Après ce cours mathématique qui à sans doute fasciné tous le monde, voici ce qui se passe de comique avec python.

from future import divison # on évite les arrondie avec la division
import math

n = 166111400
print math.e - (1 + 1/n) ** n

On obtient une valeur négative. (n peut varié en fonction de votre version de Python, de GCC ou de l'OS...) Or on sais que math.e = limite (1 + 1/n) ** n quand n tant vers l'infinie. On a démontré que cette valeur était plus petite que (1 + 1/n) ** n avec n = 166111400. La suite étant strictement croissante, on à le droit d'en déduire que 166111400 > l'infinie.

Je vous laisse méditer cette joyeuseté et je prie le ciel de m'apporter le LaTeX dans l'html sous peu.

[ 9 commentaires ]

[Python] - Math & Python

Aujourd'hui, grand coup de nettoyage dans mon /home. Je me retrouve avec des bouts de scripts python qui en intéresseront sûrement certains. Malheureusement pour d'autres, je passe la plus part de mon temps à implanter des algorithmes mathématiques en Python, donc il s'agit de petit trucs mathématique pas forcement utiles.

Conversion de base

Il peut être utile de convertir un nombre d'une base numérique vers une autre. Or il n'existe pas de fonction prévu à cet effet en Python hormis la fonction int capable de convertir un nombre de n'importe quel base (entre 2 et 32) vers 10. Cependant pour convertir dans une autre base il faut le faire à la main. En partant de la base 10, l'on peut facilement ce rendre dans les autres bases.

Explication mathématique

Un nombre dans une base s'écrit comme une somme de termes de la forme ab^c. Avec a une valeur en base 10 inférieure à la base. b la valeur de la base et c un exposant qui varie. Par exemple, 7 en binaire s'écrit 1 * 2 ^ 2 + 1 * 2 ^ 1 + 1 * 2 ^ 0 = 4 + 2 + 1. C'est ainsi qu'a partir de divisions l'on peut trouver l'écriture dans n'importe quel base d'un nombre.

from string import lowercase

def base_convert(number,frombase,tobase):

   assert isinstance(number,str)
   assert isinstance(frombase,int)
   assert isinstance(tobase,int)

   assert 2 <= frombase <= 36
   assert 2 <= tobase <= 36

   if frombase == tobase:
       return number

   # on met en base 10
   base10 = int(number,frombase)

   if tobase == 10:
       return str(base10)
   else:
       # on met dans la nouvelle base
       i = 0
       while tobase ** (i + 1) <= base10:
           i += 1
       newbase = []
       while i >= 0:
           j = 0
           while ((j+1) * tobase ** i  <= base10) and (j < (tobase - 1)):
               j += 1

           base10 = base10 - j * (tobase ** i)
           if j < 10:
               newbase.append(str(j))
           else:
               newbase.append(lowercase[j - 10])
           i -= 1

       return "".join(newbase)

Cos et sin sans math

Cela ne sert à rien, mais voici comment obtenir des valeurs corrects de cos et sin sans la librairie math :

e = 2.7182818284590451

def cos(x): return (e ** (1j * x)).real
def sin(x): return (e ** (1j * x)).imag
Petit rappels mathématiques.

Un nombre complexe d'argument x est définit tel que Z = exp(j * x) = cos(x) + isin(x). Sa partie entière est donc égale à cos(x) et sa partie imaginaire égale à sin(x).

Valeur de pi

Vous vous êtes toujours demandé comment l'on calculait la valeur de Pi avec des milliers de décimales. C'est en fait quelque chose de fort simple et en voici une méthode. Je ne dis pas que c'est la meilleur et les résultats donnés ici sont loin d'être bon du fait de l'imprécision des nombres flottants de python (Il faudrait utiliser les Decimal) mais c'est un exemple sympa qui comporte sûrement une petite erreur...

from future import division
size = 1000
nbhit = 0
nbgood = 0
for x in xrange(size + 1):
   for y in xrange(size + 1):
       nbhit += 1
       if ((size / 2 - x) ** 2 + (size / 2 - y ) ** 2) <= (size / 2) ** 2:
           nbgood += 1

print nbgood / nbhit * 4
Explication mathématique

L''air d'un cercle est égale à Pi produit de son rayon au carré. Donc un cercle inscrit dans un carré couvre une aire de Pi * r ** 2 alors que le carré a une air de 4r**2. On obtient donc un ratio entre les deux airs égale à Pi / 4.

On peut donc calculer Pi si l'on connaît le ratio des airs.

Produit cartésien

Un produit cartésien donne à partir de plusieurs liste toutes les combinaisons possibles avec les valeurs.

Le plus couramment c'est utilisé pour parcourir un tableau à plusieurs dimension et cela donne quelque chose très basique comme cela :

for x in range(10):
   for y in range(10):
       for z in range(10):
           print x,y,z

Cependant on se rend vite compte que c'est extrêmement lourd. L'on peut simplifier l'expression avec une liste :

for x,y,z in [(i,j,k) for i in range(10) for j in range(10) for k in range(10)]:
   pass

Vous noterez la légèreté de l'ensemble, surtout quand on augmente en dimension.

C'est pourquoi la fonction suivante simplifie la lisibilité et marche avec un nombre indéterminée de liste :

def prod(*args):
   if len(args) == 1: return [(x,) for x in args[0]]
   return [(i,) + j for j in prod(*args[1:]) for i in args[0]]

for x,y,z in prod(range(10),range(10),range(10)):
   pass

La fonction est particulièrement simple et totalement illisible car il s'est agit pour moi de faire un défi une ligne, mais vous pouvez la dérouler pour obtenir quelque chose de plus lisible.

Combinaisons

Soit une liste d'éléments. Comment obtenir toutes les combinaisons possibles avec ces éléments ? La solution est rudement intéressant d'un point de vu logique :

items = ('a','b','c')
sol = []
for i in range(2 ** len(items)):
   sol.append([item for j,item in enumerate(items) if (i >> j) &1])
print sol

L'explication est plus tordue par contre :(

Tout d'abord, combien de combinaisons sont possibles ? 2 ** len(items). Nous allons donc attribuer à chacune de ces combinaisons un nombre. Dans l'exemple ici celui-ci va de 0 à 7.

L'astuce ici consiste à compter en binaire jusqu'a 7 et à considérer en gardant l'ordre de la liste, que si l'on rencontre un bit vrai, la valeur sera dans la liste, sinon non :

0 000 _
1 001 c
2 010 _b_
3 011 _bc
4 100 a__
5 101 a_c
6 110 ab_
7 111 abc

Pour cela l'on va utiliser la liste [item for j,item in enumerate(items)] qui dans le cas présent énumère tous les éléments de la liste en mettant dans la valeur de j la position de chaque élément. Le teste conditionnel suivant est composé de deux opérateurs binaires. Le premier, >> consiste en un décalage. C'est à dire que l'on prend le nombre binaire et qu'on le tronque de certains bits à la fin. Ainsi en décalant à chaque fois d'un cran, on pourra obtenir tous les bits qui forment notre nombre en bit de poids faible (le plus petit).

Il suffit alors d'utiliser l'opérateur & logique qui renvoie un nombre binaire avec des 1 là où tous les bits des deux nombres sont à 1 et des 0 sinon. Comme le nombre de droite est 1, il n'a qu'un seul bit positif, le bit de poids faible. Ainsi nous renvoyons un binaire qui a tous ses bits à zéros, sauf le bit de poids faible qui dépend de la valeur du bit de poids faible de notre autre nombre.

Désolé pour la présentation médiocre, je suis en plein changements dans le blog et voila... Vivement LaTeX dans l'html.

[ 2 commentaires ]

Jeudi 7 Juillet 2005

[Cinéma] - Sin City

Les vacances sont enfin là et avec l'envie d'aller au cinéma. Il faut dire qu'avec les examens, le temps que j'ai pu consacrer au septième art s'est retrouvé fortement limité. Je suis donc aller voir Sin City.

Le film commence et rapidement l'on rentre dans l'ambiance sombre et pervertie. Lorsque le titre s'affiche lors du générique, on sent déjà que l'on a affaire à un film de bonne qualité.

L'action sera séparée en trois parties, qui se recoupe quelque peu. On entrera ainsi dans la tête de Marvin, un ex-taulard sur-homme tombé amoureux d'une prostitué assassinée et qui cherchera à assouvir sa vengeance. Puis ce sera au tour d'un tueur se battant pour la liberté des prostituée. On finir sur un Bruce Willis en flic protecteur d'une petite fille.

Si l'histoire est plutôt basique, elle traite des sujets très fort comme la vengeance, la liberté ou l'amour.

Mais ce qui fait la richesse de ce film ce n'est pas son histoire, c'est sa réalisation. L'ambiance glauque de la BD y est très bien rendue grâce à des décors en images de synthèses pour la plus part, mais d'un réalisme incroyable.

Les acteurs incarnent bien leurs rôles (Même Jessica Alba que je appréciais peu se montre à la hauteur).

Une particularité du film, la couleur n'est présente qu'a certains moments pour souligner un point important d'un personnage tel que les cheveux blonds de Goldy, les yeux bleus magnifique d'une des prostituée ou des chaussures rouges. Sans oublié le sang qui est rouge (ou jaune, ou blanc) en fonction du contexte.

Bien que la violence soit exacerbée, cela passe plutôt bien du fait de l'exagération extrême. Il reste quand même certains passages qui font mal aux tripes alors que d'autres font rires (j'ai particulièrement aimé la façon dont Marve torture un homme en voiture ;o)

On retrouve un certain Tarantino dans la réalisation et comme à l'habitude c'est un bonheur. Mention toute particulière pour la prostituée asiatique qui manie le sabre.

Malheureusement, je pense quand même qu'il s'agit d'un film à visées purement masculine. Baston, voiture et créatures de rêves en tenue légères. Votre avis mesdames ?

En conclusion, un film de très bonne qualité qui nous change de nos marques trop précises en matière de cinéma. Je recommande.

[ 0 commentaire ]

[Python] - Décorateurs

Mon apprentissage du python avance grandement et je crois qu'aujourd'hui j'ai enfin compris un point très important de la programmation orientée objet. Je vais donc essayer de vous en faire part.

Présentation

Un des buts de la POO est de faire du code réutilisable. Bref en pratique il convient de faire des classes globales qui ne prennent en compte que le cas général. Puis l'on crée des classes qui héritent de notre classe globale tout en ajoutant des méthodes ou des attributs.

Par exemple, vous avez la classe voiture, qui contient un moteur, des roues et une certaine quantité d'essence. Vous avez aussi la classe 4x4 qui est une voiture à laquelle ont ajoute les quatre roues motrices ou la classe luxe qui propose un lecteur DVD dans les appuis-têtes.

Jusque là tout était rose dans le magnifique monde de la POO. Cependant que ce passe-t-il quand votre 4x4 est un 4x4 de luxe ? Vous aller créer une nouvelle classe qui dérive des trois première ? C'est encore faisable. Mais imaginons soudain que vous vous fassiez voler le lecteur DVD. Votre 4x4 n'est plus de Luxe, il faut donc pouvoir remédier à cela.

C'est là que les décorateurs interviennent. Il s'agit en fait de redéfinir une fonction au sein même d'une classe après avoir passée celle-ci dans une fonction de décoration. L'exemple suivant vous en dira plus :

class Voiture:
   def qui_suis_je(self):
       print "---"
       print "Je suis une voiture"

def x4x(fonction):
   def nouvelle_fonction():
       fonction()
       print "Je suis un 4x4"
   return nouvelle_fonction

def luxe(fonction):
   def nouvelle_fonction():
       fonction()
       print "Je suis de luxe"
   return nouvelle_fonction

car = Voiture()
car.quisuisje()
# >>> Je suis une voiture

car.quisuisje = x4x(car.quisuisje)
car.quisuisje()
# >>> Je suis une voiture
# >>> Je suis un 4x4

car.quisuisje = luxe(car.quisuisje)
car.quisuisje()
# >>> Je suis une voiture
# >>> Je suis un 4x4
# >>> Je suis de luxe

del car.quisuisje
car.quisuisje()
# >>> Je suis une voiture

# Ajout du décorateur 4x4. 
# Le lecteur DVD à été volé, 
# ce n'est pas une raison pour 
# perdre les roues motrices
car.qui_suis_je = x4x(car.qui_suis_je)
car.qui_suis_je()
# >>> Je suis une voiture
# >>> Je suis un 4x4

Bref, je pense que vous avez compris le concept. Malheureusement il n'y a aucune technique toute faite pour enlever simplement un décorateur. Il faut donc tous les enlever via la méthode del et puis rajouter ceux que l'on veut après. Ce n'est pas très pratique et devient vite lourd dans le code.

Solution

Mais heureusement il y a toujours des solutions et donc le but va être de créer des fonctions pour automatiser tout cela. Un charmant monsieur de #python@irc.freenode.net m'a proposé le script qui suit et j'avoue qu'il était bien meilleur que le miens, donc je le garde.

class Decorable:

  def init(self, func):
      self.decorators = []
      self.original = func
      self.decorated = func

  def call(self, *args, **kw):
      self.decorated(*args, **kw)

  def decorate(self, decorator):
      self.decorated = decorator(self.decorated)
      self.decorators.append(decorator)

  def undecorate(self, decorator=None):
      if decorator:
          self.decorators.remove(decorator)
      else:
          self.decorators = []
      self.decorated = reduce(lambda decorated, decorator:
          decorator(decorated), self.decorators, self.original)

Reprenons notre classe de tout à l'heure et voyons comment procéder.

car = Voiture()
car.qui_suis_je = Decorable(car.qui_suis_je)

car.qui_suis_je.decorate(x4x)
car.qui_suis_je.decorate(luxe)

car.qui_suis_je()
# >>> Je suis une voiture
# >>> Je suis un 4x4
# >>> Je suis de luxe

car.quisuisje.undecorate(luxe)
car.quisuisje()
# >>> Je suis une voiture
# >>> Je suis un 4x4

C'est mieux non ?

Bref, cette façon de faire ouvre des perspectives plutôt intéressantes car elle permet de vraiment faire du code totalement modulable.

[ 2 commentaires ]