Considérons un polygone potentiellement auto-intersecté, défini par une liste de sommets dans un espace 2D. Par exemple
{{0, 0}, {5, 0}, {5, 4}, {1, 4}, {1, 2}, {3, 2}, {3, 3}, {2, 3}, {2, 1}, {4, 1}, {4, 5}, {0, 5}}
Il existe plusieurs façons de définir l'aire d'un tel polygone, mais la plus intéressante est la règle pair-impair. En prenant n'importe quel point dans l'avion, tracez une ligne du point vers l'infini (dans n'importe quelle direction). Si cette ligne traverse le polygone un nombre impair de fois, le point fait partie de la zone du polygone, s'il traverse le polygone un nombre pair de fois, le point ne fait pas partie du polygone. Pour l'exemple de polygone ci-dessus, voici à la fois son contour ainsi que sa zone paire-impaire:
Le polygone ne sera en général pas orthogonal. Je n'ai choisi qu'un exemple aussi simple pour faciliter le comptage de la zone.
La zone de cet exemple est 17
(pas 24
ou 33
comme d'autres définitions ou zones pourraient donner).
Notez que sous cette définition, l'aire du polygone est indépendante de son ordre d'enroulement.
Le défi
Étant donné une liste de sommets avec des coordonnées entières définissant un polygone, déterminez son aire sous la règle pair-impair.
Vous pouvez écrire une fonction ou un programme, en saisissant des données via STDIN ou l'alternative la plus proche, l'argument de ligne de commande ou l'argument de fonction et renvoyer le résultat ou l'imprimer dans STDOUT ou l'alternative la plus proche.
Vous pouvez prendre des entrées dans n'importe quel format de liste ou de chaîne pratique, tant qu'il n'est pas prétraité.
Le résultat doit être soit un nombre à virgule flottante, précis à 6 chiffres significatifs (décimaux), soit un résultat rationnel dont la représentation à virgule flottante est précise à 6 chiffres significatifs. (Si vous produisez des résultats rationnels, ils seront probablement exacts, mais je ne peux pas l'exiger, car je n'ai pas de résultats exacts pour référence.)
Vous devez être en mesure de résoudre chacun des cas de test ci-dessous dans les 10 secondes sur une machine de bureau raisonnable. (Il y a une certaine marge de manœuvre dans cette règle, alors utilisez votre meilleur jugement. Si cela prend 20 secondes sur mon ordinateur portable, je vous donnerai le bénéfice du doute, si cela prend une minute, je ne le ferai pas.) Je pense que cette limite devrait être très généreux, mais il est censé exclure les approches où vous discrétisez simplement le polygone sur une grille et un décompte suffisamment fins, ou utilisez des approches probabilistes comme Monte Carlo. Soyez un bon sportif et n'essayez pas d'optimiser ces approches de manière à pouvoir respecter le délai de toute façon. ;)
Vous ne devez utiliser aucune fonction existante directement liée aux polygones.
Il s'agit du code golf, donc la soumission la plus courte (en octets) l'emporte.
Hypothèses
- Toutes les coordonnées sont des nombres entiers dans la gamme
0 ≤ x ≤ 100
,0 ≤ y ≤ 100
. - Il y aura au moins
3
et au plus des50
sommets. - Il n'y aura pas de sommets répétés. Aucun sommet ne se trouvera non plus sur un autre bord. (Il peut cependant y avoir des points colinéaires dans la liste.)
Cas de test
{{0, 0}, {5, 0}, {5, 4}, {1, 4}, {1, 2}, {3, 2}, {3, 3}, {2, 3}, {2, 1}, {4, 1}, {4, 5}, {0, 5}}
17.0000
{{22, 87}, {6, 3}, {98, 77}, {20, 56}, {96, 52}, {79, 34}, {46, 78}, {52, 73}, {81, 85}, {90, 43}}
2788.39
{{90, 43}, {81, 85}, {52, 73}, {46, 78}, {79, 34}, {96, 52}, {20, 56}, {98, 77}, {6, 3}, {22, 87}}
2788.39
{{70, 33}, {53, 89}, {76, 35}, {14, 56}, {14, 47}, {59, 49}, {12, 32}, {22, 66}, {85, 2}, {2, 81},
{61, 39}, {1, 49}, {91, 62}, {67, 7}, {19, 55}, {47, 44}, {8, 24}, {46, 18}, {63, 64}, {23, 30}}
2037.98
{{42, 65}, {14, 59}, {97, 10}, {13, 1}, {2, 8}, {88, 80}, {24, 36}, {95, 94}, {18, 9}, {66, 64},
{91, 5}, {99, 25}, {6, 66}, {48, 55}, {83, 54}, {15, 65}, {10, 60}, {35, 86}, {44, 19}, {48, 43},
{47, 86}, {29, 5}, {15, 45}, {75, 41}, {9, 9}, {23, 100}, {22, 82}, {34, 21}, {7, 34}, {54, 83}}
3382.46
{{68, 35}, {43, 63}, {66, 98}, {60, 56}, {57, 44}, {90, 52}, {36, 26}, {23, 55}, {66, 1}, {25, 6},
{84, 65}, {38, 16}, {47, 31}, {44, 90}, {2, 30}, {87, 40}, {19, 51}, {75, 5}, {31, 94}, {85, 56},
{95, 81}, {79, 80}, {82, 45}, {95, 10}, {27, 15}, {18, 70}, {24, 6}, {12, 73}, {10, 31}, {4, 29},
{79, 93}, {45, 85}, {12, 10}, {89, 70}, {46, 5}, {56, 67}, {58, 59}, {92, 19}, {83, 49}, {22,77}}
3337.62
{{15, 22}, {71, 65}, {12, 35}, {30, 92}, {12, 92}, {97, 31}, {4, 32}, {39, 43}, {11, 40},
{20, 15}, {71, 100}, {84, 76}, {51, 98}, {35, 94}, {46, 54}, {89, 49}, {28, 35}, {65, 42},
{31, 41}, {48, 34}, {57, 46}, {14, 20}, {45, 28}, {82, 65}, {88, 78}, {55, 30}, {30, 27},
{26, 47}, {51, 93}, {9, 95}, {56, 82}, {86, 56}, {46, 28}, {62, 70}, {98, 10}, {3, 39},
{11, 34}, {17, 64}, {36, 42}, {52, 100}, {38, 11}, {83, 14}, {5, 17}, {72, 70}, {3, 97},
{8, 94}, {64, 60}, {47, 25}, {99, 26}, {99, 69}}
3514.46
la source
upath
opérateur. (Il s'agit en fait d'une conversion 1: 1 extrêmement simple entre les séparateurs. Devient}, {
justelineto
, et la virgule entre les x et y est supprimée, et les accolades d'ouverture et de fermeture sont remplacées par un en-tête et un pied de page statiques ...)upath
et leslineto
sons comme si vous prétraitez réellement l'entrée. C'est-à-dire que vous ne prenez pas une liste de coordonnées mais un véritable polygone.CrossingPolygon
.Réponses:
Mathematica,
247225222Ajoutez d'abord les points d'intersection au polygone, puis inversez certaines des arêtes, puis sa zone peut être calculée comme un simple polygone.
Exemple:
la source
{1,2},{4,4},{4,2},{2,4},{2,1},{5,3}
? Vous devriez sortir avec 3.433333333333309. J'ai regardé en utilisant une logique similaire.103/30
et la valeur numérique est3.43333
.Python 2,
323319 octetsPrend une liste de sommets via STDIN sous forme de nombres complexes, sous la forme suivante
et écrit le résultat dans STDOUT.
Même code après remplacement de la chaîne et un peu d'espacement:
Explication
Pour chaque point d'intersection de deux côtés du polygone d'entrée (y compris les sommets), passez une ligne verticale à travers ce point.
(En fait, en raison du golf, le programme passe quelques lignes de plus; cela n'a pas vraiment d'importance, tant que nous passons au moins ces lignes.) Le corps du polygone entre deux lignes consécutives est composé de trapèzes verticaux ( et les triangles et les segments de ligne, comme cas particuliers de ceux-ci). Cela doit être le cas, car si l'une de ces formes avait un sommet supplémentaire entre les deux bases, il y aurait une autre ligne verticale passant par ce point, entre les deux lignes en question. La somme des aires de tous ces trapèzes est l'aire du polygone.
Voici comment nous trouvons ces trapèzes: Pour chaque paire de lignes verticales consécutives, nous trouvons les segments de chaque côté du polygone qui se trouvent (correctement) entre ces deux lignes (qui pourraient ne pas exister pour certains des côtés). Dans l'illustration ci-dessus, ce sont les six segments rouges, si l'on considère les deux lignes verticales rouges. Notez que ces segments ne se croisent pas correctement (c'est-à-dire qu'ils ne peuvent se rencontrer qu'à leurs points d'extrémité, coïncider complètement ou ne pas se croiser du tout, car, encore une fois, s'ils se croisaient correctement, il y aurait une autre ligne verticale entre les deux;) et il est donc logique de parler de les ordonner de haut en bas, ce que nous faisons. Selon la règle pair-impair, une fois que nous traversons le premier segment, nous sommes à l'intérieur du polygone; une fois que nous avons traversé le second, nous sommes sortis; le troisième, encore une fois; le quatrième, sorti; etc...
Dans l'ensemble, il s'agit d'un algorithme O ( n 3 log n ).
la source
Haskell, 549
Il ne semble pas que je puisse jouer assez loin, mais le concept était différent des deux autres réponses, alors j'ai pensé que je le partagerais de toute façon. Il effectue O (N ^ 2) des opérations rationnelles pour calculer l'aire.
Exemple:
L'idée est de recâbler le polygone à chaque croisement, résultant en une union de polygones sans arêtes entrecroisées. Nous pouvons ensuite calculer l'aire (signée) de chacun des polygones en utilisant la formule de lacet de Gauss ( http://en.wikipedia.org/wiki/Shoelace_formula ). La règle pair-impair exige que lorsqu'un croisement est converti, l'aire du nouveau polygone soit comptée négativement par rapport à l'ancien polygone.
Par exemple, considérez le polygone dans la question d'origine. Le croisement en haut à gauche est converti en deux chemins qui ne se rencontrent qu'en un point; les deux chemins sont tous deux orientés dans le sens des aiguilles d'une montre, de sorte que les zones de chacun seraient positives à moins que nous déclarions que le chemin intérieur est pondéré par -1 par rapport au chemin extérieur. Cela équivaut à l'inversion de chemin d'alphaalpha.
Comme autre exemple, considérons le polygone du commentaire de MickyT:
Ici, certains des polygones sont orientés dans le sens horaire et d'autres dans le sens antihoraire. La règle de basculement de signe garantit que les régions orientées dans le sens horaire captent un facteur supplémentaire de -1, ce qui les amène à contribuer positivement à la zone.
Voici comment fonctionne le programme:
la source