Plus grande somme divisible par n

16

J'ai posé cette question sur StackOverflow , mais je pense que c'est un endroit plus approprié.

C'est un problème du cours d' introduction aux algorithmes :

Vous avez un tableau a avec entiers positifs (le tableau n'a pas besoin d'être trié ou les éléments sont uniques). Suggérez un algorithme pour trouver la plus grande somme d'éléments divisible par .nO(n)n

Exemple: . La réponse est (avec les éléments 6 , 13 , 4 , 8 , 25 )a=[6,1,13,4,9,8,25],n=sept566,13,4,8,25

Il est relativement facile de trouver dans O(n2) en utilisant la programmation dynamique et le stockage la plus grande somme avec le reste 0,1,2,...,n-1 .

De plus, si nous limitons l'attention à une séquence d'éléments contigus, il est facile de trouver la séquence optimale de ce type en temps O(n) , en stockant les sommes partielles modulo n : let S[i]=a[0]+a[1]++a[i] , pour chaque reste r rappelez-vous le plus grand indice j tel que , puis pour chaque vous considérez oùS[j]r(modn)iS[j]S[i]jest l'indice correspondant à .r=S[i]modn

Mais existe-t-il une solution en temps pour le cas général? Toute suggestion sera appréciée! Je considère que cela a quelque chose à voir avec l'algèbre linéaire, mais je ne sais pas exactement quoi.O(n)

Sinon, cela peut-il être fait en temps ?O(nlogn)

delta-terminator
la source
2
1. Vous avez posté exactement la même question sur Stack Overflow. Veuillez ne pas poster la même question sur plusieurs sites . Nous ne voulons pas que plusieurs copies flottent sur plusieurs sites SE. Si vous n'avez pas obtenu de réponse acceptable, vous pouvez signaler votre question pour la migration vers un autre site, mais veuillez ne pas simplement republier la même chose ailleurs. 2. Pouvez-vous donner une référence / une citation / un lien vers le manuel ou le cours où cela est apparu? Êtes-vous sûr qu'il existe une solution -time? O(n)
DW
5
Le défi de votre université est-il toujours ouvert? Il serait vraiment utile de voir le lien vers le cours, la question exacte et si c'est vraiment et les personnes qui l'ont préparé expliqueront / publieront leur réponse, ce serait génial. O(n)
Evil
Il est relativement facile de le trouver dans O (n2) O (n2) en utilisant la programmation dynamique et en stockant la plus grande somme avec le reste 0,1,2, ..., n − 10,1,2, ..., n − 1. Pourriez-vous, s'il vous plaît, développer cela un peu? Je peux comprendre comment cela serait n-carré si nous ne considérons que les éléments contigus, mais avec des éléments non contigus également, ne serait-il pas exponentiel dans l'ordre?
Nithish Inpursuit Ofhappiness

Réponses:

4

Voici quelques idées aléatoires:

  • L'algorithme de programmation dynamique peut être inversé pour rechercher une plus petite somme au lieu d'une plus grande somme. Vous finissez par chercher une somme congruente au reste de la somme de l'ensemble du tableau, au lieu d'une congruente à zéro. Si nous traitons les éléments dans l'ordre croissant, cela permet parfois à l'algorithme dynamique de se terminer avant de traiter l'ensemble du tableau.

    Le coût serait si l'on traitait k éléments. Il n'y a pas de limite inférieure de Ω ( n log n ) sur cet algorithme car nous n'avons pas à trier tous les éléments. Il ne faut que O ( n log k ) pour obtenir les k plus petits éléments.O(nk)kΩ(nlogn)O(nlogk)k

  • Si nous nous soucions de l'ensemble avec la taille du larget, au lieu de l'ensemble avec la plus grande somme, nous pourrions être en mesure d'utiliser la multiplication polynomiale basée sur la transformée de Fourier rapide pour résoudre le problème dans heure. Similaire à ce qui se fait dans 3SUM lorsque la plage de domaines est limitée. (Remarque: utilisez la quadrature répétée pour faire une recherche binaire, sinon vous obtiendrez O ( n k ( log n ) ( log log n ) )kO(n(logn)2(loglogn))O(nk(logn)(loglogn))k est le nombre d'éléments omis.)

  • Lorsque est composite et que presque tous les restes sont un multiple de l'un des n facteurs, un gain de temps significatif peut être gagné en se concentrant sur les restes qui ne sont pas un multiple de ce facteur.nn

  • Lorsqu'un reste rest très fréquent, ou qu'il n'y a que quelques restes présents, garder une trace du `` prochain emplacement ouvert si vous commencez à partir d'ici et continuez à avancer r'' peut économiser beaucoup de numérisation pour les sauts dans les espaces ouverts temps.

  • Vous pouvez raser un facteur de journalisation en suivant uniquement l'accessibilité et en utilisant des masques de bits (dans l'algorithme dynamique inversé), puis en revenant en arrière une fois que vous atteignez le reste cible.

  • L'algorithme de programmation dynamique peut très bien être exécuté en parallèle. Avec un processeur pour chaque emplacement de tampon, vous pouvez descendre à . Alternativement, en utilisant la largeur O ( n 2 ) et en divisant et conquérant l'agrégation au lieu de l'agrégation itérative, le coût de la profondeur du circuit peut descendre jusqu'à O ( log 2 n ) .O(n)O(n2)O(log2n)

  • (Meta) Je soupçonne fortement que le problème qui vous a été posé concerne les sommes contiguës . Si vous vous êtes lié au problème réel, il serait facile de le vérifier. Sinon, je suis très surpris de la difficulté de ce problème, étant donné qu'il a été attribué dans un cours intitulé "Introduction aux algorithmes". Mais peut-être que vous avez couvert un truc en classe qui le rend trivial.

Craig Gidney
la source
nO(n)
2
r1r2
-1

Mon algorithme proposé se présente comme suit:

Une somme est divisible par n si vous ajoutez seulement des sommets qui sont des multiples de n.

Avant de commencer, vous créez une table de hachage avec un int comme clé et une liste d'index comme valeur. Vous créez également une liste de résultats contenant des indices.

Vous bouclez ensuite sur le tableau et ajoutez chaque index dont le mod n est zéro à votre liste de résultats. Pour chaque autre index, vous procédez comme suit:

Vous soustrayez la valeur mod n de cet index de n. Ce résultat est la clé de votre hashmap qui stocke les index des éléments avec la valeur requise. Maintenant, vous ajoutez cet index à la liste dans la table de hachage et continuez.

Après avoir bouclé le tableau, vous calculez la sortie. Pour ce faire, vous triez chaque liste dans la table de hachage en fonction de la valeur vers laquelle l'index pointe. Maintenant, vous considérez chaque paire de la table de hachage résumant à n. Donc, si n = 7, vous recherchez dans la table de hachage pour 3 et 4. Si vous avez une entrée dans les deux, vous prenez les deux plus grandes valeurs, supprimez-les de leurs listes et ajoutez-les à votre liste de résultats.

Dernière recommandation: toujours pas testé l'algorithme, écrivez un testcase contre lui en utilisant un algorithme de force brute.

Tobias Würfl
la source
2
Gourmand, linéaire, ne fonctionne pas. Vous ne considérez que les éléments divisibles par n et les paires divisibles par n, qu'en est-il des triples et plus encore? Il ne garantit pas la somme maximale de sous-ensembles dans le cas trivial. [2, 1, 8] -> la somme maximale est 9, mais votre algorithme retourne 3.
Evil
n2
Merci de m'avoir signalé cette erreur. Mon idée sur l'amélioration serait de faire une table de hachage des piles de listes qui est ordonnée en augmentant la valeur et ne commence à s'accumuler qu'après avoir effectué un passage dans le tableau.
Tobias Würfl
Vous voulez dire un tableau de tableaux, qui sera trié, et "hashmap" est% n? Vous devez toujours les trier, et si vous les avez triés, prendre une valeur minimale / maximale est correct, mais il y a toujours une partie inévitable de choisir réellement un sous-ensemble, ce qui dans le pire des cas ne profite pas. Quoi qu'il en soit, si vous avez des améliorations, vous pourriez peut-être modifier le message?
Evil
Oui, c'était une idée assez rapide avec les piles. En fait, vous n'avez besoin que de listes dans la table de hachage que vous triez. Je ne savais pas s'il était poli de modifier ma première réponse. Après tout, j'ai fait une erreur lors de ma première tentative.
Tobias Würfl
-2

utilisez cette méthode DP à partir de ( /programming/4487438/maximum-sum-of-non-consecutive-elements?rq=1 ):

Étant donné un tableau A [0..n], soit M (i) la solution optimale en utilisant les éléments d'indices 0..i. Alors M (-1) = 0 (utilisé dans la récurrence), M (0) = A [0], et M (i) = max (M (i - 1), M (i - 2) + A [i ]) pour i = 1, ..., n. M (n) est la solution que nous voulons. C'est O (n) . Vous pouvez utiliser un autre tableau pour stocker le choix effectué pour chaque sous-problème, et ainsi récupérer les éléments réels choisis.

Changez la récursivité en M (i) = max (M (i - 1), M (i - 2) + A [i]) de telle sorte qu'elle ne soit stockée que si elle est divisible par N

Pinhead
la source
2
Cela ne fonctionne pas - je vous laisse comprendre pourquoi. (Astuce: essayez de l'exécuter sur le tableau constant 1.) De plus, dans ce problème, nous autorisons les éléments consécutifs.
Yuval Filmus
1
C'est une très bonne solution, juste pour un problème totalement différent (et beaucoup plus facile).
Evil