Je sais que Knapsack
c'est NP-complet alors qu'il peut être résolu par DP. Ils disent que la solution DP est pseudo-polynomial
, car elle est exponentielle dans la "longueur d'entrée" (c'est-à-dire le nombre de bits nécessaires pour coder l'entrée). Malheureusement, je ne l'ai pas compris. Quelqu'un peut-il m'expliquer cette pseudo-polynomial
chose lentement?
87
Réponses:
Le temps d'exécution est O (NW) pour un problème de sac à dos illimité avec N articles et un sac à dos de taille W. W n'est pas polynomial dans la longueur de l'entrée, ce qui le rend pseudo- polynomial.
Considérez W = 1 000 000 000 000. Il ne faut que 40 bits pour représenter ce nombre, donc taille d'entrée = 40, mais le temps d'exécution de calcul utilise le facteur 1 000 000 000 000 qui est O (2 40 ).
Ainsi, le temps d'exécution est plus précisément dit O (N.2 bits dans W ), ce qui est exponentiel.
Regarde aussi:
la source
Dans la plupart de nos problèmes, nous avons affaire à de grandes listes de nombres qui s'intègrent confortablement dans les types de données standard int / float. En raison de la façon dont la plupart des processeurs sont construits pour gérer des nombres de 4 à 8 octets à la fois sans coût supplémentaire (par rapport aux nombres qui ne rentrent pas dans, disons, 1 octet), nous rencontrons rarement un changement dans le temps de fonctionnement à dans les gammes que nous rencontrons dans les problèmes réels - le facteur dominant reste donc simplement la quantité de points de données, les facteurs n ou m auxquels nous sommes habitués.
(Vous pouvez imaginer que la notation Big-O cache un facteur constant qui divise 32 ou 64 bits par donnée, ne laissant que le nombre de points de données chaque fois que chacun de nos nombres tient dans autant de bits ou moins )
Mais essayez de retravailler avec d'autres algorithmes pour agir sur des ensembles de données impliquant de gros entiers - des nombres qui nécessitent plus de 8 octets pour être représentés - et voyez ce que cela fait à l'exécution. L'ampleur des nombres impliqués fait toujours une différence, même dans les autres algorithmes comme le tri binaire, une fois que vous étendez au-delà de la mémoire tampon de sécurité, les processeurs conventionnels nous donnent "gratuitement" en traitant des lots de 4 à 8 octets.
L'astuce avec l'algorithme Knapsack dont nous avons discuté est qu'il est inhabituellement sensible (par rapport à d'autres algorithmes) à la grandeur d'un paramètre particulier, W.Ajoutez un bit à W et vous doublez le temps d'exécution de l'algorithme. Nous n'avons pas vu ce genre de réponse dramatique aux changements de valeur d'autres algorithmes avant celui-ci, c'est pourquoi il peut sembler que nous traitons Knapsack différemment - mais c'est une véritable analyse de la façon dont il répond de manière non polynomiale. aux changements de taille d'entrée.
la source
Le temps d'exécution de l'algorithme Knapsack est lié non seulement à la taille de l'entrée (n - le nombre d'articles) mais également à l'amplitude de l'entrée (W - la capacité du sac à dos) O (nW) qui est exponentielle dans sa façon représenté en ordinateur en binaire (2 ^ n). La complexité de calcul (c'est-à-dire comment le traitement est effectué à l'intérieur d'un ordinateur via des bits) ne concerne que la taille des entrées, pas leurs grandeurs / valeurs .
Ne tenez pas compte de la liste des valeurs / poids pendant un moment. Disons que nous avons une instance avec une capacité de sac à dos 2. W prendrait deux bits dans les données d'entrée. Nous allons maintenant augmenter la capacité du sac à dos à 4, en conservant le reste de l'entrée. Notre contribution n'a augmenté que d'un peu, mais la complexité du calcul a été multipliée par deux. Si nous augmentons la capacité à 1024, nous n'aurions que 10 bits d'entrée pour W au lieu de 2, mais la complexité a augmenté d'un facteur 512. La complexité temporelle croît exponentiellement dans la taille de W en représentation binaire (ou décimale) .
Un autre exemple simple qui m'a aidé à comprendre le concept pseudo-polynomial est l'algorithme de test de primalité naïve. Pour un nombre n donné, nous vérifions s'il est divisé également par chaque nombre entier dans la plage 2..√n, donc l'algorithme prend √ (n − 1) pas. Mais ici, n est la grandeur de l'entrée, pas sa taille.
En revanche, la recherche d'un tableau pour un élément donné s'exécute en temps polynomial: O (n). Cela prend au plus n étapes et ici n est la taille de l'entrée (la longueur du tableau).
[ vois ici ]
Calcul des bits requis pour stocker un nombre décimal
la source
La façon dont je comprends cela est que la capacité aurait été O (W) si l'entrée de capacité était un tableau de [1,2, ..., W] , qui a une taille de W. Mais l'entrée de capacité n'est pas un tableau de nombres, c'est plutôt un seul entier. La complexité temporelle concerne la relation avec la taille de l'entrée. La taille d'un entier n'est PAS la valeur de l'entier, mais le nombre de bits le représentant. Nous convertissons plus tard cet entier W en un tableau [1,2, ..., W] dans l'algorithme, amenant les gens à penser à tort que W est la taille, mais ce tableau n'est pas l'entrée, l'entier lui-même l'est.
Considérez l'entrée comme «un tableau de choses» et la taille comme «combien de choses dans le tableau». L'entrée d'élément est en fait un tableau de n éléments dans le tableau, donc size = n. L'entrée de capacité n'est PAS un tableau de nombres W , mais un seul entier , représenté par un tableau de bits log (W). Augmentez sa taille de 1 (en ajoutant 1 bit significatif), W double donc le temps d'exécution double, d'où la complexité exponentielle du temps.
la source