Куди йде лазер?


34

Візьміть двовимірну сітку і намалюйте на ній ряд сегментів ліній, щоб зобразити дзеркала. Тепер виберіть точку, щоб розмістити теоретичний лазер і кут, щоб визначити напрямок, на який він вказує. Питання: якщо ви слідуєте шляху лазерного променя на деяку задану відстань, в якій точці координат ви знаходитесь?

Приклад:

приклад лазера

На цьому зображенні, 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 градусів. Dтакож може бути 0.
  • Дзеркал може бути довільно багато (у тому числі взагалі жодне).
  • Порядок дзеркал не повинен мати значення.
  • Ви можете припустити, що вхід буде надходити кратним 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

(Зверніть увагу, що зображення було намальовано від руки і ці значення є лише наближеннями. Вхідні значення Мартіна Бюттнера

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 очок баунті настільки привабливі, що гольф стає другорядним моментом.
Говард

1
@PeterTaylor Ти цитуєш мене добре поза контекстом. Я читаю відповіді бонусу розділу ОП, оскільки два подання є абсолютно різними, але належать до одного і того ж посади, якщо намагаються обидва (що означатиме, що також відповідь попкону також буде добре). У будь-якому разі, це ваші голоси, і саме від вас залежить, як ви їх використовуєте, і я, мабуть, додам версію для гольфу в будь-який момент. Але я думаю, що ОП може уточнити, чи він мав намір відповіді, що стосуються лише попкону, бути дійсними чи ні.
Мартін Ендер

1
@ MartinBüttner, " бонус " означає " додатковий, додатковий ". Це не є головною проблемою. І питання має лише один тег, код-гольф .
Пітер Тейлор

2
@PeterTaylor MartinBüttner має рацію. Відповідаючи лише на бонусну частину питання, це чудово. Я сказав, що відповіді на бонус можуть бути без гольфу та поблажливішими з вводу-виводу, і всі поточні подання про бонус мені здаються прекрасними. Найкоротша уявлення , що робить точно слідувати специфікаціям буде загальноприйнятим відповіддю. Наразі жодних подань не роби, але зі мною це нормально.
Захоплення Кальвіна

1
У такому випадку " бонус " - це неправильне слово, і ви просите людей порушити правила , що не є корисним.
Пітер Тейлор

Відповіді:


39

Рубін, 327 байт

(прокрутіть донизу)

Математика, бонусна відповідь

введіть тут опис зображення

Я зараз лише збираюся на графічне подання. Я можу пізніше перенести це на Рубі і пограти в гольф, якщо мені це подобається.

(* 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 (той, що знаходиться вгорі для цього вводу). Я трохи продовжив приклад ОП для цього, щоб зробити його трохи цікавішим.

Більше прикладів

Трубка з трохи розходячимися стінками, але закритим кінцем:

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, а також деякий гольф і переконайтесь, що воно відповідає критеріям вводу / виводу.


Як у вас лазер перетне дзеркальний трикутник в кінці першого прикладу?
AJMansfield

1
@AJMansfield У трикутнику є невеликий отвір, який можна побачити на початку анімації.
Мартін Ендер

Було б чудово, якби ви могли написати абзац, який пояснює, як він працює.
JeffSB

@JeffSB Я вже задокументував код Mathematica. Версія Ruby робить майже таку саму річ із незрозумілими назвами змінних та без побудови графіків.
Мартін Ендер

@ MartinBüttner Дякую Цікаво подивитися, як це роблять інші люди. Чи усвідомлювали ви до того, як сталося, що вам потрібно виключити останнє дзеркало, від якого ви відскочили? Я цього не зробив, але досить швидко зрозумів. Я помітив дуже малу кількість у вашому коді, і тому я попросив подивитися, як це працює.
JeffSB

18

Python 3 (421C 390C, 366С)

Використовуйте builtin.complexяк 2d вектор. Так

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, кофескрипт, коригування в режимі реального часу та розрахунок

Це означає, що ви перетягуєте будь-які кінцеві точки (або лазер, дзеркала), після чого трек виводиться. Він також підтримує два типи вводу, той, який описаний у запитанні та той, який використовує @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 не відповідає специфікації для введення або виводу.
Пітер Тейлор

@Ray Як бонусна відповідь це чудово. Він повинен точно відповідати специфікації, щоб стати кодовою відповіддю на гольф.
Захоплення Кальвіна

@PeterTaylor Знайомтесь спец.
Рей

Це дуже здорово, як ви можете переміщати дзеркала навколо! З вашим першим +1 голосом.
JeffSB

17

HTML JavaScript, 10 533, 947 889

Я виправив помилку і переконався, що результат відповідає специфікації питання. Веб-сторінка нижче має версію для гольфу, а також графічну бонусну версію. Я також виправив помилку, на яку вказав @Ray, яка врятувала 58 символів. (Дякую Рею.) Ви також можете запустити код для гольфу в консолі JavaScript. (Зараз я використовую зелений лазер потужністю 2 МВт.)

Код для гольфу

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.
Захоплення Кальвіна

1
Де реальна програма?
Пітер Тейлор

Це на веб-сторінці тут: goo.gl/wKgIKD
JeffSB

Відповіді на цьому веб-сайті, як правило, повинні містити весь код, необхідний для відповіді на питання. У випадку з цим питанням, це програма, яка читає з stdin і записує в stdout. Крім того, оскільки це питання щодо коду-гольфу, ви повинні максимально звести до мінімуму код: принаймні, видаляючи коментарі та зайві пробіли та використовуючи однозначні ідентифікатори, де це можливо.
Пітер Тейлор

@JeffSB Це подання дійсне для бонусної відповіді, тільки не прийнятої відповіді. (Хоча ви, можливо, захочете включити весь свій код.)
Захоплення Calvin's

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)

А ось код без вовків з бонусною цифрою

дзеркала

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 Як бонусна відповідь це чудово. Він повинен точно відповідати специфікації, щоб стати кодовою відповіддю на гольф.
Захоплення Кальвіна

Я оновив код
Віллем

Зауважте, що sys.argvце не stdin.
Рей

6

Матлаб (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.
Олексій А.

Ха-ха, дякую! Я навіть навіть не пам’ятав, що робив це, коли побачив, що ваш коментар спливає =)
недолік

Ха-ха, тоді мій коментар, ймовірно, повертає тебе назад! (До коли ви це публікували.)
Олексій А.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.