OVH Cloud OVH Cloud

probleme de realloc

18 réponses
Avatar
Nicolas
Bonjour,

Je développe actuellement une librairie pour manipuler des tas.
Pour profiter pleinement des algorithmes déjà découvert en O(log(n)),
j'ai besoin d'un tableau d'inverses (tableau qui stocke le rang de
l'element du tas) pour avoir un accès direct à la mémoire sans parcourir
tout le tas.

Mon problème réside dans l'allocation de ce tableau car il est parfois
tronqué par le début. En effet, realloc permet de rappetisser ou
d'agrandir le tableau par la fin de celui-ci. Mais je voudrais le faire
aussi par le début.

Ex:

1 | 3 | 9 | 6 | 7

insertion par le debut: 55

55 | 1 | 3 | 9 | 6 | 7

Je pourrais bien évidemment realloquer le tableau et recopié toutes les
valeurs mais je perdrais l'avantage des algorithmes en O(log(N))

Comment faire?

Merci
Nicolas

8 réponses

1 2
Avatar
Nicolas
Marc Boyer wrote:
Que tant que tu nous auras pas expliqué précisement tes
besoins et seulement des sous-parties (j'ai toujours pas
compris précisément le principe des indices inverses), on
aura du mal à trouver une solution optimale.
Désolé si je me suis mal expliqué!

Je reprends point par point.

Tout d'abord, c'est un simulateur à évenements discrets.
Comme dans tout simulateur, on traite des évenements qui vont en
engendrer d'autres (c'est un écheancier).
On a donc besoin de traiter une liste d'évenements toujours triés par
ordre croissant.

J'avais au départ fait une liste chainée simple, ainsi, l'insertion se
faisait en O(n) et l'extraction en 1 (On prend toujours la tête de la
liste qui est l'évenement de plus petite date).

Je me suis ensuite souvenu, qu'une structure de donnée en tas (arbre
binaire dont tous les noeuds ont leurs deux fils supérieurs ou égals)
permettait d'insérer des éléments dans une liste de manière triés en
O(log(n)).

Le problème c'est que mon simulateur peut génèrer plusieurs évenements
à une date T identique. Mon insertion est alors en O(log(n)) ou n est
le nombre de dates. Je me suis alors dit qu'il serait plus performant
d'avoir un algo en O(log(n)) ou n est le nombre de dates distinctes.

Pour ce faire, il faut connaître l'emplacement dans le tas des dates
déjà insérées, d'où l'idée de faire un tableau d'inverse, c'est-à-dire
un tableau où les "numéros des cases" sont des dates et ou le contenu
des cases est un rang dans le tableau du tas. Ainsi si j'insere un
evenement e, je pourrais vérifier si il a déjà été inséré dans le tas
en faisant inverse[e->date].
Le tableau d'inverse doit donc être de taille date_max (la plus grande
date que contient le tas).

Exemple:
-------

soit le tas:
-------------------------
T= | 3 | 5 | 4 | 6 | 7 | 9 |
-------------------------

L'arbre correspondant:
3
5 4
6 7 9


Et le tableau d'inverses:
---------------------------------------------
inverse= | -1 | -1 | -1 | 0 | 2 | 1 | 3 | 4 | -1 | 5 |
---------------------------------------------
où -1 correspond à une date absente du tas.

Donc si je veux insérer un évenement e de date 5, je peux connaître
son rang dans le tas (tableau T) en faisant inverse[e->date] et
j'obtiens 1. Si j'obtient -1, je l'insère normalement en mettant à jour
le tableau d'inverses.


Jusque là, tout est simple, pour une date énorme, le tableau d'inverses
est gigantesque! Pour le minimiser, on peut allouer que
date_max-date_min cases et y accéder en faisant :
inverse[e->date-date_min]

Le problème est alors que lors d'une extraction de la racine (je prends
toujours la date la plus petite), je dois realloc le tableau d'inverse
pour supprimer cette racine qui est partie. C'est-à-dire que je dois
enlever la première case mais le realloc ne permet pas cela. Il permet
seulement de tronquer sur la fin du tableau (et surtout de libérer
l'espace mémoire ne servant plus).


Est-ce plus clair ainsi?


Rapidement, une table de hashage, ça a plusieurs inconvénients:
1) tu perds l'ordre, donc, dans un simu à événement discrets,
tu ne peux pas connaitre sans parcours total la prochaine
date à lequelle un év. va se produire
2) ça se redimensionne pas bien si je me souviens bien
J'ai laissé tomber, trop peu d'avantages comme tu l'as dit...


@+
Nico

Avatar
Jean-Marc Bourguet
Nicolas writes:

Marc Boyer wrote:
Que tant que tu nous auras pas expliqué précisement tes
besoins et seulement des sous-parties (j'ai toujours pas
compris précisément le principe des indices inverses), on
aura du mal à trouver une solution optimale.
[...]


Est-ce plus clair ainsi?


Ayant compris ton probleme, je crois que je ferais une des deux choses
suivantes (le choix se faisant d'apres la probabilite d'avoir beaucoup
d'evenements a une meme date et des contraintes annexes -- est-ce que
des evenements pour la meme date doivent etre traite dans un ordre
defini ou pas):
- simplement le tas
- le tas + une table de hachage/arbre balance/BTree/...

Rapidement, une table de hashage, ça a plusieurs inconvénients:
1) tu perds l'ordre, donc, dans un simu à événement discrets,
tu ne peux pas connaitre sans parcours total la prochaine
date à lequelle un év. va se produire



De toute maniere l'ordre est maintenu pas un tas.

2) ça se redimensionne pas bien si je me souviens bien



Ah bon, toutes mes tables de hachages sont auto-redimentionnables.

J'ai laissé tomber, trop peu d'avantages comme tu l'as dit...


Un avantage est qu'avec une table de hachage, tu ne prends rien comme
place pour les dates sans evenement.

A+

--
Jean-Marc
FAQ de fclc: http://www.isty-info.uvsq.fr/~rumeau/fclc
Site de usenet-fr: http://www.usenet-fr.news.eu.org


Avatar
Marc Boyer
In article <41e6c058$0$6637$, Nicolas wrote:
Marc Boyer wrote:
Que tant que tu nous auras pas expliqué précisement tes
besoins et seulement des sous-parties (j'ai toujours pas
compris précisément le principe des indices inverses), on
aura du mal à trouver une solution optimale.
Désolé si je me suis mal expliqué!

Je reprends point par point.

Tout d'abord, c'est un simulateur à évenements discrets.
Comme dans tout simulateur, on traite des évenements qui vont en
engendrer d'autres (c'est un écheancier).
On a donc besoin de traiter une liste d'évenements toujours triés par
ordre croissant.

J'avais au départ fait une liste chainée simple, ainsi, l'insertion se
faisait en O(n) et l'extraction en 1 (On prend toujours la tête de la
liste qui est l'évenement de plus petite date).


As-tu besoin de faire un accès (en lecture et/ou modification)
à un événement donné (et avec quel critère ?).

Car avec ce que tu présentes de contrainte, je ferais un tas
de files d'événements (dans une file, tous les événements ayant
lieu à la même date).

Je me suis ensuite souvenu, qu'une structure de donnée en tas (arbre
binaire dont tous les noeuds ont leurs deux fils supérieurs ou égals)
permettait d'insérer des éléments dans une liste de manière triés en
O(log(n)).

Le problème c'est que mon simulateur peut génèrer plusieurs évenements
à une date T identique. Mon insertion est alors en O(log(n)) ou n est
le nombre de dates. Je me suis alors dit qu'il serait plus performant
d'avoir un algo en O(log(n)) ou n est le nombre de dates distinctes.


Avec ce que tu présentes de contrainte, je ferais un tas
de files d'événements (dans une file, tous les événements ayant
lieu à la même date).
Ansi, on garde l'insertion en O(log(n)), et l'extraction en
tête en O(1).

Pour ce faire, il faut connaître l'emplacement dans le tas des dates
déjà insérées, d'où l'idée de faire un tableau d'inverse, c'est-à-dire
un tableau où les "numéros des cases" sont des dates et ou le contenu
des cases est un rang dans le tableau du tas. Ainsi si j'insere un
evenement e, je pourrais vérifier si il a déjà été inséré dans le tas
en faisant inverse[e->date].


Tu veux savoir si cet événement e a déjà été inséré, ou si un événement
de même date à déjà été inséré ?

Le tableau d'inverse doit donc être de taille date_max (la plus grande
date que contient le tas).

Exemple:
-------

soit le tas:
-------------------------
T= | 3 | 5 | 4 | 6 | 7 | 9 |
-------------------------

L'arbre correspondant:
3
5 4
6 7 9


Et le tableau d'inverses:
---------------------------------------------
inverse= | -1 | -1 | -1 | 0 | 2 | 1 | 3 | 4 | -1 | 5 |
---------------------------------------------


C'est à dire que tu utilises un tableau dont la taille
va être énorme si tu as des échelles de temps différentes
(un év à date 4, un à date 6 et un à date 10000), pour
que la recherche de présence soit en O(1) au lieu de O(log(n)) ?

où -1 correspond à une date absente du tas.

Donc si je veux insérer un évenement e de date 5, je peux connaître
son rang dans le tas (tableau T) en faisant inverse[e->date] et
j'obtiens 1. Si j'obtient -1, je l'insère normalement en mettant à jour
le tableau d'inverses.


OK

Jusque là, tout est simple, pour une date énorme, le tableau d'inverses
est gigantesque! Pour le minimiser, on peut allouer que
date_max-date_min cases et y accéder en faisant :
inverse[e->date-date_min]


Ca permet à ton système d'éviter une pseudo "fuite mémoire" mais
pas de gérer des systèmes à grande différence d'échelle temporelle.

Le problème est alors que lors d'une extraction de la racine (je prends
toujours la date la plus petite), je dois realloc le tableau d'inverse
pour supprimer cette racine qui est partie. C'est-à-dire que je dois
enlever la première case mais le realloc ne permet pas cela. Il permet
seulement de tronquer sur la fin du tableau (et surtout de libérer
l'espace mémoire ne servant plus).

Est-ce plus clair ainsi?


Oui.

Marc Boyer
--
Je ne respecte plus le code de la route à vélo depuis une double fracture
due au fait que j'étais le seul à le respecter.


Avatar
Marc Boyer
Jean-Marc Bourguet wrote:
Nicolas writes:
Rapidement, une table de hashage, ça a plusieurs inconvénients:
1) tu perds l'ordre, donc, dans un simu à événement discrets,
tu ne peux pas connaitre sans parcours total la prochaine
date à lequelle un év. va se produire



De toute maniere l'ordre est maintenu pas un tas.


Oui, si on fait la table de hashage pour les indices
inverses en effet.

2) ça se redimensionne pas bien si je me souviens bien



Ah bon, toutes mes tables de hachages sont auto-redimentionnables.


Comment tu gères ça ?
Globalement, à un élément E tu associe une clef K et tu
le mets dans un tableau à l'indice I qui vaut (K % tailleTab).
Non ?
Si tu redimensionne ton tableau, tu modifie tailleTab,
donc tu as perdu l'association E->I puisque tu perds K->I.

Non ?

Marc Boyer
--
Je ne respecte plus le code de la route à vélo depuis une double fracture
due au fait que j'étais le seul à le respecter.



Avatar
Jean-Marc Bourguet
Marc Boyer writes:

2) ça se redimensionne pas bien si je me souviens bien



Ah bon, toutes mes tables de hachages sont auto-redimentionnables.


Comment tu gères ça ?


Deux techniques que j'ai utilisee:
- lors de reallocation, on rehache tout
- apres agrandissement, on garde deux tables temporairement
- lors d'une recherche, on cherche dans la nouvelle puis dans
l'ancienne si on ne l'a pas trouve et on le deplace dans la
nouvelle si necessaire.
- lors d'un ajout, on deplace un element de l'ancienne vers la
nouvelle. Le facteur d'agrandissement est choisi de sorte
qu'on soit sur d'avoir tout deplace avant d'avoir a agrandir
(je l'ai utilise dans des appli interactives ou le rehachage de
tout apres agrandissement pouvait avoir un delai visible par
l'utilisateur)

A+

--
Jean-Marc
FAQ de fclc: http://www.isty-info.uvsq.fr/~rumeau/fclc
Site de usenet-fr: http://www.usenet-fr.news.eu.org




Avatar
Marc Boyer
In article , Jean-Marc Bourguet wrote:
Marc Boyer writes:

2) ça se redimensionne pas bien si je me souviens bien



Ah bon, toutes mes tables de hachages sont auto-redimentionnables.


Comment tu gères ça ?


Deux techniques que j'ai utilisee:
- lors de reallocation, on rehache tout


Donc en O(n)

- apres agrandissement, on garde deux tables temporairement
- lors d'une recherche, on cherche dans la nouvelle puis dans
l'ancienne si on ne l'a pas trouve et on le deplace dans la
nouvelle si necessaire.
- lors d'un ajout, on deplace un element de l'ancienne vers la
nouvelle. Le facteur d'agrandissement est choisi de sorte
qu'on soit sur d'avoir tout deplace avant d'avoir a agrandir
(je l'ai utilise dans des appli interactives ou le rehachage de
tout apres agrandissement pouvait avoir un delai visible par
l'utilisateur)


Donc, je reste sur mon idée que ça se redimensionne pas très
bien: on y arrive, mais globalement, sans tirer partit de
la structure même de la table, et en O(n).

Marc Boyer
--
Je ne respecte plus le code de la route à vélo depuis une double fracture
due au fait que j'étais le seul à le respecter.





Avatar
Jean-Marc Bourguet
Marc Boyer writes:

In article , Jean-Marc Bourguet wrote:
Marc Boyer writes:

2) ça se redimensionne pas bien si je me souviens bien



Ah bon, toutes mes tables de hachages sont auto-redimentionnables.


Comment tu gères ça ?


Deux techniques que j'ai utilisee:
- lors de reallocation, on rehache tout


Donc en O(n)


Donc avec des reallocations fait pour des tailles en progression
geometrique, le cout des reallocations est de O(N) ou N est la taille
finale. Donc le cout amorti d'une insertion est de O(1). Que veux-tu
de mieux?

A+

--
Jean-Marc
FAQ de fclc: http://www.isty-info.uvsq.fr/~rumeau/fclc
Site de usenet-fr: http://www.usenet-fr.news.eu.org






Avatar
Marc Boyer
Jean-Marc Bourguet wrote:
Marc Boyer writes:

Donc avec des reallocations fait pour des tailles en progression
geometrique, le cout des reallocations est de O(N) ou N est la taille
finale. Donc le cout amorti d'une insertion est de O(1). Que veux-tu
de mieux?


Rien en fait.

Marc Boyer
--
Je ne respecte plus le code de la route à vélo depuis une double fracture
due au fait que j'étais le seul à le respecter.

1 2