En réfléchissant à un problème, j'ai réalisé que je devais créer un algorithme efficace résolvant la tâche suivante:
Le problème: on nous donne une boîte carrée bidimensionnelle de côté dont les côtés sont parallèles aux axes. Nous pouvons l'examiner par le haut. Cependant, il y a aussi segments horizontaux. Chaque segment a un nombre entier de coordonnées ( ) et coordonnées ( ) et relie les points et (x_2, y) (regardez le image ci-dessous).
Nous aimerions savoir, pour chaque segment d'unité en haut de la boîte, à quelle profondeur pouvons-nous regarder verticalement à l'intérieur de la boîte si nous regardons à travers ce segment.
Formellement, pour , nous aimerions trouver .
Exemple: étant donné et segments situés comme dans l'image ci-dessous, le résultat est . Regardez comment la lumière profonde peut pénétrer dans la boîte.
Heureusement pour nous, et sont assez petits et nous pouvons faire les calculs hors ligne.
L'algorithme le plus simple pour résoudre ce problème est la force brute: pour chaque segment, parcourez l'ensemble du tableau et mettez-le à jour si nécessaire. Cependant, cela nous donne un O (mn) pas très impressionnant .
Une grande amélioration consiste à utiliser une arborescence de segments capable de maximiser les valeurs sur le segment pendant la requête et de lire les valeurs finales. Je ne le décrirai pas plus loin, mais nous voyons que la complexité temporelle est .
Cependant, j'ai trouvé un algorithme plus rapide:
Contour:
Triez les segments par ordre décroissant de coordonnées (temps linéaire en utilisant une variation de tri de comptage). Notez maintenant que si un segment d'unité a été couvert par un segment auparavant, aucun segment suivant ne peut plus lier le faisceau lumineux traversant ce segment d'unité . Ensuite, nous ferons un balayage de ligne du haut vers le bas de la boîte.
Introduisons maintenant quelques définitions: le segment unit est un segment horizontal imaginaire sur le balayage dont les coordonnées sont des entiers et dont la longueur est 1. Chaque segment pendant le processus de balayage peut être non marqué (c'est-à-dire, un faisceau lumineux partant du haut de la boîte peut atteindre ce segment) ou marqué (cas opposé). Considérons un segment unité avec , toujours non marqué. Présentons également les ensembles . Chaque ensemble contiendra une séquence entière de segments consécutifs marqués d' unité (s'il y en a) avec un suivant non marquéx x x 1 = n x 2 = n + 1 S 0 = { 0 } , S 1 = { 1 } , … , S n = { n } x segment.
Nous avons besoin d'une structure de données capable de fonctionner efficacement sur ces segments et ensembles. Nous utiliserons une structure de recherche d'union étendue par un champ contenant l' indice de segment maximal d' unité (indice du segment non marqué ).
Nous pouvons maintenant gérer les segments efficacement. Disons que nous considérons maintenant le ème segment dans l'ordre (appelez-le "requête"), qui commence en et se termine en . Nous devons trouver tous les segments non marqués de qui sont contenus à l'intérieur du ème segment (ce sont exactement les segments sur lesquels le faisceau lumineux finira son chemin). Nous allons faire ce qui suit: premièrement, nous trouvons le premier segment non marqué à l'intérieur de la requête ( Trouvez le représentant de l'ensemble dans lequel est contenu et obtenez l'index max de cet ensemble, qui est le segment non marqué par définition ). Alors cet indicex 1 x 2 x i x 1 x y est à l'intérieur de la requête, ajoutez-le au résultat (le résultat pour ce segment est ) et marquez cet index ( ensembles d' Union contenant et ). Répétez ensuite cette procédure jusqu'à ce que nous trouvions tous les segments non marqués , c'est-à-dire que la prochaine requête Find nous donne l'index .x + 1
Notez que chaque opération find-union se fera dans seulement deux cas: soit nous commençons à considérer un segment (ce qui peut arriver fois), soit nous venons de marquer un segment -unit (cela peut arriver fois). La complexité globale est donc ( est une fonction Ackermann inverse ). Si quelque chose n'est pas clair, je peux en dire plus. Je pourrai peut-être ajouter quelques photos si j'ai un peu de temps.x n O ( ( n + m ) α ( n ) ) α
Maintenant, j'ai atteint "le mur". Je ne peux pas proposer d'algorithme linéaire, bien qu'il semble qu'il devrait y en avoir un. J'ai donc deux questions:
- Existe-t-il un algorithme de temps linéaire (c'est-à-dire ) qui résout le problème de visibilité du segment horizontal?
- Sinon, quelle est la preuve que le problème de visibilité est ?
Réponses:
find ( ) peut être implémenté en utilisant un tableau de bits avec bits. Maintenant, chaque fois que nous supprimons ou ajoutons un élément à nous pouvons mettre à jour cet entier en définissant un bit sur true ou false respectivement. Maintenant, vous avez deux options en fonction du langage de programmation utilisé et l'hypothèse est relativement petite, c'est-à-dire plus petite que qui est d'au moins 64 bits ou une quantité fixe de ces entiers:n L n l o n g l o n g i n tmax n L n longlongint
Je sais que c'est tout à fait un hack car il suppose une valeur maximale pour et donc peut être considéré comme une constante alors ...nn n
la source
long long int
comme un type entier d'au moins 64 bits. Cependant, ne sera-t-il pas que si est énorme et que nous désignons la taille du mot comme (généralement ), alors chacun prendra ? Nous finirions alors avec un total de . w w = 64 Ofind
OJe n'ai pas d'algorithme linéaire, mais celui-ci semble être O (m log m).
Triez les segments en fonction de la première coordonnée et de la hauteur. Cela signifie que (x1, l1) précède toujours (x2, l2) chaque fois que x1 <x2. De plus, (x1, l1) à la hauteur y1 précède (x1, l2) à la hauteur y2 chaque fois que y1> y2.
Pour chaque sous-ensemble avec la même première coordonnée, nous procédons comme suit. Soit le premier segment (x1, L). Pour tous les autres segments du sous-ensemble: Si le segment est plus long que le premier, changez-le de (x1, xt) à (L, xt) et ajoutez-le au sous-ensemble L dans l'ordre approprié. Sinon, laissez tomber. Enfin, si le sous-ensemble suivant a une première coordonnée inférieure à L, divisez ensuite (x1, L) en (x1, x2) et (x2, L). Ajoutez le (x2, L) au sous-ensemble suivant dans l'ordre correct. Nous pouvons le faire parce que le premier segment du sous-ensemble est plus élevé et couvre la plage de (x1, L). Ce nouveau segment peut être celui qui couvre (L, x2), mais nous ne le saurons pas avant de regarder le sous-ensemble qui a la première coordonnée L.
Après avoir parcouru tous les sous-ensembles, nous aurons un ensemble de segments qui ne se chevauchent pas. Pour déterminer quelle est la valeur Y pour un X donné, nous n'avons qu'à parcourir les segments restants.
Alors, quelle est la complexité ici: Le tri est O (m log m). La boucle à travers les sous-ensembles est O (m). Une recherche est également O (m).
Il semble donc que cet algorithme soit indépendant de n.
la source