이 방은 얼마나 밝습니까? pt. 1


25

이 질문과 관련 있습니다.

공간은 2 차원 좌표들의 순서화 된리스트로 표시되는 (반드시 볼록) 이외의 교차 다각형, 정의된다. 충분히 밝은 전구는 실내의 특정 지점에 배치되어 모든 방향으로 빛을 방출합니다. 당신의 임무는 방의 전체 조명 영역을 찾는 것입니다. 합리적인 형식으로 입력 할 수 있습니다. 다각형 / 방의 점과 광원의 좌표는 유리수입니다. 그것들은 시계 방향 또는 반 시계 방향으로 찍을 수 있으며, 어느 형식이든 좋습니다. 문제의 테스트 사례는 시계 반대 방향으로 제공됩니다.

다음 그림은 자주색 점이 광원을 나타내고 음영 처리 된 영역이 조명 된 영역을 나타내는 두 개의 예 방을 보여줍니다.조명이있는 방의 그림-음영 처리 된 영역이 조명됩니다.

테스트 사례 :

(1/2, 18)
(1,3)
(5,1/2)
(7,5)
(12,7)
(16,3)
(15,11)
(8,19)
(3,7)
Light source located at (5,8)
Answer: 815523/6710 ≈ 121.538

다음은 해당 테스트 사례에 대한 솔루션을 그래픽으로 나타낸 것입니다. 원래 다각형에없는 솔루션을 정의하는 두 점은 (55/61, 363/61) 및 (856/55, 357/55)입니다. 여기에 이미지 설명을 입력하십시오

이 수식은 면적을 계산하는 데 도움이 될 수 있습니다. https://en.wikipedia.org/wiki/Shoelace_formula

이것은 이므로 바이트 단위의 가장 짧은 코드가 이깁니다.


궁금한 점이 있다면 그림 2를 그리는 데 시간이 오래 걸리고 파트 2를 게시하는 데 시간이 걸릴 수 있으며 해결 방법도 모릅니다.
리깅

다각형 / 방의 점과 광원의 좌표는 유리수입니다.
리깅

꼭짓점의 개수에 상한이 있습니까? 아니면 이론적으로 프로그램이 무제한으로 처리 할 수 ​​있어야합니까? 또한 코드 골프 태그가 손상되었습니다. 그건[tag:code-golf]
Veskah

3
아, 좋은 오래된 신발 끈 공식 ! 그건 그렇고, 실제로 MathJax가 있으므로 수식을 이미지로 포함 할 필요가 없습니다.
주세페

1
예, 시계 방향으로 주문할 수 있습니다. 테스트 케이스는 반 시계 방향으로 주문되지만 이것이 "합리적인 형식"에 해당한다고 생각합니다.
리깅

답변:


12

파이썬 3 388 398 408 409 415 417 493 바이트


더 정확하게하려면 증가 n

from random import*
u=uniform
c=lambda A,B,C:(C[1]-A[1])*(B[0]-A[0])>(B[1]-A[1])*(C[0]-A[0])
I=lambda A,B,C,D:c(A,C,D)!=c(B,C,D)and c(A,B,C)!=c(A,B,D)
def a(l,v,n=9**6,s=0):
 g=lambda i:(min(x[i]for x in v),max(x[i]for x in v))
 for _ in'x'*n:
  h=((u(*g(0)),u(*g(1))),l);s+=any([I(*f,*h)for f in list(zip(v,v[1:]+[v[0]]))])^1
 return(abs(g(0)[0]-g(0)[1])*abs(g(1)[0]-g(1)[1]))*float(s/n)

기본 몬테카를로 접근. 아래 나열된 단계.

  1. 모양이 차지하는 x 및 y 범위를 찾으십시오.
  2. 정점에 의해 생성 된 모서리리스트 생성
  3. 여러 번 반복 (더 좋을수록 좋음)
  4. x, y 범위 안에 임의의 점 (j, k)을 만듭니다.
  5. 가장자리와 빛으로 생성 된 선분과 임의의 점을 가로채는 모서리가 있는지 확인하십시오. 모서리가 가로 채면 변수를 증가시킵니다.s
  6. 분할 s전체 범위의 면적으로 다음 곱셈, 총 숫자로.

언 골프 버전 :

import random

def ccw(A,B,C):
    return (C[1]-A[1])*(B[0]-A[0]) > (B[1]-A[1])*(C[0]-A[0])

def intersect(A,B,C,D):
    return ccw(A,C,D) != ccw(B,C,D) and ccw(A,B,C) != ccw(A,B,D)

def lit_area(light, vertices):
    # points: list of points
    # i     : x => i=0
    #       : y => i=1
    get_range = lambda i: (min(x[i] for x in vertices), max(x[i] for x in vertices))
    xr = abs(get_range(0)[0] - get_range(0)[1])
    yr = abs(get_range(1)[0] - get_range(1)[1])

    edges = list(zip(vertices, vertices[1:] + [vertices[0]]))

    num_sims = 1000000

    num_successes = 0
    for _ in range(num_sims):
        guess_x = random.uniform(*get_range(0))
        guess_y = random.uniform(*get_range(1))

        light_guess_line = ((guess_x, guess_y), light)

        if not any([intersect(*e, *light_guess_line) for e in edges]):
            num_successes += 1
    return float(num_successes / num_sims) * (xr * yr)


if __name__ == "__main__":
    points = [
    (1/2, 18),
    (1,3),
    (5,1/2),
    (7,5),
    (12,7),
    (16,3),
    (15,11),
    (8,19),
    (3,7)
    ]
    light_source = (5,8)
    print("Area lit by light: %f"% lit_area(light_source, points))

온라인으로 사용해보십시오!

라인 교차 알고리즘에 대한 크레딧

또한 골프를 치는 방법에 대한 모든 유용한 의견을 제시하십시오.


첫 번째 줄은 -2 바이트에 대해 from random import*(줄 바꿈) 이 될 수 있습니다u=uniform
Conor O'Brien

1
함수의 4 개 공백 각각을 단일 공백으로 교체하여 바이트를 더 줄일 수 있으며 이후 공간을 제거 할 수 있습니다.g=lambda i:
Conor O'Brien

n10의 거듭 제곱이어야 합니까 ? 그렇지 않으면 9의 거듭 제곱을 사용하여 바이트를 절약 할 수 있습니다
.

아니요, 10의 거듭 제곱은 필요하지 않습니다. 내일 당신의 제안을 모두 넣겠습니다! 그때까지, 행복한 발렌타인 데이 여러분!
JPeroutek

으로 ConorO'Brien는 @ 언급, 당신은 최고의 공백의 부하를 제거 할 수 있습니다. 의 공간 외에 i:(min공간을 x[i]for제거 할 수도 있습니다. 또한 일 return float(s/n)*(r*t)수 있습니다 return(r*t)*float(s/n). 그리고 나는 확실하지 않지만 변수를 한 번만 사용하기 때문에 변수 re제거하고 직접 사용할 수 없습니까? 그것은 g수정되지 않았지만 약간 다른 결과를 제공 하므로 부분이 약간 혼란 스럽습니다 (결과가 약간 다른 이유를 이해하기에는 파이썬에 익숙하지 않습니다).
Kevin Cruijssen

5

하스켈 , 559 618 632 바이트

r(a:b)=b++[a]
s=zip<*>r
(?)a=sum.zipWith(*)a
o(a,b)=r a?b-a?r b
(a,b)!(c,d)=(c-a,d-b)
(a,b)#(c,d)=a*d-b*c
x i a@(e,f)b j c d|let k@(g,h)=a!b;l=c!d;m=c!a;n=l#k;o=m#l/n;p=m#k/n;q|i>0=o<0||o>1|let=o<=0||o>=1;r|n==0||q||p<0||p*j>1=[]|let=[(e+o*g,f+o*h)]=r
(a&b)(c:e@(d:_))|let(f,g)=span(/=d)b;h=zip f$r$f++[d]=concat[[k,l]|(i,j)<-h,[[k],[l]]<-[x 1 i j 0 a<$>[c,d]],and[x 0 m n 1 a o==[]|o<-[k,l],(m,n)<-h,(m,n)/=(i,j)]]++(a&g)e
(_&_)_=[]
z a b=sum[o$unzip[c,a,d]|e@(f:_)<-[[c|c<-b,and[all(==c)$x 1 d e 1 a c|(d,e)<-s b]]],(c,d)<-s$a&until((f==).head)r b$e++[f]]/2

정확한 해결책 (버그 금지). 하스켈에는 정확한 합리적 산술이 내장되어 있습니다. 온라인으로 사용해보십시오!

이 나타내는 것 815523/6710없는, 814643/6710예 방 들어, 상기 제 1 벽 교차점은 다음과 같이 계산된다 (55/61, 363/61). Monte Carlo 항목이 (느리게) 동일한 결과로 수렴되기 때문에 이것이 정확하다고 확신합니다.

전설:

z light roomPoints
    -- Main function, returns lit area.
    -- Compute list of visible corners in the room, then calls (&).
(&) light roomPoints' visibleCorners
    -- Compute visibility polygon. visibleCorners is the subset of points
    -- that are visible from the light. The first point of roomPoints'
    -- must coincide with the first visibleCorner.
x pEndpoints p1 p2 qSegment q1 q2
    -- Intersect line segments (p1, p2) and (q1, q2).
    -- If pEndpoints, exclude endpoints p1, p2.
    -- If not qSegment, allow intersection to extend past q2 (i.e. raycast).
r   -- Rotate list by one, used to construct closed loops etc.
s   -- Construct closed loop
(!) -- Vector between two points
(?) -- Dot product
(#) -- Cross product
o   -- Polygon area

보너스 : 테스트를위한 광택 GUI. 포인트를 이동하려면 다음을 클릭하십시오.

import qualified Graphics.Gloss as G
import qualified Graphics.Gloss.Interface.IO.Interact as GI

solnPoly a b|let c@(d:_)=[c|c<-b,and[all(==c)$x 1 d e 1 a c|(d,e)<-s b]]=a&until((d==).head)r b$c++[d]
solnArea = z

main =
  let fromRatP (x, y) = (fromRational x, fromRational y)
      displayScale = 10
      scalePoints = G.scale (fromInteger displayScale) (fromInteger displayScale)
      displayMode = G.InWindow "" (512, 512) (0, 0)
      drawBasePoly pointSz ps =
          mconcat $ G.lineLoop ps :
                    [G.translate x y (G.circleSolid pointSz) | (x, y) <- ps]
      drawVisPolyOf light ps =
          G.color G.blue $ drawBasePoly 0.2 $ map fromRatP $ solnPoly light ps
      drawLight (x, y) =
          G.translate x y $
          G.color G.yellow (G.circleSolid 0.5) <> G.circle 0.5
      draw (light, ps) =
          mconcat [
              scalePoints $ drawLight (fromRatP light),
              scalePoints $ drawBasePoly 0.4 (map fromRatP ps),
              scalePoints $ drawVisPolyOf light ps,
              G.translate (-200) (-50) $ G.scale 0.2 0.2 $
                G.color G.blue $ G.text $ "Lit area: " ++ show (solnArea light ps)
          ]
      event (GI.EventKey (GI.MouseButton GI.LeftButton) GI.Down _ (curx_, cury_)) (light, ps) =
          let dist (x,y) (x',y') = (x'-x)^2 + (y'-y)^2
              curx = curx_ / fromInteger displayScale
              cury = cury_ / fromInteger displayScale
              cursorR = (fromInteger$round curx, fromInteger$round cury)
              maxDist = 3
              snapAmount = 1
              (d, i) = minimum [(dist p cursorR, i) | (p, i) <- zip (light : ps) [0..]]
              snapTo n a = fromInteger$n*round(a/fromInteger n)
              snapCursor = (snapTo snapAmount curx, snapTo snapAmount cury)
              light' | i == 0 && d < maxDist^2 = snapCursor
                     | otherwise = light
              ps' | i > 0 && d < maxDist^2 = take (i-1) ps ++ [snapCursor] ++ drop i ps
                  | otherwise = ps
          in (light', ps')
      event _ state = state
      state0 =
        ((2, 2), [(0, 0), (10, 0), (10, 5), (20, 0), (20, 20), (15, 5),
                  (10, 10), (6, 10), (10, 12), (0, 12), (4, 10), (0, 10)])
  in G.play displayMode G.white 60
            state0
            draw
            event
            (\_ -> id)

스크린 샷


사실, 네 말이 맞아 나는 오타 롤을 만들었을 것이다. 약간 그 숫자를 업데이트합니다
리깅

1

APL + WIN

이것은 내 논리를 보여주기 위해 제공하는이 흥미로운 도전의 골치 아픈 버전입니다. 내 고대 버전의 APL + WIN은 골프 중첩 제어 구조에 적합하지 않습니다. 더 현대적인 APL이 더 잘할 수 있습니다.

독자가 논리를 검증하면이 솔루션을 골라 보겠습니다. 논리가 잘못되면 간단히 삭제합니다.

r←b Room v

⍝Separate x and y coordinates of vertices               
x←v[;1] ⋄ y←v[;2]

⍝Intercept and slope of each line segment and ray through each vertex
s←(y,¨1⌽y)⌹¨(1E¯9+1,[1.1]¨x,¨1⌽1E¯9+x)
l←(y,¨b[2])⌹¨(1E¯9+1,[1.1]¨x,¨b[1]+1E¯9)                          

⍝Coordinates of vertices
x←x,¨1⌽x ⋄ y←y,¨1⌽y                                                  

⍝Initialise intersection matrix
r←((⍴s),0)⍴0

⍝Evaluate intersection matrix 
:for i :in ⍳⍴l 
    t←0⍴0
    :for j :in ⍳⍴s
        t←t,⍎1⍕∊((↑∊l[i])-↑∊s[j])÷((1↓∊s[j])-1↓∊l[i]) 
    :endfor
    z←r←r,t
:endfor 

⍝Identify x coordinates of valid intersections in the direction of the ray
:for i :in ⍳⍴l 
    t←(r[i;i])
    :for j :in ⍳⍴s
        :if t<b[1] 
            r[j;i]←r[j;i]×(r[j;i]<t)^r[j;i]>⌊/∊x[i]
        :else
            r[j;i]←r[j;i]×(r[j;i]>t)^r[j;i]<⌈/∊x[i]
        :endif
    :endfor
 :endfor

⍝Identify the edges intersected
e←(+/r≠0)/⍳⍴l 

⍝Intersection x coordinates
cx←(+/r)[e]

⍝Intersection y coordinates
cy←⍎1⍕+/¨(s[e])× 1,¨(+/r)[e]

⍝Replace original coordinates that are in shadow
x[e]←(1↓¨x[e]),¨cx
y[e]←(1↓¨y[e]),¨cy

⍝Calculate lit area
r←+/.5×(|-/¨x)×|-/¨y                                               

0

R , 296255 바이트

function(s,l,u=cbind(s,s[,1]),d=t(diff(t(u))),q=l-u,r=apply(s,1,range),g=c(diff(r)))mean(replicate(1e6,!any((q[i<-1:ncol(s)*2]*(p=runif(2)*g+r[1,]-u)[j<-i-1]>p[i]*q[j])!=(q[i+2]*p[i+1]>q[i+1]*p[i+2])&(p[i]*d[j]>p[j]*d[i])!=(q[i]*d[j]>q[j]*d[i]))))*prod(g)

온라인으로 사용해보십시오!

이것은 파이썬 답변 의 추가 축소 버전입니다 . 핵심 몬테카를로 방법은 동일하지만 일부 기능을 더 짧게 재 배열했습니다. 첫 번째 반복에서 재배치가 지나치게 공격적이었고 파이썬에 더 가까운 교차 알고리즘 버전으로 돌아가 길이와 속도를 모두 최적화 할 수 있음을 깨달았습니다.

다음은 결과를 나타내는 ungolfed 버전입니다.

find_lit_ungolf <- function(shape, light, plot = TRUE) {
  lines <- cbind(shape ,shape[,1])
  diffs <- t(diff(t(lines)))
  light_minus_lines <- light - lines
  shape_range <- apply(s,1,range)
  shape_range_diff <- c(diff(shape_range))
  successes <- t(replicate(
    n = 1e5,
    {
      random_point <- runif(2) * shape_range_diff + shape_range[1, ]
      random_minus_lines <- random_point - lines
      q <- light_minus_lines
      p <- random_minus_lines
      d <- diffs
      i <- 1:ncol(s)*2
      success <-
        !any((q[i]*p[i-1]>p[i]*q[i-1])!=(q[i+2]*p[i+1]>q[i+1]*p[i+2])&(p[i]*d[i-1]>p[i-1]*d[i])!=(q[i]*d[i-1]>q[i-1]*d[i]))
      c(random_point, success)
    }))
  colnames(successes) <- c("x", "y", "success")
  if (plot) {
    shape <- t(shape)
    colnames(shape) <- c("x", "y")
    print(ggplot(as_tibble(successes), aes(x, y)) +
      geom_point(aes(colour = factor(success)), alpha = 0.3) +
      geom_polygon(data = as_tibble(shape), alpha = 0.2) +
      annotate("point", light[1], light[2], col = "yellow"))
  }
  mean(successes[, 3]) * prod(shape_range_diff)
}
find_lit_ungolf(s, l)

방에 빛의 줄거리

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.