random_sequence()

Jeudi 28 octobre 2004 20:09 - Code

Où l'auteur se prend le chou sur la réécriture de la fonction dont le nom a servi de titre.

Ladite fonction a une fonction (ça tombe bien) nette et précise, retourner une chaîne de caractères (de taille n) contenant des caractères hexadécimaux aléatoires.

L'implémentation d'origine était grosso modo la suivante:

char* random_sequence(int n)
{
      int i, val;
      char *s;

      s = malloc(n+1);
      for (i=0; i<n; i++) {
              val = rand() & 0xf;
              if (val < 10) {
                      s[i] = 48 + val;
              } else {
                      s[i] = 65 + val-10;
              }
      }
      s[n] = 0;

      return s;
}

J'étais un peu ennuyé par tous les appels à rand(); cette fonction retourne un entier qui fait quand même une floppée de bits et il n'en était gardé que quatre (c'est ce que fait le & 0xf, la version originale utilise en fait la GLib qui vient avec une fonction retournant un entier entre deux bornes); dommage, il fallait trouver un moyen de les utiliser tous.

Autre idée; pourquoi coder la conversion du nombre en caractère hexa ? Pourquoi ne pas utiliser sprintf() ?

Tsk. Mauvaise idée. sprintf est un gouffre. Voyons quand même.

char* random_sequence(int n)
{
      char *s, *t;

      s = t = malloc(n+sizeof(int)+1);
      while (t-s < n) {
              sprintf(t, "%0X", rand());
              t += sizeof(int);
      }
      s[n] = 0;

      return s;
}

C'est tout joli mais remplacer quelques appels à rand() par un peu moins d'appels à sprintf, ça ne mène nulle part.

Virer sprintf, donc.

char* random_sequence(int n)
{
      char *s, *t;
      int rnd, i;

      s = t = malloc(n+sizeof(int)+1);
      while (t-s < n) {
              rnd = rand();
              for (i=0; i<sizeof(int); i++) {
                      *(t++) = '0' + ((rnd>>i*4)&0xf);
                      if (*(t-1) > '9') *(t-1) += 7;
              }
      }
      s[n] = 0;

      return s;
}

Et hop. Il reste à en taper un petit million (de taille 60) et à mesurer le temps que ça prend (via time, quelques appels et une moyenne).

  • Version originale: 3,314s
  • Version "sprintf": 8,689s (wouh, 2,6x plus lent)
  • Version finale: 1,996s (1,6x plus rapide)

Ce n'est pas négligeable mais la lisibilité du code souffre un peu, je ne sais qu'en faire.