Des Millions de points 3D: Comment trouver le 10 de leur plus proche d'un point donné?
Un point 3-d est définie par (x,y,z). La Distance d entre deux points (X,Y,Z) et (x,y,z) est d= Sqrt[(X-x)^2 + (Y-Y)^2 + (Z-z)^2].
Maintenant il y a un million d'entrées dans un fichier, chaque entrée est un point dans l'espace, dans aucun ordre particulier. Étant donné un point (a,b,c) trouver le plus proche de 10 points. Comment voulez-vous stocker les millions de points et comment feriez-vous pour récupérer ces 10 points à partir de cette structure de données.
- Avez-vous de créer et de remplir la structure de données avant ou après on vous dit que le point (a,b,c) est? David réponse, par exemple, ne fonctionne pas si vous créez la structure de données tout d'abord, et ensuite un utilisateur types (a,b,c) et veut une réponse instantanément.
- Bon point (sans mauvais jeu de mots!) Bien sûr, si (a,b,c) n'est pas connu à l'avance, c'est plus un problème d'optimisation de la liste des points pour la recherche par géolocalisation 3D, plutôt que de faire de la recherche.
- Il devrait vraiment être précisé si le coût de préparation de la structure des données et le stockage des millions de points dans cette structure de données doit être pris en compte, ou seulement la récupération de la performance compte. Si ce coût n'a pas d'importance, alors peu importe combien de fois vous permettra de récupérer les points de kd-tree va gagner. Si ce coût n'a d'importance, alors vous devez aussi spécifier le nombre de fois que vous attendez pour lancer la recherche (pour le petit nombre de recherches de la force brute va gagner, pour les plus grands kd va gagner).
Vous devez vous connecter pour publier un commentaire.
Millions de points est un petit nombre. L'approche la plus simple qui fonctionne ici (code basé sur KDTree est plus lent (pour l'interrogation d'un seul point)).
Force Brute approche (temps ~1 seconde)
De l'exécuter:
Voici le script qui génère des millions de points 3D:
De sortie:
Vous pouvez utiliser ce code pour tester plus complexe des structures de données et algorithmes (par exemple, si elles réellement consommer moins de mémoire ou plus rapide, puis au-dessus de la méthode la plus simple). Il est intéressant de noter que pour l'instant c'est la seule réponse qui contient du code qui fonctionne.
Solution basée sur KDTree (temps ~1,4 secondes)
De l'exécuter:
Partielle de tri en C++ (temps ~1.1 secondes)
De l'exécuter:
File d'attente de priorité en C++ (temps ~1.2 secondes)
De l'exécuter:
Linéaire basée sur la Recherche de l'approche (temps ~1.15 secondes)
Mesures montre que la plupart du temps est consacré à la lecture de tableau à partir du fichier, réel les calculs de prendre sur l'ordre de grandeur de moins de temps.
np.argpartition
plutôt quenp.argsort
.np.argsort()
.np.argpartition
être une "force brute" de la solution. Je pense qu'il serait au moins la peine de mentionner, étant donné que vous avez également montré une codés à la main partielle de tri en C++.Si le million d'entrées sont déjà dans un fichier, il n'y a pas besoin de charger le tout dans une structure de données en mémoire. Il suffit de garder un tableau avec les dix points trouvés jusqu'à présent, et de balayage sur le million de points, la mise à jour de votre top-ten de la liste que vous allez.
C'est O(n) du nombre de points.
Vous pouvez stocker les points dans une k-dimensions de l'arbre (kd-tree). Kd-trees sont optimisés pour le plus proche voisin de recherches (recherche du n points les plus proches d'un point donné).
Je pense que c'est une question délicate que les tests si vous n'essayez pas de faire trop de choses.
Considérer le simple algorithme de personnes l'ont déjà donnée ci-dessus: garder une table des dix meilleurs la mesure des candidats et de passer par tous les points un par un. Si vous trouvez de plus près que l'un des dix meilleurs la mesure, le remplacer. Quelle est la complexité? Eh bien, nous devons nous pencher sur chaque point à partir du fichier une fois, calculer la distance (ou le carré de la distance) et de les comparer avec le 10ème point le plus proche. Si c'est mieux, l'insérer à l'endroit approprié dans le 10-meilleur-si-loin de la table.
Alors, quelle est la complexité? Nous regardons chaque point d'une fois, il est donc n calculs de la distance et n comparaisons. Si le point est mieux, nous avons besoin de l'insérer dans la bonne position, cela demande un peu plus de comparaisons, mais c'est une constante depuis le tableau des meilleurs candidats est d'une taille constante 10.
Nous nous retrouvons avec un algorithme qui s'exécute en temps linéaire, O(n) du nombre de points.
Mais maintenant examiner ce qui est le limite inférieure sur un tel algorithme? Si il n'y a pas d'ordre dans les données d'entrée, nous ont pour regarder chaque point pour voir si ce n'est pas l'un des plus proches. Donc autant que je peux voir, la limite inférieure est Omega(n) et donc l'algorithme ci-dessus est optimal.
Cette question est essentiellement de tester vos connaissances et/ou l'intuition de les algorithmes de partitionnement de l'espace. Je dirais que de stocker les données dans un l'octree est votre meilleur pari. Il est couramment utilisé dans les moteurs 3d qui gèrent ce type de problème (stocker des millions de sommets, le lancer de rayons, de trouver des collisions, etc.). Le temps de recherche sera de l'ordre de
log(n)
dans le pire des cas (je crois).Pas nécessaire de calculer la distance. Juste le carré de la distance devrait servir à vos besoins. Devrait être plus rapide je pense. En d'autres termes, vous pouvez sauter le
sqrt
peu.Ce n'est pas des devoirs à faire à la question, est-il? 😉
Ma pensée: itérer sur tous les points et de les mettre dans un tas min ou délimitée de la file d'attente de priorité, clé en fonction de la distance de la cible.
Simple algorithme:
Stocker les points comme une liste de tuples, et d'analyse sur les points, le calcul de la distance, et de garder un "plus proche" la liste.
Plus créatif:
Groupe de points dans des régions comme le cube décrit par "0,0,0" à "50,50,50", ou "0,0,0" à "-20,-20,-20"), de sorte que vous pouvez "index" dans les de la cible. Vérifier cube de la cible se trouve dans l', et seulement à la recherche à travers les points du cube. Si il y a moins de 10 points du cube, cochez la case "voisins" des cubes, et ainsi de suite.
Sur la poursuite de la réflexion, ce n'est pas un très bon algorithme: si votre cible est plus proche de la paroi d'un cube de 10 points, alors vous aurez à chercher dans les pays voisins cube ainsi.
J'irais avec le kd-tree approche et de trouver le plus proche, puis l'enlever (ou la marque) que le plus proche nœud, et la recherche de la nouvelle la plus proche nœud. Rincer et répéter.
Pour toutes les deux points P1 (x1, y1, z1) et P2 (x2, y2, z2), si la distance entre les points est d alors toutes les conditions suivantes doivent être remplies:
Tenir le 10 le plus proche que vous itérer sur l'ensemble de votre jeu, mais aussi la distance à la 10e plus proche. Epargnez-vous beaucoup de complexité à l'aide de ces trois conditions avant de calculer la distance de chaque point que vous regardez.
essentiellement d'une combinaison des deux premiers réponse au-dessus de moi. puisque les points sont dans un fichier il n'y a pas besoin de les garder en mémoire. Au lieu d'un tableau, ou un tas min, je voudrais utiliser un max de tas, puisque vous ne voulez vérifier pour les distances inférieures à la 10e point le plus proche. Pour un tableau, vous devez comparer chaque nouvelle calcule la distance avec les 10 distances vous avez gardé. Pour un min d'un segment, vous devez effectuer 3 comparaisons avec chaque nouvelle distance calculée. Avec un max de tas, que vous n'effectuez 1 comparaison quand la nouvelle distance calculée est plus grande que le nœud racine.
Cette question mérite une définition plus précise.
1)
La décision concernant les algorithmes de pré-données de l'indice varie beaucoup en fonction de si vous pouvez tenir l'ensemble des données dans la mémoire ou non.
Avec kdtrees et octrees vous n'aurez pas à contenir les données dans la mémoire et la performance des prestations de ce fait, non seulement parce que l'empreinte mémoire est plus bas, mais tout simplement parce que vous n'aurez pas à lire le fichier en entier.
Avec bruteforce, vous aurez à lire l'ensemble du dossier et recalculer les distances pour chaque nouveau point, vous serez à la recherche pour.
Encore, cela peut ne pas être important pour vous.
2)
Un autre facteur est combien de fois vous avez à la recherche pour un point.
Comme indiqué par J. F. Sebastian parfois bruteforce est plus rapide, même sur de grands ensembles de données, mais prendre soin de prendre en compte le fait que ses repères mesure de la lecture de l'ensemble du jeu de données à partir du disque (qui n'est pas nécessaire, une fois kd-tree ou de l'octree est construit et écrit quelque part) et qu'ils ne mesurent que l'on recherche.
Calculer la distance pour chacun d'eux, et de faire une sélection(1..10, n) à O(n) fois. Que serait l'algorithme naïf, je suppose.