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 ?