레이저는 어디로 갑니까?


34

2 차원 그리드를 가져 와서 여러 개의 선분을 그려 거울을 나타냅니다. 이제 이론적 인 레이저를 배치 할 지점과 가리키는 방향을 정의하는 각도를 선택하십시오. 문제는 특정 거리만큼 레이저 빔 경로를 따르는 경우 어떤 좌표 점에 있습니까?

예:

레이저 예

이 화상에서는 L, 레이저의 위치 t(플러스 X 축으로부터 측정) 그것의 각도이며 M1, M2그리고 M3모든 선분 거울, E후의 레이저 빔의 경로에있는 지점이다 D = d1 + d2 + d3 + d4부터 단위 L.

(바이트)의 최단 프로그램 출력 것을 작성 E주어진 L, t, D거울 및 목록. 바이트 수를 계산
하려면 http://mothereff.in/byte-counter 를 사용 하십시오 .

입력 형식

입력은 stdin에서 다음 형식으로 제공됩니다.

Lx Ly t D M1x1 M1y1 M1x2 M1y2 M2x1 M2y1 M2x2 M2y2 ...
  • 모든 값은이 정규식과 일치하는 부동 소수점 [-+]?[0-9]*\.?[0-9]+입니다.
  • 각 숫자 사이에는 항상 정확히 하나의 공백이 있습니다.
  • 입력 주위에 따옴표를 요구할 수 있습니다.
  • t도 단위이지만 반드시 [0, 360)범위 내에있는 것은 아닙니다 . (원하는 경우 라디안을 대신 사용할 수 있습니다. 대답에서 그렇게 말하십시오.)
  • D레이저를 180도 효과적으로 회전 시키면 음수 일 수 있습니다. D0 일 수도 있습니다.
  • 임의로 많은 미러가있을 수 있습니다 (아무 것도 포함하지 않음).
  • 거울의 순서는 중요하지 않습니다.
  • 입력 값이 4의 배수로 가정 될 수 있습니다. 예를 들어 Lx Ly t또는 Lx Ly t D M1x1유효하지 않으며 테스트되지 않습니다. 입력도 전혀 유효하지 않습니다.

위의 레이아웃은 다음과 같이 입력 될 수 있습니다.

1 1 430 17 4.8 6.3 6.2 5.3 1.5 4.8 3.5 6 6.3 1.8 7.1 3

(이미지는 자유롭게 그려졌으며이 값은 대략적인 값입니다. Martin Büttner의 입력 값은

1 1 430 17 4.8 5.3 6.2 4.3 1.5 4.8 3.5 6 6.3 1.8 7.1 3

스케치와 일치하지 않지만 더 많은 충돌이 발생합니다.)

출력 형식

출력은 다음 형식으로 stdout으로 이동해야합니다.

Ex Ey

이들은 또한 수레이며 지수 형태 일 수 있습니다.

노트

  • 거울이 서로 교차 할 수 있습니다.
  • 거울의 양면은 반사적입니다.
  • 빔이 동일한 미러에 여러 번 충돌 할 수 있습니다.
  • 광선은 영원히 계속됩니다.

정의되지 않은 사례

당신은 경우에 가정

  • 레이저는 미러 라인 세그먼트에서 시작
  • 레이저 빔이 거울의 끝점에 닿습니다.
  • 레이저 빔이 두 거울 사이의 교차점에 부딪칩니다

정의되지 않았으며 테스트되지 않습니다. 오류 발생을 포함하여 이러한 상황이 발생하면 프로그램에서 어떤 작업을 수행 할 수 있습니다.

보너스

재미로, 문제의 그래픽 표현을 출력하는 투표율이 가장 높은 제출물에 200 개의 현상금 포인트를 부여합니다 (대화식 스크립트를 작성할 수도 있음). 이 보너스 제출은 골프를 밟을 필요가 없으며 입력 및 출력 처리 방법에 관대 할 수 있습니다. 그것들은 실제 골프 제출과는 다르지만 둘 다 같은 답변으로 제출해야합니다 .

참고 : 보너스 답변 만 제출하면됩니다. 수락 된 답변이 아닙니다. 수락하려면 입력 / 출력 사양을 정확하게 따르고 (예 : 출력은 Ex Ey이미지가 아닌을 포함 ) 가장 짧아야합니다.


1
한 가지 질문으로 골프를 치고 골을 넣지 않은 제출물을 얻는 것은 큰 혼란이 될 것입니다. 200 바운티 포인트는 골프가 작은 포인트가 될 정도로 매력적입니다.
Howard

1
@PeterTaylor 당신은 나를 문맥에서 잘 인용하고 있습니다. OP의 섹션 보너스 답변은 두 제출이 완전히 별개이지만 둘 다 시도하면 동일한 게시물에 속하기 때문에 읽습니다 (popcon 답변 만해도 괜찮을 것입니다). 어쨌든, 그들은 당신의 투표이며, 당신이 그것을 어떻게 사용하는지에 달려 있습니다. 어쨌든 골프 버전을 추가 할 것입니다. 그러나 OP는 그가 의도 한 popcon 전용 답변이 유효한지 아닌지를 명확히 할 수 있다고 생각합니다.
Martin Ender

1
@ MartinBüttner, " 보너스 "는 " 추가, 추가 "를 의미 합니다. 주요 도전의 일부가 아닙니다. 그리고 질문에는 하나의 태그 code-golf 만 있습니다.
피터 테일러

2
@PeterTaylor MartinBüttner가 맞습니다. 질문의 보너스 부분에만 대답하는 것이 좋습니다. 보너스 답변은 골프를 치르지 않고 i / o에서 관대하게 할 수 있으며 현재의 모든 보너스 제출은 나에게 잘 보입니다. 가장 짧은 제출 않습니다 정확히 사양에 따라이 허용 해답이 될 것입니다. 현재 제출물은 없지만 괜찮습니다.
Calvin 's Hobbies

1
이 경우 " 보너스 "는 사용하기에 잘못된 단어이며 사람들에게 규칙어기 도록 요청 하는 것은 도움이되지 않습니다.
피터 테일러

답변:


39

루비, 327 바이트

(바닥으로 스크롤)

수학, 보너스 답변

여기에 이미지 설명을 입력하십시오

지금 그래픽 제출 만 할 것입니다. 나중에 이것을 Ruby로 포팅하고 기분이 좋으면 골프를 칠 수 있습니다.

(* This function tests for an intersection between the laser beam
   and a mirror. r contains the end-points of the laser, s contains
   the end-points of the mirror. *)
intersect[r_, s_] := Module[
   {lr, dr, nr, ds, ns, \[Lambda]},
   (* Get a unit vector in the direction of the beam *)
   dr = r[[2]] - r[[1]];
   lr = Norm@dr;
   dr /= lr;
   (* Get a normal to that vector *)
   nr = {dr[[2]], -dr[[1]]};

   (* The sign of dot product in here depends on whether that end-point
      of the mirror is to the left or to the right of the array. Return 
      infinity if both ends of s are on the same side of the beam. *)
   If[Apply[Times, (s - {r[[1]], r[[1]]}).nr] > 0, 
    Return[\[Infinity]]];

   (* Get a unit vector along the mirror. *)
   ds = s[[2]] - s[[1]];
   ds /= Norm@ds;
   (* And a normal to that. *)
   ns = {ds[[2]], -ds[[1]]};
   (* We can write the beam as p + λ*dr and mirror as q + μ*ds,
      where λ and μ are real parameters. If we set those equal and
      solve for λ we get the following equation. Since dr is a unit 
      vector, λ is also the distance to the intersection. *)
   \[Lambda] = ns.(r[[1]] - s[[1]])/nr.ds;
   (* Make sure that the intersection is before the end of the beam.
      This check could actually be slightly simpler (see Ruby version). *)
   If[\[Lambda] != 0 && lr/\[Lambda] < 1, Infinity, \[Lambda]]
   ];

(* This function actually does the simulation and generates the plot. *)
plotLaser[L_, t_, distance_, M_] := Module[
   {coords, plotRange, points, e, lastSegment, dLeft, \[Lambda], m, p,
     d, md, mn, segments, frames, durations},

   (* This will contain all the intersections along the way, as well
      as the starting point. *)
   points = {L};
   (* The tentative end point. *)
   e = L + distance {Cos@t, Sin@t};
   (* This will always be the currently last segment for which we need
      to check for intersections. *)
   lastSegment = {L, e};
   (* Keep track of the remaining beam length. *)
   dLeft = distance;

   While[True,
    (* Use the above function to find intersections with all mirrors
       and pick the first one (we add a small tolerance to avoid
       intersections with the most recent mirror). *)
    {\[Lambda], m} = 
     DeleteCases[
       SortBy[{intersect[lastSegment, #], #} & /@ M, #[[1]] &], 
       i_ /; i[[1]] < 1*^-10][[1]];
    (* If no intersection was found, we're done. *)
    If[\[Lambda] == \[Infinity], Break[]];
    (* Reduce remaining beam length. *)
    dLeft -= \[Lambda];
    (* The following lines reflect the beam at the mirror and add
       the intersection to our list of points. We also update the
       end-point and the last segment. *)
    p = lastSegment[[1]];
    d = -Subtract @@ lastSegment;
    d /= Norm@d;
    md = -Subtract @@ m;
    md /= Norm@md;
    mn = {md[[2]], -md[[1]]};
    AppendTo[points, p + \[Lambda]*d];
    d = -d + 2*(d - d.mn*mn);
    e = Last@points + dLeft*d;
    lastSegment = {Last@points, e};
    ];
   (* Get a list of all points in the set up so we can determine
      the plot range. *)
   coords = Transpose@Join[Flatten[M, 1], {L, e}];
   (* Turn the list of points into a list of segments. *)
   segments = Partition[points, 2, 1];
   (* For each prefix of that list, generate a frame. *)
   frames = Map[
     Graphics[
       {Line /@ M,
        Red,
        Point@L,
        Line /@ segments[[1 ;; #]]},
       PlotRange -> {
         {Min@coords[[1]] - 1, Max@coords[[1]] + 1},
         {Min@coords[[2]] - 1, Max@coords[[2]] + 1}
         }
       ] &,
     Range@Length@segments];
   (* Generate the initial frame, without any segments. *)
   PrependTo[frames,
    Graphics[
     {Line /@ M,
      Red,
      Point@L},
     PlotRange -> {
       {Min@coords[[1]] - 1, Max@coords[[1]] + 1},
       {Min@coords[[2]] - 1, Max@coords[[2]] + 1}
       }
     ]
    ];
   (* Generate the final frame including lastSegment. *)
   AppendTo[frames,
    Graphics[
     {Line /@ M,
      Red,
      Point@L,
      Line /@ segments,
      Line[lastSegment],
      Point@e},
     PlotRange -> {
       {Min@coords[[1]] - 1, Max@coords[[1]] + 1},
       {Min@coords[[2]] - 1, Max@coords[[2]] + 1}
       }
     ]];

   (*Uncomment to only view the final state *)
   (*Last@frames*)

   (* Export the frames as a GIF. *)
   durations = ConstantArray[0.1, Length@frames];
   durations[[-1]] = 1;
   Export["hardcoded/path/to/laser.gif", frames, 
    "GIF", {"DisplayDurations" -> durations, ImageSize -> 600}];

   (* Generate a Mathematica animation form the frame. *)
   ListAnimate@frames
   ];

당신은 그것을처럼 호출 할 수 있습니다

plotLaser[{1, 1}, 7.50492, 95, {
  {{4.8, 5.3}, {6.2, 4.3}}, {{1.5, 4.8}, {3.5, 6}}, {{6.3, 1.8}, {7.1, 3}}, 
  {{5, 1}, {4, 3}}, {{7, 6}, {5, 6.1}}, {{8.5, 2.965}, {8.4, 2}}, 
  {{8.5, 3.035}, {8.6, 4}}, {{8.4, 2}, {10.5, 3}}, {{8.6, 4}, {10.5, 3}}
}]

그러면 Mathematica에서 애니메이션이 제공되고 GIF (이 입력의 맨 위에있는 GIF)도 내보내집니다. 좀 더 흥미롭게 만들기 위해 OPs 예제를 약간 확장했습니다.

더 많은 예

벽이 약간 갈라 지지만 끝이 닫힌 튜브 :

plotLaser[{0, 0}, 1.51, 200, {
  {{0, 1}, {20, 1.1}},
  {{0, -1}, {20, -1.1}},
  {{20, 1.1}, {20, -1.1}}
}]

여기에 이미지 설명을 입력하십시오

한 변과 거의 평행 한 정삼각형과 초기 방향.

plotLaser[{-1, 0}, Pi/3 + .01, 200, {
  {{-2.5, 5 Sqrt[3]/6}, {2.5, 5 Sqrt[3]/6}},
  {{0, -5 Sqrt[3]/3}, {-2.5, 5 Sqrt[3]/6}},
  {{0, -5 Sqrt[3]/3}, {2.5, 5 Sqrt[3]/6}}
}]

여기에 이미지 설명을 입력하십시오

하나 더:

plotLaser[
 {0, 10}, -Pi/2, 145,
 {
   {{-1, 1}, {1, -1}}, {{4.5, -1}, {7.5, Sqrt[3] - 1}},
   {{11, 10}, {13, 10}}, {{16.5, Sqrt[3] - 1}, {19.5, -1}},
   {{23, -1}, {25, 1}}, {{23, 6}, {25, 4}}, {{18, 6}, {20, 4}}, {{18, 9}, {20, 11}},
   {{31, 9}, {31.01, 11}}, {{24.5, 10.01}, {25.52, 11.01}}, {{31, 4}, {31, 6}}, {{25, 4.6}, {26, 5.6}}, {{24.5, 0.5}, {25.5, -0.5}}, 
   {{31, -1}, {33, 1}}, {{31, 9}, {33, 11}}, {{38, 10.5}, {38.45, 9}}
 }
]

여기에 이미지 설명을 입력하십시오

루비, 골프 답변

x,y,t,p,*m=gets.split.map &:to_f
u=q=Math.cos t
v=r=Math.sin t
loop{k=i=p
u=x+q*p
v=y+r*p
m.each_slice(4){|a,b,c,d|((a-u)*r-(b-v)*q)*((c-u)*r-(d-v)*q)>0?next: g=c-a
h=d-b
l=(h*(x-a)-g*(y-b))/(r*g-q*h)
f=(g*g+h*h)**0.5
t,k,i=g/f,h/f,l if l.abs>1e-9&&l/i<1}
i==p ?abort([u,v]*' '): p-=i
x+=q*i
y+=r*i
n=q*k-r*t
q-=2*n*k
r+=2*n*t}

이것은 기본적으로 Mathematica 솔루션을 Ruby로 직접 변환 한 것입니다. 또한 골프를 치고 I / O 기준을 충족하는지 확인합니다.


첫 번째 예의 끝에서 레이저가 거울 삼각형을 어떻게 교차하게합니까?
AJMansfield

1
@AJMansfield 삼각형에는 작은 구멍이 있는데 애니메이션의 시작 부분에서 볼 수 있습니다.
마틴 엔더

작동 방식을 설명하는 단락을 작성할 수 있다면 좋을 것입니다.
JeffSB

@JeffSB 지금 Mathematica 코드를 문서화했습니다. 루비 버전은 불명확 한 변수 이름과 플로팅없이 거의 똑같은 일을합니다.
마틴 엔더

@ MartinBüttner 감사합니다. 다른 사람들이 어떻게 하는지를 보는 것은 흥미 롭습니다. 바운스 된 마지막 미러를 제외해야한다는 사실을 알고 계셨습니까? 나는하지 않았지만 곧 알아 냈습니다. 귀하의 코드에서 매우 작은 숫자를 발견했기 때문에 그것이 어떻게 작동하는지 묻습니다.
JeffSB

18

파이썬 3 (421C 390C, 366C)

builtin.complex2D 벡터로 사용하십시오 . 그래서

dot = lambda a, b: (a.conjugate() * b).real
cross = lambda a, b: (a.conjugate() * b).imag

368C Ruby 솔루션을 이기기 위해 거울을 따라 점 반사를 계산하는 매우 컴팩트 한 방법을 찾았습니다. 또한 더 많은 문자를 줄이기 위해 복잡한 대수를 사용했습니다. 이것들은 ungolfed 코드에서 쉽게 찾을 수 있습니다.

골프 버전입니다.

C=lambda a,b:(abs(a)**2/a*b).imag
J=1j
x,y,r,d,*a=map(float,input().split())
p=x+y*J
q=p+d*2.718281828459045**(r*J)
M=[]
while a:x,y,z,w,*a=a;M+=[(x+y*J,z-x+w*J-y*J)]
def T(m):x,y=m;d=C(y,r)+1e-9;t=C(y,x-p)/d;s=C(r,x-p)/d;return[1,t][(1e-6<t<1)*(0<s<1)]
while 1:
 r=q-p;m=f,g=min(M,key=T)
 if T(m)==1:break
 p+=r*T(m);q=(q/g-f/g).conjugate()*g+f
print(q.real,q.imag)

언 골프

# cross product of two vector
# abs(a)**2 / a == a.conjugate()
cross = lambda a, b: (abs(a)**2 / a * b).imag
# Parse input
x, y, angle, distance, *rest = map(float, input().split())
start = x + y * 1j
# e = 2.718281828459045
# Using formula: e**(r*j) == cos(r) + sin(r) * j
end = start + distance * 2.718281828459045 ** (angle * 1j)
mirrors = []
while rest:
    x1, y1, x2, y2, *rest = rest
    # Store end point and direction vector for this mirror
    mirrors.append((x1 + y1 * 1j, (x2 - x1) + (y2 - y1) * 1j))

def find_cross(mirror):
    # a: one end of mirror
    # s: direction vector of mirror
    a, s = mirror
    # Solve (t, r) for equation: start + t * end == a + r * s
    d = cross(s, end - start) + 1e-9 # offset hack to "avoid" dividing by zero
    t = cross(s, a - start) / d
    r = cross(end - start, a - start) / d
    return t if 1e-6 < t < 1 and 0 < r < 1 else 1

def reflect(p, mirror):
    a, s = mirror
    # Calculate reflection point:
    #  1. Project r = p - a onto a coordinate system that use s as x axis, as r1.
    #  2. Take r1's conjugate as r2.
    #  3. Recover r2 to original coordinate system as r3
    #  4. r3 + a is the final result
    #
    # So we got conjugate((p - a) * conjugate(s)) / conjugate(s) + a
    # which can be reduced to conjugate((p - a) / s) * s + a
    return ((p - a) / s).conjugate() * s + a

while 1:
    mirror = min(mirrors, key=find_cross)
    if find_cross(mirror) == 1:
        break
    start += (end - start) * find_cross(mirror)
    end = reflect(end, mirror)
print(end.real, end.imag)

보너스 : HTML, Coffeescript, 실시간 조정 및 계산

끝점 (또는 lazer, mirros)을 드래그하면 트랙이 렌더링됩니다. 또한 질문에 설명 된 것과 @Martin Büttner가 사용하는 두 가지 유형의 입력을 지원합니다.

스케일링도 자동으로 조정됩니다.

현재로서는 애니메이션이 없습니다. 어쩌면 나중에 개선 할 것입니다. 그러나 흰색 점을 드래그하면 다른 유형의 애니메이션을 볼 수 있습니다. 온라인으로 직접 해보십시오. 재미 있습니다!

전체 프로젝트는 여기 에서 찾을 수 있습니다

사례 1 사례 2

최신 정보

여기에 흥미로운 사례가 있습니다.

0 0.6 -0.0002 500.0 0.980785280403 -0.195090322016 1.0 0.0 1.0 0.0 0.980785280403 0.195090322016 0.980785280403 0.195090322016 0.923879532511 0.382683432365 0.923879532511 0.382683432365 0.831469612303 0.55557023302 0.831469612303 0.55557023302 0.707106781187 0.707106781187 0.707106781187 0.707106781187 0.55557023302 0.831469612303 0.55557023302 0.831469612303 0.382683432365 0.923879532511 0.382683432365 0.923879532511 0.195090322016 0.980785280403 0.195090322016 0.980785280403 6.12323399574e-17 1.0 6.12323399574e-17 1.0 -0.195090322016 0.980785280403 -0.195090322016 0.980785280403 -0.382683432365 0.923879532511 -0.382683432365 0.923879532511 -0.55557023302 0.831469612303 -0.55557023302 0.831469612303 -0.707106781187 0.707106781187 -0.707106781187 0.707106781187 -0.831469612303 0.55557023302 -0.831469612303 0.55557023302 -0.923879532511 0.382683432365 -0.923879532511 0.382683432365 -0.980785280403 0.195090322016 -0.980785280403 0.195090322016 -1.0 1.22464679915e-16 -1.0 1.22464679915e-16 -0.980785280403 -0.195090322016 -0.980785280403 -0.195090322016 -0.923879532511 -0.382683432365 -0.923879532511 -0.382683432365 -0.831469612303 -0.55557023302 -0.831469612303 -0.55557023302 -0.707106781187 -0.707106781187 -0.707106781187 -0.707106781187 -0.55557023302 -0.831469612303 -0.55557023302 -0.831469612303 -0.382683432365 -0.923879532511 -0.382683432365 -0.923879532511 -0.195090322016 -0.980785280403 -0.195090322016 -0.980785280403 -1.83697019872e-16 -1.0 -1.83697019872e-16 -1.0 0.195090322016 -0.980785280403 0.195090322016 -0.980785280403 0.382683432365 -0.923879532511 0.382683432365 -0.923879532511 0.55557023302 -0.831469612303 0.55557023302 -0.831469612303 0.707106781187 -0.707106781187 0.707106781187 -0.707106781187 0.831469612303 -0.55557023302 0.831469612303 -0.55557023302 0.923879532511 -0.382683432365 0.923879532511 -0.382683432365 0.980785280403 -0.195090322016

결과는 다음과 같습니다. 원


-1이 입력 또는 출력 사양을 충족하지 않습니다.
피터 테일러

@ 레이 보너스 답변으로 이것은 괜찮습니다. 코드 골프 답변이 되려면 사양을 정확히 충족해야합니다.
Calvin 's Hobbies

@PeterTaylor 지금 스펙을 만나보십시오.
Ray

거울을 어떻게 움직일 수 있는지 정말 멋집니다! 너의 것은 나의 첫 번째 +1 투표이다.
JeffSB

17

HTML 자바 스크립트, 10,543, 947 889

버그를 수정하고 출력이 질문 사양을 충족하는지 확인했습니다. 아래 웹 페이지에는 골프 버전과 그래픽 보너스 버전이 있습니다. 또한 @Ray가 지적한 버그를 수정하여 58자를 저장했습니다. (감사합니다 Ray) JavaScript 콘솔에서 골프 코드를 실행할 수도 있습니다. (이제 2mW 녹색 레이저를 사용하고 있습니다.)

골프 코드

a=prompt().split(" ").map(Number);M=Math,Mc=M.cos,Ms=M.sin,P=M.PI,T=2*P,t=true;l=new S(a[0],a[1],a[0]+a[3]*Mc(a[2]),a[1]+a[3]*Ms(a[2]));m=[];for(i=4;i<a.length;)m.push(new S(a[i++],a[i++],a[i++],a[i++]));f=-1;for(;;){var h=!t,d,x,y,n,r={};for(i=0;i<m.length;i++)if(i!=f)if(I(l,m[i],r))if(!h||r.d<d){h=t;d=r.d;x=r.x;y=r.y;n=i}if(h){l.a=x;l.b=y;l.e-=d;l.f=2*(m[f=n].f+P/2)-(l.f+P);l.c=l.a+l.e*Mc(l.f);l.d=l.b+l.e*Ms(l.f);}else break;}alert(l.c+" "+l.d);function S(a,b,c,d){this.a=a;this.b=b;this.c=c;this.d=d;this.e=D(a,b,c,d);this.f=M.atan2(d-b,c-a)}function D(a,b,c,d){return M.sqrt((a-c)*(a-c)+(b-d)*(b-d))}function I(l,m,r){A=l.a-l.c,B=l.b-l.d,C=m.a-m.c,L=m.b-m.d,E=l.a*l.d-l.b*l.c,F=m.a*m.d-m.b*m.c,G=A*L-B*C;if(!G)return!t;r.x=(E*C-A*F)/G;r.y=(E*L-B*F)/G;H=r.d=D(l.a,l.b,r.x,r.y),O=D(l.c,l.d,r.x,r.y),J=D(m.a,m.b,r.x,r.y),K=D(m.c,m.d,r.x,r.y);return(H<l.e)&&(O<l.e)&&(J<m.e)&&(K<m.e);} 

입력

1 1 7.50492 17 4.8 6.3 6.2 5.3 1.5 4.8 3.5 6 6.3 1.8 7.1 3

산출

14.743305098514739 3.759749038188634


여기에서 테스트 할 수 있습니다 : http://goo.gl/wKgIKD

여기에 이미지 설명을 입력하십시오

설명

웹 페이지의 코드가 주석 처리됩니다. 기본적으로 레이저와 거울이 무한히 길다고 가정하면 모든 거울과 레이저의 교차점을 계산합니다. 그런 다음 교차점이 거울과 레이저의 유한 길이 내에 있는지 확인합니다. 그런 다음 가장 가까운 교차점을 잡고 레이저를 해당 지점으로 이동 한 다음 레이저가 모든 거울을 놓칠 때까지 계속합니다.

매우 재미있는 프로젝트. 이 질문을 해주셔서 감사합니다!

읽을 수있는 코드

// a = input array
// M = Math, Mc = M.cos, Ms = M.sin, P=M.PI, T=2*P, t=true
// l = laser segment
// m = array of mirror segments
// i = loop variable
// S = segment class (this.a=x1,b=y1,c=x2,d=y2,e=len,f=theta)
// D = distance function
// I = intersect function
// f = last mirror bounced from
// h = hits a mirror
// n = next intersecing mirror
// d = distance to mirror
// x = intersection point x
// y = intersection point y
// r = mirror intersection result (d,x,y)
// b = number of bounces (FOR DEBUGGING)
// A,B,C,E,F,G,H,J,K,L,O temp variables
// s = laser segment array

// get input array
var a = prompt().split(" ").map(Number);

// some constants
var M = Math, Mc = M.cos, Ms = M.sin, P = M.PI, T = 2 * P, t = true;

// laser segment
var l = new S(a[0], a[1], a[0] + a[3] * Mc(a[2]), a[1] + a[3] * Ms(a[2])), s = [];

// mirror segments
var m = []; for (var i = 4; i < a.length;) m.push(new S(a[i++], a[i++], a[i++], a[i++]));

// bounce until miss
var f = -1, b = 0; for (; ;) {

    // best mirror found
    var h = !t, d, x, y, n, r = {};

    // loop through mirrors, skipping last one bounced from
    for (var i = 0; i < m.length; i++)
        if (i != f)
            if (I(l, m[i], r))
                if (!h || r.d < d) { h = t; d = r.d; x = r.x; y = r.y; n = i }

    // a mirror is hit
    if (h) {

        // add to draw list, inc bounces
        s.push(new S(l.a, l.b, x, y)); b++;

        // move and shorten mirror
        l.a = x; l.b = y; l.e -= d;

        // calculate next angle
        l.f = 2 * (m[f = n].f + P / 2) - (l.f + P);

        // laser end point
        l.c = l.a + l.e * Mc(l.f); l.d = l.b + l.e * Ms(l.f);

    } else {

        // add to draw list, break
        s.push(new S(l.a, l.b, l.c, l.d));
        break;
    }
}
// done, print result
alert("X = " + l.c.toFixed(6) + ",  Y = " + l.d.toFixed(6) + ",  bounces = " + b);
PlotResult();

// segment class
function S(a, b, c, d) { this.a = a; this.b = b; this.c = c; this.d = d; this.e = D(a, b, c, d); this.f = M.atan2(d - b, c - a) }

// distance function
function D(a, b, c, d) { return M.sqrt((a - c) * (a - c) + (b - d) * (b - d)) }

// intersect function
function I(l, m, r) {

    // some values
    var A = l.a - l.c, B = l.b - l.d, C = m.a - m.c, L = m.b - m.d, E = l.a * l.d - l.b * l.c, F = m.a * m.d - m.b * m.c, G = A * L - B * C;

    // test if parallel
    if (!G) return !t;

    // intersection
    r.x = (E * C - A * F) / G; r.y = (E * L - B * F) / G;

    // distances
    var H = r.d = D(l.a, l.b, r.x, r.y), O = D(l.c, l.d, r.x, r.y), J = D(m.a, m.b, r.x, r.y), K = D(m.c, m.d, r.x, r.y);

    // return true if intersection is with both segments
    return (H < l.e) && (O < l.e) && (J < m.e) && (K < m.e);
}

꽤 멋지다, 나는 웹 인터페이스를 좋아한다. 또 다른 재미있는 입력 : 0 0 0.4 100 1 1 1 -1 1 -1 -1 -1 -1 -1 -1 1 -1 1 1 1.
Calvin 's Hobbies

1
실제 프로그램은 어디에 있습니까?
피터 테일러

여기 웹 페이지에 있습니다 : goo.gl/wKgIKD
JeffSB

이 사이트의 답변에는 일반적으로 질문에 답변하는 데 필요한 모든 코드가 포함되어야합니다. 이 질문의 경우 stdin에서 읽고 stdout에 쓰는 프로그램입니다. 또한 코드 골프 문제이므로 가능한 한 주석을 제거하고 불필요한 공백을 제거하고 가능한 한 문자 식별자를 사용하여 코드를 최대한 최소화해야합니다.
피터 테일러

@JeffSB이 제출물은 보너스 답변에 유효하며 수락 된 답변이 아닙니다. (모든 코드를 포함하고 싶을 수도 있습니다.)
Calvin 's Hobbies

6

파이썬-765

좋은 도전입니다. 이것은 stdin에서 입력을 받고 stdout으로 출력하는 솔루션입니다. @Martin Büttner의 예제 사용 :

python mirrors.py 1 1 70.00024158332184 95 4.8 5.3 6.2 4.3 1.5 4.8 3.5 6 6.3 1.8 7.1 3     5 1 4 3 7 6 5 6.1 8.5 2.965 8.4 2 8.5 3.035 8.6 4 8.4 2 10.5 3 8.6 4 10.5 3

7.7094468894 3.84896396639

골프 코드는 다음과 같습니다.

import sys;from cmath import*
l=[float(d) for d in sys.argv[1:]];c=180/pi;p=phase;q=exp;u=len;v=range
def o(l):
 L=l[0]+1j*l[1];t=l[2]/c;D=l[3];S=[L,L+D*q(1j*t)];N=[[l[i]+1j*l[i+1],l[i+2]+1j*l[i+3]] for i in v(4,u(l),4)];a=[];b=[]
 for M in N:
  z=S[1].real-S[0].real;y=M[0].real-M[1].real;x=S[1].imag-S[0].imag;w=M[0].imag-M[1].imag;d=M[0].real-S[0].real;f=M[0].imag-S[0].imag;g=z*w-x*y;h=w/g;j=-y/g;m=-x/g;n=z/g;a.append(h*d+j*f);b.append(m*d+n*f)
 i=1;e=-1
 for k in v(u(N)):
  if 1>b[k]>0:
   if i>a[k]>1e-14:
    i=a[k];e=k
 if e>-1:
  L=S[0]+i*(S[1]-S[0]);M=N[e];l[0]=L.real;l[1]=L.imag;l[2]=c*(p(M[1]-M[0])+p(q(1j*p(M[1]-M[0]))*q(1j*-t)));l[3]=D*(1-i)
  return l
 J=S[0]+i*(S[1]-S[0]) 
 print J.real, J.imag   
 return J.real, J.imag   
while u(l)>2:
 l=o(l)

그리고 여기에 보너스 수치가있는 ungolfed 코드가 있습니다

거울

import sys
from cmath import*
import matplotlib
import matplotlib.pyplot as plt
l=[float(d) for d in sys.argv[1:]]
def nextpos(l):
    L=l[0]+1j*l[1]
    t=l[2]/180*pi
    D=l[3]
    S=[L,L + D * exp(1j * t)]
    MM=[[l[i]+1j*l[i+1],l[i+2]+1j*l[i+3]] for i in range(4,len(l), 4)]    
    a=[]
    b=[]
    for M in MM:
        #determine intersections
        a11 = S[1].real-S[0].real 
        a12 = M[0].real-M[1].real
        a21 = S[1].imag-S[0].imag
        a22 = M[0].imag-M[1].imag
        b1  = M[0].real-S[0].real
        b2  = M[0].imag-S[0].imag
        deta = a11*a22-a21*a12
        ai11 = a22/deta
        ai12 = -a12/deta
        ai21 = -a21/deta
        ai22 = a11/deta        
        a.append(ai11*b1+ai12*b2)
        b.append(ai21*b1+ai22*b2)
    #determine best intersection    
    mina = 1
    bestk = -1
    for k in range(len(MM)):
        if 1>b[k]>0:
            if mina>a[k]>1e-14:
                mina=a[k]
                bestk=k
    if bestk>-1:
        #determine new input set
        L=S[0]+mina*(S[1]-S[0])
        M=MM[bestk]
        l[0]=L.real
        l[1]=L.imag
        angr=phase(exp(1j*phase(M[1]-M[0]))*exp(1j *-t))
        l[2]=180/pi*(phase(M[1]-M[0])+angr)
        l[3]=D*(1-mina)
        return l
    J= S[0]+mina*(S[1]-S[0]) 
    print J.real, J.imag   
    return J.real, J.imag   
#plotting
xL = [l[0]]
yL = [l[1]]
fig = plt.figure()
ax = fig.add_subplot(111,aspect='equal')
for i in range(4,len(l), 4):
    plt.plot([l[i],l[i+2]],[l[i+1],l[i+3]], color='b')
while len(l)>2:
    #loop until out of lasers reach
    l = nextpos(l)
    xL.append(l[0])
    yL.append(l[1])
plt.plot(xL,yL, color='r')
plt.show()

-1 : 사양을 충족하지 않습니다. 지정된 출력은 두 개의 숫자가 아니라 두 개의 숫자와 이미지입니다.
피터 테일러

@PeterTaylor 그래서 stdin / stdout을 의미합니까?
Ray

@willem 보너스 답변으로 이것은 괜찮습니다. 코드 골프 답변이 되려면 사양을 정확히 충족해야합니다.
Calvin 's Hobbies

코드를 업데이트했습니다
Willem

참고 sys.argvstdin을하지 않습니다.
Ray

6

MATLAB (388)

음모

음모 plot2

개념

반사점

반사점을 계산하려면 기본적으로 두 개의 직선을 교차해야합니다. 하나는 점 p0과 벡터 v를 갖고 다른 하나는 두 점 p1, p2 사이에 있습니다. 따라서 풀어야 할 방정식은 (s, t는 매개 변수입니다) : p0 + t v = s p1 + (1-s) * p2.

매개 변수 s는 거울의 무게 중심 좌표이므로 0이면

미러링

v의 미러링은 매우 간단합니다. || v || = || n || = 1 여기서 n은 현재 미러의 법선 벡터입니다. 그런 다음 공식 v : = v-2 ** n을 사용할 수 있습니다. 여기서 <,>는 내적입니다.

단계의 유효성

가장 가까운 '유효한'미러를 계산하는 동안이를 유효하게하는 몇 가지 기준을 고려해야합니다. 먼저 미러의 차단 지점이 두 끝점 사이에 있어야하므로 0이어야합니다.

프로그램

p = [1 1 430 17 4.8 5.3 6.2 4.3 1.5 4.8 3.5 6 6.3 1.8 7.1 3];
hold on
grid on
for i=2:length(p)/4
    i = i*4+1-4
    p2=p(i+2:i+3)';
    p1=p(i:i+1)'
    plot([p1(1),p2(1)],[p1(2),p2(2)],'r-')
    text(p1(1),p1(2),['m' num2str((i+3)/4-1)])
end
%hold off

history = p(1:2)';


currentPosition = p(1:2)';%current
currentDirection=[cos(p(3)*pi/180);sin(p(3)*pi/180)];
while p(4)>0%as long as we do not have finished our distance
   distanceBuffer = Inf%distance next point buffer
   intersectionBuffer = NaN %next point buffer
   for i=2:length(p)/4%number of mirrors
       i = i*4+1-4 %i is now the index of the firs coordinate of the mirror
       %calculate all crosspoints
       p2=p(i+2:i+3)';
       mirrorVector = p2-p(i:i+1)';
       % idea: p0+s*currentDirection = s*p1+(1-s)*p2 solving for s,t
       r=[currentDirection,mirrorVector]\[p2-currentPosition];
       if r(1)<distanceBuffer && 0.001< r(1) && r(1)<p(4) &&0<=r(2) && r(2)<=1 %search for the nearest intersection
           distanceBuffer=r(1);
           intersectionBuffer=r(1)*currentDirection+currentPosition;
           mirrorBuffer = mirrorVector
       end
   end
   if distanceBuffer == Inf %no reachable mirror found
       endpoint = currentPosition+p(4)*currentDirection;
       counter = counter+1
       history = [history,endpoint];
       break
   else %mirroring takes place
       counter = counter+1
       history = [history,intersectionBuffer];
       currentPosition=intersectionBuffer;
       normal = [0,-1;1,0]*mirrorBuffer;%normal vector of mirror
       normal = normal/norm(normal)
       disp('arccos')
       currentDirection = currentDirection-2*(currentDirection'*normal)*normal;
       %v = v/norm(v)
       p(4)=p(4)-distanceBuffer
   end
end
history
plot(history(1,:),history(2,:))

약간 골프 (388)

p=[1 1 430 17 4.8 5.3 6.2 4.3 1.5 4.8 3.5 6 6.3 1.8 7.1 3];
c=p(1:2)'
b=pi/180
v=[cos(p(3)*b);sin(p(3)*b)]
f=p(4)
while f>0
q=Inf
for i=2:length(p)/4
b=p(i+2:i+3)'
u=b-p(i:i+1)'
r=[v,u]\[b-c]
s=r(1)
t=r(2)
if s<q&&0.001<s&&s<f&&0<=t&&t<=1 
q=s
n=s*v+c
m=u
end
end
if q==Inf
disp(c+f*v)
break
else 
c=n
g=[0,-1;1,0]*m
g=g/norm(g)
v=v-2*(v'*g)*g
f=f-q
end
end

이것은 나를 다시 데려 간다. Matlab에 대한 첫 경험은 학부 과정에서 연구 위치에있는 동안 거울과 렌즈 시스템을 통해 레이저의 경로를 모델링하는 것이 었습니다. 특히 그래픽이 매우 친숙해 보입니다. :) 어쨌든, 제쳐두고. 잘 했어, +1
Alex A.

하하 감사합니다! 난 단지 내가 당신의 코멘트 팝업을 볼 때 내가 이것을 한 것을 기억하지 않았다 =)
flawr

하하 그러면 내 의견은 아마도 당신을 다시 데려 것입니다 ! (이 글을 올렸을 때까지)
Alex A.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.