Je sais que Knapsack
est NP-complet alors qu'il peut être résolu par DP. Ils disent que la solution DP est pseudo-polynomial
, car il est exponentiel dans la "longueur d'entrée" (c'est-à-dire le nombre de bits requis pour coder l'entrée). Malheureusement, je ne l'ai pas compris. Quelqu'un peut-il expliquer que pseudo-polynomial
chose pour moi lentement?
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 cependant pas polynomial dans la longueur de l'entrée, ce qui le rend pseudo - polynôme.
Considérons W = 1 000 000 000 000. Il ne faut que 40 bits pour représenter ce nombre, donc la taille d'entrée = 40, mais le runtime de calcul utilise le facteur 1 000 000 000 000 qui est O (240).
Donc, le runtime est plus précisément dit O (N.2bits en W), qui est exponentielle.
Regarde aussi:
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 conçus pour gérer des nombres de 4 à 8 octets à la fois sans frais supplémentaires (par rapport aux nombres par rapport à la taille, par exemple, 1 octet), nous rencontrons rarement un changement dans le temps d'exécution de l'augmentation de nos nombres ou dans les fourchettes que nous rencontrons dans de vrais problèmes - donc le facteur dominant reste juste la quantité de points de données, les n ou m facteurs 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 ce nombre de bits ou moins )
Mais essayez de retravailler avec d'autres algorithmes pour agir sur des ensembles de données impliquant de gros nombres entiers - des nombres qui nécessitent plus de 8 octets pour représenter - et voyez ce que cela fait pour le runtime. 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 vous étendez au-delà du tampon de sécurité, les processeurs conventionnels nous donnent "gratuitement" en gérant 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 magnitude 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 spectaculaire aux changements de valeur dans d'autres algorithmes avant celui-ci, c'est pourquoi il pourrait sembler que nous traitons Knapsack différemment - mais c'est une véritable analyse de la façon dont il réagit de manière non polynomiale aux changements de taille d'entrée.
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 entier unique. La complexité temporelle est à propos de la relation à la taille = d'entrée. La taille d'un entier n'est PAS la valeur de l'entier, mais le nombre de bits qui le représentent. Nous convertirons 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é temporelle exponentielle.