2 डी टकराव का पता लगाने


21

यह चुनौती वास्तविक टकराव का पता लगाने पर आधारित है जिसे मुझे हाल ही में एक साधारण खेल के लिए लिखना था।

एक प्रोग्राम या फ़ंक्शन लिखें, जिसमें दो ऑब्जेक्ट दिए गए हों, दो वस्तुओं के टकराव (यानी प्रतिच्छेदन) में हैं या नहीं , इसके आधार पर एक सत्य या मिथ्या मूल्य देता है

आपको तीन प्रकार की वस्तुओं का समर्थन करने की आवश्यकता है:

  • लाइन सेगमेंट : 4 फ्लोट्स द्वारा दर्शाया गया है, जो दो समापन बिंदुओं ( यानी 1 x , y 1 ) और (x 2 , y 2 ) को दर्शाता है । आप मान सकते हैं कि एंडपॉइंट समान नहीं हैं (इसलिए लाइन सेगमेंट पतित नहीं है)।
  • डिस्क : यानी भरे हुए सर्कल, 3 फ्लोट्स द्वारा दर्शाए गए, केंद्र के लिए दो (x, y) और त्रिज्या r के लिए एक (पॉजिटिव) ।
  • गुहाएं : ये एक डिस्क के पूरक हैं। यही है, एक गुहा एक केंद्र और त्रिज्या द्वारा निर्दिष्ट एक परिपत्र क्षेत्र को छोड़कर , सभी 2 डी अंतरिक्ष को भरता है ।

आपके प्रोग्राम या फ़ंक्शन को पहचानकर्ता पूर्णांक (अपनी पसंद का) और उनकी 3 या 4 फ़्लोट्स के रूप में दो ऐसी वस्तुएं प्राप्त होंगी। आप STDIN, ARGV या फ़ंक्शन तर्क के माध्यम से इनपुट ले सकते हैं। आप किसी भी सुविधाजनक रूप में इनपुट का प्रतिनिधित्व कर सकते हैं जो प्रीप्रोसेस नहीं है, उदाहरण के लिए 8 से 10 व्यक्तिगत संख्याएं, दो अल्पविराम द्वारा अलग-अलग मान या दो सूची। परिणाम लौटाया जा सकता है या STDOUT को लिखा जा सकता है।

आप मान सकते हैं कि वस्तुएं कम-से-कम 10 -10 लंबाई की इकाइयाँ हैं या उससे अधिक अंतर करती हैं, इसलिए आपको फ़्लोटिंग पॉइंट प्रकारों की सीमाओं के बारे में चिंता करने की आवश्यकता नहीं है।

यह कोड गोल्फ है, इसलिए सबसे छोटा उत्तर (बाइट्स में) जीतता है।

परीक्षण के मामलों

सूची-आधारित इनपुट प्रारूप का उपयोग करते हुए 0, रेखाओं के साथ 1और सेगमेंट के साथ , सेगमेंट का प्रतिनिधित्व 2करते हुए, निम्नलिखित सभी को एक सच्चाई का उत्पादन करना चाहिए:

[0,[0,0],[2,2]], [0,[1,0],[2,4]]        # Crossing line segments
[0,[0.5,0],[-0.5,0]], [1,[0,0],1]       # Line contained in a disc
[0,[0.5,0],[1.5,0]], [1,[0,0],1]        # Line partially within disc
[0,[-1.5,0.5],[1.5,0.5]], [1,[0,0],1]   # Line cutting through disc
[0,[0.5,2],[-0.5,2]], [2,[0,0],1]       # Line outside cavity
[0,[0.5,0],[1.5,0]], [2,[0,0],1]        # Line partially outside cavity
[0,[-1.5,0.5],[1.5,0.5]], [2,[0,0],1]   # Line cutting through cavity
[1,[0,0],1], [1,[0,0],2]                # Disc contained within another
[1,[0,0],1.1], [1,[2,0],1.1]            # Intersecting discs
[1,[3,0],1], [2,[0,0],1]                # Disc outside cavity
[1,[1,0],0.1], [2,[0,0],1]              # Disc partially outside cavity
[1,[0,0],2], [2,[0,0],1]                # Disc encircling cavity
[2,[0,0],1], [2,[0,0],1]                # Any two cavities intersect
[2,[-1,0],1], [2,[1,0],1]               # Any two cavities intersect

जबकि निम्नलिखित सभी में एक मिथ्या आउटपुट होना चाहिए

[0,[0,0],[1,0]], [0,[0,1],[1,1]]        # Parallel lines
[0,[-2,0],[-1,0]], [0,[1,0],[2,0]]      # Collinear non-overlapping lines
[0,[0,0],[2,0]], [0,[1,1],[1,2]]        # Intersection outside one segment
[0,[0,0],[1,0]], [0,[2,1],[2,3]]        # Intersection outside both segments
[0,[-1,2],[1,2]], [1,[0,0],1]           # Line passes outside disc
[0,[2,0],[3,0]], [1,[0,0],1]            # Circle lies outside segment
[0,[-0.5,0.5],[0.5,-0.5]], [2,[0,0],1]  # Line inside cavity
[1,[-1,0],1], [1,[1,1],0.5]             # Non-intersecting circles
[1,[0.5,0],0.1], [2,[0,0],1]            # Circle contained within cavity

ट्रिकियर मैंने पहले सोचा था। लाइन / लाइन के मामले से शुरू होकर, मैं एक आश्चर्यजनक संख्या के किनारे के मामलों पर आता हूं। क्या आप कोलियर सेगमेंट को अस्वीकार नहीं कर सकते? चीजों को बहुत आसान बना देगा। ;)
एमिल

@Emil क्षमा करें, लेकिन पोस्ट करने के 9 घंटे बाद, मुझे यह मान लेना होगा कि अन्य लोगों ने चुनौती पर काम करना शुरू कर दिया होगा, और युक्ति को बदलना (ब्रेकिंग समस्याओं को ठीक करने के अलावा) मुझे एक अच्छा विचार नहीं लगता। आप इसे कैसे करते हैं, इस पर निर्भर करते हुए, समानांतर लाइन सेगमेंट एकमात्र किनारा मामला होना चाहिए जिसे आपको लाइन-लाइन टकरावों के बारे में चिंता करने की आवश्यकता है, हालांकि, मुझे लगता है।
मार्टिन एंडर

निश्चित रूप से, मैं वास्तव में आपसे इसे बदलने की उम्मीद नहीं कर रहा था। मैं बस थोड़ा सा निराश था कि कोलीनियर लाइन सेगमेंट के विभिन्न वेरिएंट को संभालने से अब तक मेरे कोड की लंबाई दोगुनी हो गई है। :) (एक बड़ी चुनौती, वैसे!)
एमिल

क्या "10 ^ -10 से नहीं टकराता"
ट्वाईनाइट

@TwiNight नहीं अगर दो रेखाएं आपस में टकराती हैं लेकिन ओवरलैप नहीं होती हैं। उदा[0,[-2,0],[-1,0]], [0,[1,0],[2,0]]
मार्टिन एंडर

जवाबों:


6

APL, 279 208 206 203

s←1 ¯1
f←{x←⊣/¨z←⍺⍵[⍋⊣/¨⍺⍵]
2 2≡x:∧/0∧.=⌊(2⊃-⌿↑z)⌹⍣(≠.×∘⌽/x)⍉↑x←s×-/2⊢/↑z
2≡2⌷x:∨/((2⊃z)∇2,x[1]×(2⌷⊃z)+,∘-⍨⊂y÷.5*⍨+.×⍨y←⌽s×⊃-/y),x[1]=(×⍨3⊃⊃z)>+.×⍨¨y←(s↓⌽↑z)-2⌷⊃z
~x∨.∧x[1]≠(.5*⍨+.×⍨2⊃-⌿↑z)<-/⊢/¨z×s*1⌷x}

फ़ंक्शन में लाइन ब्रेक fस्पष्टता के लिए हैं। उन्हें स्टेटमेंट सेपरेटर से बदला जाना चाहिए

जब से मैंने आखिरी बार इस तरह का एक जटिल एपीएल कार्यक्रम बनाया है तब से यह बहुत लंबा है। मुझे लगता है कि पिछली बार यह था लेकिन मैं यह भी निश्चित नहीं हूं कि यह उतना ही जटिल था।

इनपुट प्रारूप
मूल रूप से ओपी के समान है 0, 1डिस्क और 2लाइन सेगमेंट के लिए , कैविटी का उपयोग करने के अलावा ।

प्रमुख अद्यतन

मैं एक अलग एल्गोरिथ्म का उपयोग करते हुए बहुत सारे गोल्फों में कामयाब रहा। कोई और gबैल ** टी !!

मुख्य कार्य fमामलों में विभाजित है:


2 2≡x: खंड-खंड

इस मामले में, प्रत्येक पंक्ति के अंतिम बिंदुओं से वेक्टर की गणना करें और यह जांचने के लिए रैखिक समीकरणों की एक प्रणाली को हल करें कि क्या चौराहे को वैक्टर के भीतर समाहित किया गया है।

चेतावनियां:

  • एक वेक्टर के अंतिम बिंदु को वेक्टर का एक हिस्सा नहीं माना जाता है (जबकि इसका मूल है)। हालांकि, यदि केवल एक वेक्टर की नोक दूसरे पर है, तो इनपुट कल्पना के अनुसार अमान्य है।
  • गैर-पतित समानांतर खंड हमेशा मिथ्या रिटर्न करते हैं, चाहे वह समवर्ती हो।
  • यदि किसी एक खंड का अध: पतन होता है, तो हमेशा गलत तरीके से लौटते हैं। यदि दोनों खंड पतित हैं, तो हमेशा सही लौटें।

उदाहरण: (दाईं ओर आकृति में कार्रवाई में नोट 1)


2≡2⌷x: खंड-अन्य

इस मामले में, दूसरी वस्तु एक गोलाकार है। जांचें कि क्या खंड के अंतिम बिंदु दूरी की जांच का उपयोग करते हुए सर्कल के भीतर हैं।

डिस्क केस में, दिए गए सेगमेंट के व्यास के एक लाइन सेगमेंट का भी निर्माण करें। जांचें कि क्या खंड पुनरावृत्ति से टकराते हैं।
गुहा के मामले में, उक्त खंड के निर्माण में एक "गुना 0" में घुसना, इसे पतित बनाने के लिए। (देखें कि मैं 0कैविटी के लिए और 1डिस्क के लिए अभी क्यों उपयोग करता हूं ?) दिए गए सेगमेंट में गिरावट नहीं है, सेगमेंट-सेगमेंट टकराव का पता हमेशा गलत लगता है।

अंत में दूरी जांच और टक्कर का पता लगाने के परिणामों को मिलाएं। गुहा मामले के लिए, पहले दूरी की जांच के परिणामों को नकारें। तब (दोनों मामलों में) या 3 परिणाम एक साथ।

सेगमेंट-सेगमेंट कैविट्स के संबंध में, संख्या 3 को संबोधित किया जाता है (और शोषित)। नंबर 2 कोई समस्या नहीं है क्योंकि हम यहाँ लंबवत खंडों को काट रहे हैं, जो कभी पतित नहीं होने पर समानांतर नहीं होते हैं। नंबर 1 केवल डिस्क मामले में प्रभावी होता है, जब दिए गए अंतिम बिंदु में से एक निर्माण व्यास पर होता है। यदि अंत बिंदु सर्कल के अंदर अच्छी तरह से है, तो दूरी की जांच ने इसका ध्यान रखा होगा। यदि अंत बिंदु सर्कल पर है, चूंकि निर्मित व्यास दिए गए सेगमेंट के समानांतर है, तो उत्तरार्द्ध को केवल एक बिंदु के साथ सर्कल के स्पर्शरेखा होना चाहिए, जो कि वैध इनपुट नहीं है।

उदाहरण:


डिफ़ॉल्ट मामला: अन्य-अन्य

केंद्रों के बीच की दूरी की गणना करें। डिस्क-डिस्क टक्कर तब होती है यदि और केवल अगर दूरी रेडी के योग से छोटी हो। डिस्क-कैविटी टकराव तब होता है यदि और केवल अगर दूरी रेडीआई में अंतर से अधिक है।

गुहा-गुहा के मामले की देखभाल करने के लिए, दूरी की जांच के परिणाम को नकारें, और प्रत्येक पहचानने वाले पूर्णांक के साथ और फिर उन्हें एक साथ। कुछ तर्क का उपयोग करते हुए, कोई यह दिखा सकता है कि यह प्रक्रिया सही है और यदि केवल दोनों पूर्णांकों की पहचान करना गलत है (कैविटी-कैविटी केस), या यदि दूरी की जाँच सही हुई


मेरी समझ यह है कि यदि आपका प्रोग्राम ASCII के बजाय यूनिकोड को फैलाने वाले वर्णों का उपयोग करके लिखा गया है, तो बाइट काउंट को यूनिकोड के 2-बाइट्स-प्रति-वर्ण प्रकृति को स्वीकार करने की आवश्यकता है।
सीओटीओ

@ कोटो मैंने यूनिकोड को निर्दिष्ट नहीं किया। जहाँ तक के रूप में मुझे पता है हूँ, एपीएल वर्ण सेट एक बाइट में फिट करता है, और वहाँ रहे हैं एकल-बाइट कोड पृष्ठों जो सभी एपीएल वर्ण हो ताकि एन्कोडिंग का उपयोग, बाइट गिनती ठीक है। बाइट्स की गिनती ज्यादातर उन लोगों के लिए प्रासंगिक होती है, जो "सामान्य" भाषाओं में मल्टी-बाइट स्ट्रिंग्स में सामान एन्कोडिंग करते हैं, या जो गणितज्ञ यूनिकोड शॉर्टकट का उपयोग करते हैं।
मार्टिन एंडर

@ मार्टिनबटनर: तो आप कह रहे हैं कि भले ही कोई भी वाजिब तौर पर या व्यावहारिक रूप से किसी भी टेक्स्ट एडिटर में स्ट्रिंग के 1-बाइट-प्रति-चार संस्करण का प्रतिनिधित्व नहीं कर सकता है, इसके अलावा एपीएल के लिए एक स्पष्ट रूप से डिज़ाइन किया गया है, यह 1-बाइट-प्रति-चार के रूप में गिना जाता है। क्योंकि भाषा युक्ति में 256 या इससे कम अनुमत वर्ण हैं?
COTO

@ कोटो खैर और क्योंकि एन्कोडिंग मौजूद है जिसके अनुसार एक एकल-बाइट एन्कोडेड फ़ाइल की व्याख्या की जा सकती है। मुझे नहीं लगता कि मैं उस रास्ते से नीचे जाने को तैयार होता अगर लोगों को अपनी एन्कोडिंग बनानी पड़ती। अन्यथा कोई भी प्रोग्राम जो 257 से कम विभिन्न वर्णों का उपयोग करता है, वह दावा कर सकता है (जो कि पीपीसीजी पर लगभग कोई जवाब होगा, मुझे लगता है)। मुझे लगता है कि हमें एपीएल को कई दशकों तक यूनिकोडिंग की भविष्यवाणी करने के लिए दंडित नहीं करना चाहिए - तो फिर यह बाइट्स की व्याख्या करने के लिए आपके पास अजीब फंकी चरित्रों के रूप में काम करता है, जो कि स्मृति चिन्ह के रूप में काम करते हैं।
मार्टिन एंडर

1
@ COTO वहाँ J है, जो APL पर आधारित है और केवल ASCII वर्णों का उपयोग करता है। वे आमतौर पर इसी तरह से स्कोर करते हैं, ताकि यूनिकोड द्वारा रन बनाए जाने पर भी वह आपको हरा सके। और मुझे यह जोड़ना चाहिए कि न तो भाषा को गोल्फ के लिए डिज़ाइन किया गया था, और AFAIK दोनों वास्तव में पेशेवर रूप से उपयोग किए जाते हैं। और यहाँ गोल्फ की चुनौतियाँ ग्रीन चेकमार्क प्राप्त करने के बारे में बहुत अधिक नहीं हैं, लेकिन आपके कार्यक्रम के हर अंतिम छोटे बाइट को अपनी भाषा के साधनों के साथ निचोड़ने और एक ही "वेट श्रेणी" में सभी को पछाड़ने के बारे में हैं, जो आमतौर पर आपको और अधिक बढ़ावा देगा। वैसे भी एक टाल भाषा का उपयोग करने से। ;)
मार्टिन एंडर

5

जावास्क्रिप्ट - 393 बाइट्स

न्यूनतम किया गया:

F=(s,a,t,b,e,x)=>(x=e||F(t,b,s,a,1),[A,B]=a,[C,D]=b,r=(p,l)=>([g,h]=l,[f,i]=y(h,g),[j,k]=y(p,g),m=Math.sqrt(f*f+i*i),[(f*j+i*k)/m,(f*k-i*j)/m]),u=(p,c)=>([f,g]=c,[i,j]=y(p,f),i*i+j*j<g*g),y=(p,c)=>[p[0]-c[0],p[1]-c[1]],[n,o]=r(C,a),[q,v]=r(D,a),w=(v*n-o*q)/(v-o),z=r(B,a)[0],Y=u(A,b),Z=u(B,b),[v*o<0&&w*(w-z)<0,Y||Z||o<D&&o>-D&&n*(n-z)<0,!Y||!Z,x,u(A,[C,D+B]),B>D||!u(A,[C,D-B]),x,x,1][s*3+t])

विस्तारित:

F = (s,a,t,b,e,x) => (
    x = e || F(t,b,s,a,1),
    [A,B] = a,
    [C,D] = b,
    r = (p,l) => (
        [g,h] = l,
        [f,i] = y(h,g),
        [j,k] = y(p,g),
        m = Math.sqrt( f*f + i*i ),
        [(f*j + i*k)/m, (f*k - i*j)/m] ),
    u = (p,c) => (
        [f,g] = c,
        [i,j] = y(p,f),
        i*i + j*j < g*g ),
    y = (p,c) => [p[0] - c[0], p[1] - c[1]],
    [n,o] = r(C,a),
    [q,v] = r(D,a),
    w = (v*n - o*q)/(v - o),
    z = r(B,a)[0],
    Y = u(A,b), Z = u(B,b),
    [   v*o < 0 && w*(w-z) < 0,
        Y || Z || o < D && o > -D && n*(n-z) < 0,
        !Y || !Z,
        x,
        u(A,[C,D+B]),
        B > D || !u(A,[C,D-B]),
        x,
        x,
        1
    ][s*3+t]);

टिप्पणियाँ:

  • फ़ंक्शन को परिभाषित Fकरता है जो आवश्यक तर्कों को स्वीकार करता है और आवश्यक मान लौटाता है
  • इनपुट प्रारूप ओपी में प्रारूप के समान है, इस अपवाद के साथ कि प्रत्येक आदिम के लिए पूर्णांक प्रकार का कोड टपल से अलग है। उदाहरण के लिए, F( 0,[[0,0],[2,2]], 0,[[1,0],[2,4]] )या F( 1,[[3,0],1], 2,[[0,0],1] )
  • ओपी में आपूर्ति किए गए सभी परीक्षण मामलों पर कोड मान्य है
  • सभी किनारे और कोने के मामलों को संभालना चाहिए, जिसमें शून्य-लंबाई लाइन सेगमेंट और शून्य-त्रिज्या सर्कल शामिल हैं

आह, उन दो किनारे मामलों का उल्लेख करने के लिए धन्यवाद। शून्य-त्रिज्या सर्कल को संभालना नहीं है (पोस्ट सकारात्मक त्रिज्या कहता है), और मैं यह भी स्पष्ट करूंगा कि लाइन खंड के छोर अलग-अलग होंगे।
मार्टिन एंडर

4

पायथन, 284

मैं इन सभी ज्यामितीय चालों की तुलना में एक सुंदर कचरा एल्गोरिथ्म का उपयोग कर रहा हूं, लेकिन यह सही उत्तर प्राप्त करता है, भले ही परीक्षण के मामलों के माध्यम से प्राप्त करने में एक मिनट से अधिक समय लगता है। बड़ा फायदा यह है कि मुझे केवल तीन स्कोरिंग कार्य लिखने हैं, और हिलक्लिंबिंग सभी किनारे मामलों की देखभाल करता है।

golfed:

import math,random as r
n=lambda(a,c),(b,d):math.sqrt((a-b)**2+(c-d)**2)
x=lambda(t,a,b),p:max(eval(["n(b,p)-n(a,b)+","-b+","b-"][t]+'n(a,p)'),0)
def F(t,j):
q=0,0;w=1e9
 for i in q*9000:
    y=x(t,q)+x(j,q)
    if y<w:p,w=q,y
    q=(r.random()-.5)*w+p[0],(r.random()-.5)*w+p[1]
 return w<.0001

Ungolfed:

import math
import random as r
def norm(a, b):
 return math.sqrt((a[0] - b[0])**2 + (a[1] - b[1])**2)

def lineWeight(a, b, p):
 l1 = norm(a, p)
 l2 = norm(b, p)
 return min(l1, l2, l1 + l2 - norm(a, b))

def circleWeight(a, r, p):
 return max(0, norm(a, p) - r)

def voidWeight(a, r, p):
 return max(0, r - norm(a, p))

def weight(f1, f2, s1, s2, p):
 return f1(s1[1], s1[2], p) + f2(s2[1], s2[2], p)

def checkCollision(s1, s2):
 a = [lineWeight, circleWeight, voidWeight]
 f1 = a[s1[0]]
 f2 = a[s2[0]]
 p = (0.0, 0.0)
 w = 0
 for i in a*1000:
  w = weight(f1, f2, s1, s2, p)
  p2 = ((r.random()-.5)*w + p[0], (r.random()-.5)*w + p[1])
  if(weight(f1, f2, s1, s2, p2) < w):
   p = p2
 if w < .0001:
  return True
 return False

और अंत में, एक परीक्षण स्क्रिप्ट के मामले में कोई और अजगर में यह कोशिश करना चाहता है:

import collisiongolfedbak
reload(collisiongolfedbak)

tests = [
[0,[0,0],[2,2]], [0,[1,0],[2,4]],        # Crossing line segments
[0,[0.5,0],[-0.5,0]], [1,[0,0],1],       # Line contained in a disc
[0,[0.5,0],[1.5,0]], [1,[0,0],1],        # Line partially within disc
[0,[-1.5,0.5],[1.5,0.5]], [1,[0,0],1],   # Line cutting through disc
[0,[0.5,2],[-0.5,2]], [2,[0,0],1],       # Line outside cavity
[0,[0.5,0],[1.5,0]], [2,[0,0],1],        # Line partially outside cavity
[0,[-1.5,0.5],[1.5,0.5]], [2,[0,0],1],   # Line cutting through cavity
[1,[0,0],1], [1,[0,0],2],                # Disc contained within another
[1,[0,0],1.1], [1,[2,0],1.1],            # Intersecting discs
[1,[3,0],1], [2,[0,0],1],                # Disc outside cavity
[1,[1,0],0.1], [2,[0,0],1],              # Disc partially outside cavity
[1,[0,0],2], [2,[0,0],1],                # Disc encircling cavity
[2,[0,0],1], [2,[0,0],1] ,               # Any two cavities intersect
[2,[-1,0],1], [2,[1,0],1] ,              # Any two cavities intersect
[0,[0,0],[1,0]], [0,[0,1],[1,1]] ,       # Parallel lines
[0,[-2,0],[-1,0]], [0,[1,0],[2,0]],      # Collinear non-overlapping lines
[0,[0,0],[2,0]], [0,[1,1],[1,2]],        # Intersection outside one segment
[0,[0,0],[1,0]], [0,[2,1],[2,3]],        # Intersection outside both segments
[0,[-1,2],[1,2]], [1,[0,0],1],           # Line passes outside disc
[0,[2,0],[3,0]], [1,[0,0],1],            # Circle lies outside segment
[0,[-0.5,0.5],[0.5,-0.5]], [2,[0,0],1],  # Line inside cavity
[1,[-1,0],1], [1,[1,1],0.5],             # Non-intersecting circles
[1,[0.5,0],0.1], [2,[0,0],1]            # Circle contained within cavity
]

for a, b in zip(tests[0::2], tests[1::2]):
 print collisiongolfedbak.F(a,b)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.