レーザーはどこに行きますか?


34

2次元のグリッドを作成し、その上に多数の線セグメントを描画してミラーを表します。次に、理論上のレーザーを配置するポイントと、それが指す方向を定義する角度を選択します。質問は、指定された距離でレーザー光線の経路をたどる場合、どの座標点にいますか?

例:

レーザーの例

この画像において、Lレーザの位置であり、t(正のX軸から測定される)、それの角度であり、M1M2、およびM3すべての線分のミラーであり、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]+
  • 各番号の間には常に1つのスペースがあります。
  • 入力を引用符で囲む必要があります。
  • 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

(画像は手書きで描かれており、これらの値は近似値にすぎないことに注意してください。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

これらも浮動小数点数であり、指数形式の場合があります。

ノート

  • ミラーは互いに交差する場合があります。
  • ミラーの両側は反射性です。
  • ビームは同じミラーに何度も当たる可能性があります。
  • ビームは永遠に続きます。

未定義のケース

あなたはそのケースを仮定することができます

  • レーザーはミラーラインセグメントで開始します
  • レーザービームが鏡の終点に当たる
  • レーザービームが2つのミラーの交差点に当たる

未定義であり、テストされません。これらが発生した場合、エラーをスローするなど、プログラムは何でも実行できます。

ボーナス

楽しみのために、問題のグラフィカルな表現を出力する最高票数の提出に対して200バウンティポイントを授与します(インタラクティブスクリプトを作成することもできます)。これらのボーナスの提出は、ゴルフをする必要はなく、入力および出力の処理方法に寛大なことができます。それらは実際のゴルフされた提出とは異なりますが、両方が同じ答えで提出されるべきです。

注:ボーナスの回答を送信するだけで構いません。受け入れられる回答にはなりません。受け入れられるためには、入力/出力仕様に厳密に従う必要があり(たとえば、出力のみが含まれEx Ey、画像は含まれません)、最短でなければなりません。


1
1つの質問でゴルフとゴルフのない提出物を提出することは、巨大な混乱になりそうです。200バウンティポイントは非常に魅力的で、ゴルフはマイナーポイントになります。
ハワード14

1
@PeterTaylorあなたは私を文脈から引用しています。OPのセクションボーナスの回答は、2つの投稿が完全に異なるが、両方が試みられた場合は同じ投稿に属するものであると読みました(つまり、ポプコンの回答も問題ないことを意味します)。とにかく、彼らはあなたの投票であり、あなたがそれらをどのように使うかはあなた次第であり、おそらくいずれかの時点でゴルフバージョンを追加するでしょう。しかし、OPは、ポプコンのみの回答が有効であるかどうかを明確にできると思います。
マーティンエンダー14

1
@MartinBüttner、「ボーナス」は「追加、追加」を意味します。それは主な課題の一部ではありません。また、質問にはcode-golfというタグが1つだけあります。
ピーターテイラー14

2
@PeterTaylorMartinBüttnerが正しい。質問のボーナス部分のみに回答しても問題ありません。私は、ボーナスの答えはゴルフをしなくてもI / Oに対して寛大であり、現在のすべてのボーナスの提出は私にとって見栄えが良いと言いました。最短提出正確に仕様に従うには、受け入れ答えになります。現在、提出物はありませんが、それは大丈夫です。
カルバンの趣味

1
その場合、「ボーナス」は間違った言葉であり、あなたは人々に規則破るように頼んでいますが、これは役に立ちません。
ピーターテイラー14

回答:


39

ルビー、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(この入力の上部にあるもの)もエクスポートされます。少し面白くするために、このためにOPの例を少し拡張しました。

その他の例

わずかに発散する壁を備えた端部が閉じたチューブ:

plotLaser[{0, 0}, 1.51, 200, {
  {{0, 1}, {20, 1.1}},
  {{0, -1}, {20, -1.1}},
  {{20, 1.1}, {20, -1.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 14

1
@AJMansfield三角形には小さな穴があり、アニメーションの最初に見ることができます。
マーティンエンダー14

それがどのように機能するかを説明するパラグラフを書くことができたら素晴らしいでしょう。
JeffSB 14

@JeffSBこれでMathematicaコードを文書化しました。Rubyバージョンは、変数名が不明瞭であってもプロットせずにほぼ同じことを行います。
マーティンエンダー

@MartinBüttnerありがとう。他の人がどのようにそれをしているのかを見るのは面白いです。跳ね返った最後のミラーを除外しなければならないことに気付いたのですか?私はしませんでしたが、私はすぐにそれを見つけました。私はあなたのコードに非常に小さな数があることに気づきました。それが私がそれがどのように機能するかを尋ねた理由です。
JeffSB 14

18

Python 3(421C 390C、366C)

builtin.complex2Dベクトルとして 使用します。そう

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、リアルタイム調整および計算

つまり、エンドポイント(またはレーザー、ミラー)をドラッグすると、トラックがレンダリングされます。また、質問で説明したものと@MartinBüttnerが使用したものの2種類の入力をサポートします。

スケーリングも自動的に調整されます。

今のところ、アニメーションはありません。たぶん私は後でそれを改善します。ただし、白色点をドラッグすると、別の種類のアニメーションを見ることができます。ここでオンラインで試してみてください、面白いです!

プロジェクト全体はこちらにあります

ケース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は、入力または出力の仕様を満たしていません。
ピーターテイラー14

@Rayおまけの答えとして、これは問題ありません。コードとゴルフの答えを得るには、仕様を正確に満たす必要があります。
カルビンの趣味

@PeterTaylor Meet specを今すぐ。
レイ14

ミラーを動かす方法は本当に素晴らしいです!あなたのものは私の最初の+1票です。
JeffSB 14

17

HTML JavaScript、 10,543947 889

バグを修正し、出力が質問の仕様を満たしていることを確認しました。以下のWebページには、ゴルフバージョンとグラフィカルボーナスバージョンがあります。また、@ 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

ここに画像の説明を入力してください

説明

Webページのコードはコメント化されています。基本的に、レーザーとミラーが無限に長いと仮定して、レーザーとすべてのミラーの交差を計算します。次に、交点がミラーとレーザーの有限長内にあるかどうかを確認します。次に、最も近い交差点を取り、そのポイントにレーザーを移動し、レーザーがすべてのミラーを見落とすまで続けます。

非常に楽しいプロジェクト。この質問をしてくれてありがとう!

読み取り可能なコード

// 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
カルビンの趣味

1
実際のプログラムはどこですか?
ピーターテイラー14

以下のWebページにあります:goo.gl/wKgIKD
JeffSB 14

通常、このサイトの回答には、質問に回答するために必要なすべてのコードを含める必要があります。この質問の場合、それは標準入力から読み取り、標準出力に書き込むプログラムです。さらに、これはコードとゴルフの問題なので、可能な限りコードを最小限に抑える必要があります。少なくとも、コメントと不要な空白を削除し、可能な場合は1文字の識別子を使用します。
ピーターテイラー14

@JeffSBこの投稿は、ボーナス回答に対して有効であり、受け入れられた回答に対してのみ有効です。(ただし、すべてのコードを含めることができます。)
カルビンの趣味

6

パイソン-765

良い挑戦。これは標準入力から入力を取得し、標準出力に出力する私のソリューションです。@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:仕様を満たしていません。指定された出力は、2つの数字と1つの画像ではなく、2つの数字です。
ピーターテイラー14

@PeterTaylorつまり、stdin / stdoutのことですか?
レイ14

@willemボーナスの答えとして、これは問題ありません。コードとゴルフの答えを得るには、仕様を正確に満たす必要があります。
カルバンの趣味14

コードを更新しました
ウィレム14

sys.argvは標準入力ではないことに注意してください。
レイ14

6

Matlab(388)

プロット

プロット プロット2

コンセプト

反射点

反射点を計算するには、基本的に2本の直線を交差させる必要があります。1つはポイントp0とベクトルvで、もう1つは2つのポイントp1、p2の間です。したがって、解く方程式は(s、tはパラメーターです):p0 + t v = s p1 +(1-s)* p2です。

パラメータsはミラーの重心座標なので、0の場合

ミラーリング

vのミラーリングは非常に簡単です。|| v ||と仮定しましょう = || n || = 1ここで、nは現在のミラーの法線ベクトルです。次に、式v:= v-2 ** nを使用できます。ここで、<、>は内積です。

ステップの有効性

最も近い「有効な」ミラーを計算するとき、それを有効にするいくつかの基準を考慮する必要があります。最初に、ミラーのインターセプトポイントは2つのエンドポイントの間にある必要があるため、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。
アレックスA.

ハハありがとう!私はあなたのコメントが見たときにこれをやったことさえ覚えていませんでしたポップアップ=)
flawr

ハハその後、私のコメントはおそらくあなたを取り戻すでしょう!(あなたがこれを投稿したとき。)
アレックスA.
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.