/*
Au moment où j'écris ceci, toutes les solutions sur cette page doubleront (ou plus) la quantité d'espace nécessaire pour stocker le tableau. La solution suivante réduit la quantité d'espace gaspillé de Ω(n) à θ(n/w), où w est le nombre de bits dans un "mot" d'ordinateur. Sur ma machine, c'est 64.
La prose de cette réponse est en commentaires C, vous pouvez copier-coller cette réponse intégralement et la compiler avec votre compilateur C.
*/
#include
#include
#include
#include
/*
Le problème est de soutenir la lecture et l'écriture de valeurs dans un tableau en temps O(1) en même temps que des écritures en bloc où toutes les valeurs du tableau sont écrites en même temps en temps O(1). C'est possible en utilisant une technique inventée par Aho, Hopcroft et Ullman, autant que je sache. Je vais présenter une version due à Gonzalo Navarro, "Initialisation des tableaux en temps constant dans un petit espace".
L'idée est de garder trois tableaux de métadonnées avec le tableau de données. Nous gardons également deux entiers: unset
, qui est la dernière valeur utilisée dans une opération d'écriture en bloc, et size
, une approximation pour le nombre de valeurs qui ont été définies depuis la dernière écriture en bloc. En tout temps, le nombre de valeurs distinctes écrites depuis la dernière écriture en bloc est entre size
et w * size
.
Les trois tableaux de métadonnées décrivent des informations sur des blocs de w valeurs dans le tableau de données. Ils sont:
-
nth
: nth[i] est le ième bloc unique à écrire depuis la dernière écriture en bloc
-
inverse_nth
: inverse_nth[i] est l'ordre dans lequel le ième bloc du tableau a été écrit, en comptant à partir de 0 à la dernière écriture en bloc.
-
bitset
: Le j-ième bit de bitset[i]
est 1 lorsque la cellule de tableau numérotée 64*i + j a été écrite depuis la dernière écriture en bloc.
bitset[i]
et inverse_nth[i]
sont autorisés à être invalides si i
n'est pas un membre de l'ensemble {nth[0]
, nth[1]
, ... , nth[size-1]
}. En d'autres termes, inverse_nth[i]
et bitset[i]
sont valides si et seulement si inverse_nth[i] < size
et nth[inverse_nth[i]] == i
.
Au lieu de stocker trois tableaux séparés de même longueur, j'ai choisi de stocker un seul tableau, is_set
, avec trois champs.
*/
typedef struct {
int nth_;
int inverse_nth_;
uint64_t bitset_;
} IsSetCell;
typedef struct {
int unset_;
int size_;
IsSetCell is_set_[];
} IsSetArray;
typedef struct {
IsSetArray * metadata_;
int payload_[];
} ResettableArray;
/*
Pour construire un tableau, nous avons besoin d'une valeur par défaut à retourner lorsque l'on lit une valeur qui n'a jamais été écrite.
*/
ResettableArray * ConstructResettableArray(int n, int unset) {
ResettableArray* result =
malloc(offsetof(ResettableArray, payload_) + n * sizeof(int));
if (!result) return NULL;
n = (n + 63) / 64;
result->metadata_ =
malloc(offsetof(IsSetArray, is_set_) + n * sizeof(IsSetCell));
if (!result->metadata_) {
free(result);
return NULL;
}
result->metadata_->size_ = 0;
result->metadata_->unset_ = unset;
return result;
}
void DestructResettableArray(ResettableArray * a) {
if (a) free(a->metadata_);
free(a);
}
/*
Le cœur de l'algorithme réside dans l'écriture et la lecture des métadonnées. Après IsSet()
et Set()
sont définis (ci-dessous), lire et écrire dans les tableaux est simple.
*/
bool IsSet(const IsSetArray * a, int i);
void Set(IsSetArray * a, int i);
int GetValue(const ResettableArray * a, int i) {
if (!IsSet(a->metadata_, i)) return a->metadata_->unset_;
return a->payload_[i];
}
void SetValue(ResettableArray * a, int i, int v) {
a->payload_[i] = v;
Set(a->metadata_, i);
}
void SetAllValues(ResettableArray * a, int v) {
a->metadata_->unset_ = v;
}
/*
La partie complexe de la lecture et de l'écriture est la relation bidirectionnelle entre inverse_nth
et nth
. S'ils se pointent mutuellement à l'emplacement i (is_set[is_set[i].inverse_nth].nth == i
) alors l'emplacement i contient des données valides qui ont été écrites après la dernière écriture en bloc, tant que is_set[i].inverse_nth < size
.
*/
uint64_t OneBit(int i) {
return UINT64_C(1) << i;
}
bool IsSet(const IsSetArray * a, int i) {
const int cell = i/64, offset = i & 63;
const int inverse_nth = a->is_set_[cell].inverse_nth_;
return inverse_nth < a->size_ && a->is_set_[inverse_nth].nth_ == cell &&
a->is_set_[cell].bitset_ & OneBit(offset);
}
void Set(IsSetArray * a, int i) {
const int cell = i/64, offset = i & 63;
const int inverse_nth = a->is_set_[cell].inverse_nth_;
if (inverse_nth >= a->size_ || a->is_set_[inverse_nth].nth_ != cell) {
a->is_set_[cell].inverse_nth_ = a->size_;
a->is_set_[cell].bitset_ = 0;
a->is_set_[a->size_].nth_ = cell;
++a->size_;
}
a->is_set_[cell].bitset_ |= OneBit(offset);
}
0 votes
Est-il possible de mettre en œuvre par une table de hachage?