web-dev-qa-db-fra.com

Sont du temps constant et du temps constant amorti-il efficacement considéré comme équivalent?

Je dois écrire une aléatoire qui permet une suppression ajoutée et aléatoire en temps constant (O(1)).

Ma première pensée était de le soutenir avec une sorte de matrice (j'ai choisi une arraylist), car les tableaux ont un accès constant via un index.

En examinant la documentation, j'ai réalisé que les ajouts des arraylistes sont considérés comme une durée constante amortie, car une addition peut nécessiter une réaffectation de la matrice sous-jacente, qui est O (n).

Les temps constants et le temps constant sont-ils efficacement identiques, ou est-ce que je dois regarder une structure Thay ne nécessite pas une réaffectation complète sur chaque ajout?

Je demande cela parce que les structures basées sur des array de côté (qui autant que je sache auront toujours des ajouts de temps constants amorties), je ne peux penser à rien qui répondra aux exigences:

  • Tout ce qui est basé sur l'arbre aura au mieux o (log n) accès
  • Une liste liée pourrait potentiellement avoir O(1) ajouts (si une référence à la queue est conservée), mais une suppression aléatoire doit être au mieux O (n).

Voici toute la question; Au cas où je me suis vitré sur certains détails importants:

Concevoir et mettre en œuvre une aléatoire. Il s'agit d'une implémentation de l'interface de file d'attente dans laquelle l'opération Supprimer () supprime un élément choisi uniformément au hasard parmi tous les éléments actuellement dans la file d'attente. (Pensez à une aléatoire comme sac dans lequel nous pouvons ajouter des éléments ou atteindre et supprimer aveuglément un élément aléatoire.) Les opérations Ajouter (x) et Supprimer () dans une randomqueur doivent fonctionner en temps constant par opération.

16
Carcigenicate

Le temps constant amorti peut presque toujours être considéré comme équivalent au temps constant et sans connaître les spécificités de votre application et le type d'utilisation que vous prévoyez de faire à cette file d'attente, la plupart des chances sont que vous serez couverts.

Une liste de matrices a le concept de capacité, qui est fondamentalement égale à la plus grande taille/longueur/nombre d'éléments qui l'ont déjà été requis jusqu'à présent. Donc, ce qui se passera-t-il est que, au début, la liste des matrices continuera à se réaffecter à elle-même pour augmenter sa capacité que vous continuez à ajouter des articles à ce sujet, mais à un moment donné, le nombre moyen d'articles ajoutés par unité correspondra inévitablement le nombre moyen d'éléments. Enlevé par unité de temps, (sinon, vous risquez de sortir de la mémoire de toute façon) à quel point le tableau arrêtera de réaffecter lui-même et que tous les ajouts seront satisfaits à la durée constante de O (1).

Cependant, gardez à l'esprit que par défaut, le retrait aléatoire d'une liste de réseau n'est pas O (1), il est O (n), car les listes de réseau déplacent tous les éléments après l'élément éliminé une position pour prendre la place du retrait. Objet. Afin d'atteindre O(1) Vous devrez remplacer le comportement par défaut pour remplacer l'élément supprimé par une copie du dernier élément de la liste Array, puis supprimez le dernier élément. qu'aucun objet ne sera déplacé. Mais alors, si vous faites cela, vous n'avez plus de file d'attente.

10
Mike Nakis

La question semble spécifiquement demander du temps constant et non une temps constant amorti . Donc, en ce qui concerne la question citée, non, ils ne sont pas effectivement les mêmes *. Sont-ils cependant dans des applications du monde réel?

Le problème typique avec la constante amorti est que vous devez parfois payer la dette accumulée. Ainsi, tandis que les inserts sont généralement constants, vous devez parfois subir la surcharge de réinsérer tout lorsqu'un nouveau bloc est attribué.

Lorsque la différence entre le temps constant et le temps constant amortit est pertinente pour une application dépend de la question de savoir si cette vitesse très lente occasionnelle est acceptable. Pour un très grand nombre de domaines, cela va généralement bien. Surtout si le conteneur a une taille maximale efficace (similaire de caches, tampons temporaires, de conteneurs de travail), vous pouvez payer effectivement des coûts une fois pendant l'exécution.

En réponse aux applications critiques, ces temps peuvent être inacceptables. Si vous êtes tenu de répondre à une courte garantie de la variation de temps, vous ne pouvez pas compter sur un algorithme qui dépasse occasionnellement cela. J'ai déjà travaillé sur de tels projets, mais ils sont extrêmement rares.

Cela dépend également de la hauteur de ce coût réellement. Les vecteurs ont tendance à se produire bien depuis que leur coût de réaffectation est relativement faible. Si vous allez à la carte de hachage cependant, la réaffectation peut être beaucoup plus élevée. Bien que, à nouveau, pour la plupart des applications probablement bien, des serveurs vivent particulièrement plus longtemps avec une limite supérieure sur les éléments du conteneur.

* Il y a un peu de problème ici cependant. Afin de faire de tout conteneur à usage général être une durée constante pour l'insertion, une des deux choses doit tenir:

  • Le conteneur doit avoir une taille maximale fixe; ou
  • vous pouvez assumer une allocation de mémoire d'éléments individuels est une durée constante.
14
edA-qa mort-ora-y

Cela dépend de savoir si vous optimisez pour le débit ou pour la latence:

  • Les systèmes sensibles à la latence nécessitent des performances cohérentes. Pour un tel scénario, nous devons mettre l'accent sur le pire comportement du système. Des exemples sont des systèmes de temps réel doux tels que des jeux qui souhaitent atteindre un cadre de framerate cohérent ou des serveurs Web qui doivent envoyer une réponse dans un certain temps étanche: gaspiller des cycles de processeur est meilleur que d'être en retard.
  • Les systèmes optimisés par un débit ne se soucient pas des stands occasionnels, tant que la quantité maximale de données peut être traitée à long terme. Ici, nous nous intéressons principalement aux performances amorties. C'est généralement le cas pour le chiffre d'échange de numéros ou d'autres travaux de traitement par lots.

Notez qu'un système peut avoir différents composants qui doivent être classés différemment. Par exemple. Un processeur de texte moderne aurait un fil d'interface utilisateur sensible à la latence, mais des fils optimisés par un débit pour d'autres tâches telles que la vérification orthographique ou PDF Exportations.

En outre, la complexité algorithmique n'a souvent pas d'importance autant que nous pourrions penser: lorsqu'un problème est soumis à un certain nombre, les caractéristiques de performance réelles et mesurées sont plus importantes que le comportement "pour très grand n = ".

6
amon

Si vous êtes demandé un algorithme "amortit constant", votre algorithme peut parfois prendre beaucoup de temps. Par exemple, si vous utilisez STD :: Vector in C++, un tel vecteur peut avoir alloué de l'espace pour 10 objets et lorsque vous allouez le 11ème objet, l'espace pour 20 objets est attribué, 10 objets sont copiés et le 11ème ajouté, qui prend du temps considérable. Mais si vous ajoutez un million d'objets, vous pouvez avoir 999 980 opérations rapides et 20 lentes, avec le temps moyen rapide.

Si vous êtes demandé un algorithme "temps constant", votre algorithme doit toujours être rapide, pour chaque opération. Ce serait important pour les systèmes en temps réel où vous pourriez avoir besoin d'une garantie que chaque opération est toujours rapide. "Temps constant" est très souvent nécessaire, mais c'est définitivement non la même chose que "du temps constant amorti".

1
gnasher729