激光去哪儿了?


34

取一个二维网格并在其上绘制许多线段以表示镜像。现在选择一个点以放置理论激光,并选择一个角度以定义其指向的方向。问题是:如果您沿着激光束路径走了指定的距离,您在哪个坐标点?

例:

激光例子

在该图像中,L是激光的位置,t是它的角度(从正X轴测量的), ,M1M2M3都是线段反射镜,并且E是后的激光束路径上的点D = d1 + d2 + d3 + d4单位,从开始L

目标

写的最短程序(以字节计)的输出E给出LtD,和一个反射镜的列表。
(使用http://mothereff.in/byte-counter计数字节。)

输入格式

输入来自标准输入,格式为:

Lx Ly t D M1x1 M1y1 M1x2 M1y2 M2x1 M2y1 M2x2 M2y2 ...
  • 所有值都是与该正则表达式匹配的浮点数:[-+]?[0-9]*\.?[0-9]+
  • 每个数字之间始终只有一个空格。
  • 允许在输入周围使用引号。
  • t以度为单位,但不一定在该[0, 360)范围内。(如果您愿意,可以改用弧度,请在回答中说出来。)
  • D可能为负,有效地将激光旋转180度。D也可能是0。
  • 可能有任意多个镜像(根本没有)。
  • 镜子的顺序无关紧要。
  • 您可以假设输入将是4个数字的倍数。例如Lx Ly tLx 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

(请注意,图像是徒手绘制的,这些值仅是近似值。MartinBü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

尽管它们与草图不匹配,但会产生更多的碰撞。)

输出格式

输出应以以下格式进入标准输出:

Ex Ey

这些也是浮点数,可能是指数形式。

笔记

  • 镜子可能彼此相交。
  • 镜子的两面都是反光的。
  • 光束可能多次击中同一面镜子。
  • 光束永远发光。

未定义的案例

您可以假设以下情况

  • 激光从镜线段开始
  • 激光束撞击镜子的端点
  • 激光束撞击两个反射镜之间的交点

未定义,将不会进行测试。如果发生这些情况,您的程序可能会采取任何措施,包括引发错误。

奖金

只是为了好玩,我将为投票最多的提交的问题以图形表示形式(您甚至可以编写一个交互式脚本)的奖励200个奖励积分。这些奖金提交不需要打高尔夫球,并且可以宽容输入和输出的处理方式。它们与实际的高尔夫球投稿不同,但是两者都应以相同的答案提交

注意:仅提交奖励答案是可以的,您将不会被接受。要被接受,您必须严格遵守输入/输出规范(例如,输出仅涉及Ex Ey,而不涉及图像),并且要最短。


1
一个问题中有高尔夫球和非高尔夫球的提交文件,恕我直言将变得一团糟。200个奖励积分非常诱人,高尔夫球成为次要积分。
霍华德

1
@PeterTaylor您在上下文中对我的引用很好。我读了OP的部分奖金答案,因为这两个提交完全不同,但是如果都尝试了,则属于同一帖子(这意味着仅popcon回答也可以)。无论如何,它们是您的选票,这取决于您如何使用它们,而且我可能随时会添加一个高尔夫球版本。但是我猜想OP可以澄清他是否打算将仅popcon的答案有效。
Martin Ender 2014年

1
@MartinBüttner,“ 红利 ”表示“ 额外的额外 ”。这不是主要挑战的一部分。这个问题只有一个标记,即code-golf
彼得·泰勒

2
@PeterTaylorMartinBüttner是正确的。仅回答问题的奖励部分就可以了。我说过,对于I / O来说,奖金答案可能会显得有些松懈和宽容,而当前所有提交的奖金对我来说都很好。该最短提交严格遵循该规范将是公认的答案。目前尚无任何提交,但是我可以。
卡尔文的爱好2014年

1
在这种情况下,“ 红利 ”一词会用错,并且您在要求人们打破规则,这无济于事。
彼得·泰勒

Answers:


39

Ruby,327个字节

(滚动到底部)

Mathematica,奖励答案

在此处输入图片说明

我现在只打算以图形方式提交。我可能会稍后将其移植到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)。为此,我稍微扩展了OP的示例,使其更加有趣。

更多例子

管壁略有分歧但末端封闭的管:

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三角形中有一个小孔,您可以在动画开始时看到。
Martin Ender 2014年

如果您可以写一段解释其工作原理的话,那将是很好的。
JeffSB 2014年

@JeffSB我已经记录了Mathematica代码。Ruby版本在变量名晦涩且不作图的情况下几乎具有完全相同的功能。
马丁·恩德

@MartinBüttner谢谢。看看其他人是如何做到的很有趣。您是否意识到在发生之前必须排除反弹的最后一面镜子?我没有,但是我很快就知道了。我注意到您的代码中的数字非常小,这就是为什么我要求查看其工作原理的原因。
JeffSB 2014年

18

Python 3(421C 390C,366C)

使用builtin.complex的二维矢量。所以

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

为了击败368C Ruby解决方案,我找到了一种非常紧凑的方法来计算沿反射镜的点反射。并且还使用了一些复杂的代数来减少更多字符。这些可以在非高尔夫代码中轻松找到。

这是高尔夫球版。

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),然后渲染轨迹。它还支持两种类型的输入,一种在问题中描述,另一种由@MartinBü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不符合输入或输出规范。
彼得·泰勒

@Ray作为奖励,这很好。只需完全符合规范即可成为代码高尔夫的答案。
加尔文的爱好2014年

@PeterTaylor立即满足规格。

怎样移动镜子真是太酷了!您的是我的+1票。
JeffSB 2014年

17

HTML JavaScript, 10,543947 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);
}

非常酷,我喜欢Web界面。另一个有趣的输入:0 0 0.4 100 1 1 1 -1 1 -1 -1 -1 -1 -1 -1 1 -1 1 1 1
加尔文的爱好2014年

1
实际的程序在哪里?
彼得·泰勒

它在这里的网页中:goo.gl/wKgIKD
JeffSB 2014年

该网站上的答案通常应包括回答问题所需的所有代码。对于这个问题,这是一个从stdin读取并写入stdout的程序。另外,由于这是一个代码问题,因此您应尽可能减少代码:至少删除注释和不必要的空格,并在可能的情况下使用一个字符的标识符。
彼得·泰勒

@JeffSB此提交仅对已获答的答案有效,对奖励答案有效。(尽管您可能希望包括所有代码。)
卡尔文的爱好2014年

6

蟒蛇-765

好挑战。这是我的解决方案,它从stdin获取输入,并输出到stdout。使用@MartinBü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)

这是带有奖金数字的非高尔夫代码

镜子

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?

@willem作为奖励,这很好。只需完全符合规范即可成为代码高尔夫的答案。
卡尔文的爱好2014年

我已经更新了代码
Willem

注意sys.argv不是stdin。

6

Matlab(388)

情节

情节 情节2

概念

反射点

为了计算反射点,我们基本上必须将两条直线相交。一个带有点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

哈哈,那么我的评论可能会让退缩!(要当你张贴了这个。)
亚历A.
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.