Réponses:
Il existe une approche assez pratique qui fonctionne en temps , où est le nombre de bits dans le mot processeur. L'idée principale est de parcourir les éléments de la matrice un par un dans l'ordre croissant (rompre arbitrairement les liens) et de les "allumer". Considérons le moment où le plus grand élément de certains triples est activé. Pour simplifier, supposons que ledit élément soit . Il est naturel d'ajouter la valeur du triple à la réponse maintenant, lorsque le dernier élément est allumé. Nous devons donc compter le nombre de possibles tels que etsont déjà allumés (ce serait le nombre de triplets, ici est le plus grand élément, donc ils ont été complètement allumés tout à l'heure). Ici, nous pouvons accélérer la mise en œuvre naïve en utilisant l'optimisation des bits.
Pour plus de détails, vous pouvez vous référer à l'implémentation suivante en C ++ 11 qui devrait fonctionner pour , (il n'est pas très optimisé; cependant, il bat toujours la sommation naïve pour de grande marge au moins sur ma machine).
// code is not very elegant,
// but should be understandable
// here the matrix a has dimensions n x n
// a has to be symmetric!
int64_t solve (int n, const vector<vector<int32_t>> &a)
{
std::vector<boost::dynamic_bitset<int64_t>> mat
(n, boost::dynamic_bitset<int64_t>(n));
vector<pair<int, int>> order;
for (int j = 1; j < n; j++)
for (int i = 0; i < j; i++)
order.emplace_back(i, j);
sort(order.begin(), order.end(),
[&] (const pair<int, int> &l, const pair<int, int> &r)
{return a[l.first][l.second] < a[r.first][r.second];});
int64_t ans = 0;
for (const auto &position : order)
{
int i, j;
tie (i, j) = position;
mat[i][j] = mat[j][i] = 1;
// here it is important that conditions
// mat[i][i] = 0 and mat[j][j] = 0 always hold
ans += (mat[i] & mat[j]).count() * int64_t(a[i][j]);
}
return ans;
}
Si vous envisagez d'utiliser des optimisations de bits pour tricher, vous pouvez utiliser quatre méthodes russes pour le même résultat ici, produisant un algorithme , qui devrait être moins pratique (car est assez grand sur la plupart des matériels modernes) mais c'est théoriquement mieux. En effet, choisissons et gardons chaque ligne de la matrice sous la forme d'un tableau d' de à , où le -ième nombre dans le tableau correspond aux bits de la ligne allant de inclus à exclusif en-indexation. Nous pouvons précalculer les produits scalaires de deux de ces blocs en temps . La mise à jour d'une position dans la matrice est rapide car nous ne modifions qu'un seul entier. Pour trouver le produit scalaire des lignes et parcourez simplement les tableaux correspondant à ces lignes, recherchez les produits scalaires des blocs correspondants dans le tableau et résumez les produits obtenus.
Le paragraphe ci-dessus suppose que les opérations avec des entiers prennent du temps . Cette hypothèse est assez courante , car elle ne modifie généralement pas la vitesse comparative des algorithmes (par exemple, si nous n'utilisons pas cette hypothèse, la méthode de la force brute fonctionne en fait en temps (ici nous mesurons le temps en opérations binaires) si prend des valeurs entières avec des valeurs absolues au moins jusqu'à pour une constante (et sinon nous pouvons résoudre le problème avec multiplications de la matrice de toute façon); cependant la méthode des quatre russes suggérée ci-dessus utilise opérations avec des nombres de taille dans ce cas; il fait donc des opérations sur bits, ce qui est encore mieux que la force brute malgré le changement de modèle).
La question de l'existence de l' approche reste cependant intéressante.
Les techniques (optimisations de bits et méthode des quatre russes) présentées dans cette réponse ne sont en aucun cas originales et sont présentées ici pour compléter l'exposé. Cependant, trouver un moyen de les appliquer n'était pas anodin.
mat[i]
mat[j]
mat
ce qui semble être important. Je comprends comment cela pourrait être défini, mais je me demande si (mat[i] & mat[j]).count()
cela fonctionnerait comme souhaité avec n'importe quel conteneur STL.
mat
- je suppose que nous devons utiliser std::vector<boost::dynamic_bitset<int64_t>>
.
mat
: oui, je pensais au bitet standard en fait, mais boost::dynamic_bitset
c'est encore mieux dans ce cas, car sa taille n'a pas à être constante au moment de la compilation. Éditera la réponse pour ajouter ce détail et clarifier l'approche des quatre russes.