web-dev-qa-db-fra.com

Quel est l'algorithme le plus efficace pour trouver une ligne droite passant par la plupart des points?

Le problème:

N points sont donnés sur un plan à 2 dimensions. Quel est le nombre maximum de points sur le même straight line?

Le problème a O (N2) solution: parcourez chaque point et trouvez le nombre de points qui ont le même dx / dy par rapport au point actuel. Enregistrez les relations dx / dy dans une carte de hachage pour plus d’efficacité.

Y at-il une meilleure solution à ce problème que O (N2)?

46
Leonid

Il n’ya probablement aucune solution à ce problème qui soit significativement meilleure que O (n ^ 2) dans un modèle de calcul standard.

Le problème de la recherche de trois points colinéaires se réduit au problème de la recherche de la ligne passant par le plus de points, et la recherche de trois points colinéaires est difficile à résoudre, ce qui signifie que le résoudre en moins de 0 (n ^ 2) serait un problème majeur. résultat théorique.

Voir la question précédente sur la recherche de trois points colinéaires.

Pour votre référence (en utilisant la preuve connue), supposons que nous voulions répondre à un problème de 3SUM tel que trouver x, y, z dans la liste X tels que x + y + z = 0. Si nous avions un algorithme rapide pour le problème de points colinéaires , nous pourrions utiliser cet algorithme pour résoudre le problème 3SUM comme suit. 

Pour chaque x dans X, créez le point (x, x ^ 3) (pour l'instant nous supposons que les éléments de X sont distincts). Ensuite, vérifiez s’il existe trois points colinéaires parmi les points créés.

Pour voir que cela fonctionne, notez que si x + y + z = 0 alors la pente de la ligne de x à y est

(y ^ 3 - x ^ 3)/(y - x) = y ^ 2 + yx + x ^ 2

et la pente de la ligne de x à z est

(z ^ 3 - x ^ 3)/(z - x) = z ^ 2 + zx + x ^ 2 = (- (x + y)) ^ 2 - (x + y) x + x ^ 2 = x ^ 2 + 2xy + y ^ 2 - x ^ 2 - xy + x ^ 2 = y ^ 2 + yx + x ^ 2

Inversement, si la pente de x à y est égale à la pente de x à z alors

y ^ 2 + yx + x ^ 2 = z ^ 2 + zx + x ^ 2,

ce qui implique que

(y - z) (x + y + z) = 0,

donc soit y = z ou z = -x - y comme il suffit de prouver que la réduction est valide.

S'il y a des doublons dans X, vous devez d'abord vérifier si x + 2y = 0 pour tout x et élément de duplication y (en temps linéaire en utilisant le hachage ou en temps de tri (n)), puis supprimez les doublons avant de les réduire au minimum. problème de recherche de points colinéaire.

38
jonderry

Si vous limitez le problème aux lignes passant par l'origine, vous pouvez convertir les points en coordonnées polaires (angle, distance depuis l'origine) et les trier par angle. Tous les points avec le même angle se trouvent sur la même ligne. O (n logn) 

Je ne pense pas qu'il y ait une solution plus rapide dans le cas général.

4
Erwin J.

Le Transformation de Hough peut vous donner une solution approximative. Elle est approximative car la technique de binning a une résolution limitée en espace de paramètres. Par conséquent, le maximum bin vous donnera une plage limitée de lignes possibles. 

4
ergosys

Comme déjà mentionné, il n'y a probablement pas de moyen de résoudre le cas général de ce problème mieux que O (n ^ 2). Cependant, si vous supposez qu'un grand nombre de points se trouvent sur la même ligne (par exemple, la probabilité qu'un point aléatoire de l'ensemble des points se trouve sur la ligne avec le nombre maximal de points est p) et n'a pas besoin d'un algorithme exact, un algorithme randomisé est plus efficace.

maxPoints = 0
Repeat for k iterations:
    1. Pick 2 random, distinct points uniformly at random
    2. maxPoints = max(maxPoints, number of points that lies on the 
       line defined by the 2 points chosen in step 1)

Notez que dans la première étape, si vous choisissez 2 points situés sur la droite du nombre maximal de points, vous obtenez la solution optimale. En supposant que n est très grand (c’est-à-dire que nous pouvons traiter la probabilité de trouver 2 points souhaitables comme étant un échantillonnage avec remplacement), la probabilité que cela se produise est p ^ 2. Par conséquent, la probabilité de trouver une solution sous-optimale après k itérations est de (1 - p ^ 2) ^ k. 

Supposons que vous puissiez tolérer un taux de taux de faux négatifs = err. Ensuite, cet algorithme s'exécute dans O(nk) = O (n * log (err)/log (1 - p ^ 2)). Si n et p sont tous deux assez grands, ceci est nettement plus efficace que O (n ^ 2). (ie Supposé n = 1 000 000 et vous savez qu'il y a au moins 10 000 points qui se trouvent sur la même ligne. Alors n ^ 2 nécessiterait une magnitude de 10 ^ 12 opérations, tandis qu'un algorithme aléatoire nécessiterait de 10 10 pour obtenir un taux d'erreur inférieur à 5 * 10 ^ -5.)

0
Tony Cai

Encore une solution O (n ^ 2) avec pseudo-code. L'idée est de créer une table de hachage avec la ligne elle-même comme clé. La ligne est définie par la pente entre les deux points, le point où la ligne coupe l'axe des x et le point où la ligne coupe l'axe des y.

La solution suppose des langages tels que Java, C # où la méthode equals et les méthodes de hachage de l’objet sont utilisées pour la fonction de hachage. 

Créer un objet (appelez SlopeObject) avec 3 champs

  1. Slope // Peut être l'infini
  2. Point d'interception avec l'axe des x - poix // sera (Infini, une certaine valeur de y) ou (valeur x, 0)
  3. Compter

poix sera une paire de points (x, y). Si la ligne traverse l'axe des x, la variable poix sera (un nombre égal à 0). Si line est parallèle à l'axe des x, poix = (Infinity, un nombre) où la valeur y est le cas où la ligne croise l'axe des y . Méthode de substitution égale où 2 objets sont égaux si Slope et poix.

Le hashcode est remplacé par une fonction qui fournit un hashcode basé sur une combinaison de valeurs de Slope et poix. Quelques pseudo-codes ci-dessous

Hashmap map;
foreach(point in the array a) {
    foeach(every other point b) {
        slope = calculateSlope(a, b);
        poix = calculateXInterception(a, b);
        SlopeObject so = new SlopeObject(slope, poix, 1); // Slope, poix and intial count 1.
        SlopeObject inMapSlopeObj = map.get(so);
        if(inMapSlopeObj == null) {
            inMapSlopeObj.put(so);
        } else {
            inMapSlopeObj.setCount(inMapSlopeObj.getCount() + 1);
        }
    }
}
SlopeObject maxCounted = getObjectWithMaxCount(map);
print("line is through " + maxCounted.poix + " with slope " + maxCounted.slope);
0
Dheerendra Kulkarni