Căn phòng này sáng như thế nào? 🔥 pt. 1


25

Liên quan đến câu hỏi này .

Một căn phòng được xác định là một đa giác không giao nhau (không nhất thiết phải lồi), được biểu thị dưới dạng một danh sách theo thứ tự các tọa độ 2 chiều. Một bóng đèn đủ sáng được đặt tại một điểm cụ thể trong phòng và phát ra ánh sáng theo mọi hướng. Nhiệm vụ của bạn là tìm tổng diện tích chiếu sáng của căn phòng. Bạn có thể nhận đầu vào ở bất kỳ định dạng hợp lý. Các điểm trên đa giác / phòng cũng như tọa độ của nguồn sáng là các số hữu tỷ. Chúng có thể được chụp theo chiều kim đồng hồ hoặc ngược chiều kim đồng hồ, cả hai định dạng đều ổn. Các trường hợp thử nghiệm trong vấn đề được đưa ra ngược chiều kim đồng hồ.

Hình ảnh sau đây cho thấy hai phòng mẫu, trong đó chấm màu tía tượng trưng cho nguồn sáng và vùng bóng mờ đại diện cho vùng được chiếu sáng.Một bản vẽ của một căn phòng được chiếu sáng - khu vực bóng mờ được chiếu sáng

Trường hợp thử nghiệm:

(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

Dưới đây là một mô tả đồ họa của giải pháp cho trường hợp thử nghiệm đó. Hai điểm xác định giải pháp không nằm trên đa giác ban đầu là (55/61, 363/61) và (856/55, 357/55). nhập mô tả hình ảnh ở đây

Công thức này có thể hữu ích trong việc tính toán diện tích. https://en.wikipedia.org/wiki/Shoelace_formula

Vì đây là , mã ngắn nhất tính bằng byte sẽ thắng.


Đối với những người tò mò, phần 2 có thể mất một chút thời gian để đăng bởi vì tôi sẽ mất mãi mãi để vẽ những bức tranh, và tôi cũng không biết làm thế nào để giải quyết nó.
lận

Các điểm trên đa giác / phòng cũng như tọa độ của nguồn sáng là các số hữu tỷ.
lận

Có giới hạn trên về số lượng đỉnh hay về mặt lý thuyết chương trình của bạn có thể xử lý một số lượng không giới hạn? Ngoài ra, thẻ mã golf của bạn bị hỏng. đó là[tag:code-golf]
Veskah

3
Ah, công thức dây giày cũ tốt ! Nhân tiện, chúng tôi thực sự có MathJax nên bạn không cần nhúng công thức dưới dạng hình ảnh.
Giuseppe

1
Vâng, họ có thể được đảm bảo để được đặt hàng theo chiều kim đồng hồ, sau đó. Trường hợp thử nghiệm được đặt hàng ngược chiều kim đồng hồ, nhưng tôi nghĩ rằng điều này thuộc về bất kỳ định dạng hợp lý nào.
Nghiêm

Câu trả lời:


12

Python 3 , 388 398 408 409 415 417 493 byte


Để làm cho nó chính xác hơn, tăng 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)

Cách tiếp cận cơ bản Monte-Carlo. Các bước được liệt kê dưới đây.

  1. Tìm phạm vi x và y mà hình dạng chiếm.
  2. Tạo một danh sách các cạnh được tạo bởi các đỉnh
  3. Lặp lại một số lượng lớn lần (càng nhiều càng tốt)
  4. Tạo một điểm ngẫu nhiên (j, k) trong phạm vi x, y.
  5. Kiểm tra xem có bất kỳ cạnh nào chặn với đoạn đường được tạo bởi ánh sáng và điểm ngẫu nhiên không. Nếu bất kỳ cạnh nào chặn, tăng biếns
  6. Chia scho tổng số, sau đó nhân với tổng diện tích phạm vi.

Phiên bản bị đánh cắp:

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))

Hãy thử trực tuyến!

Tín dụng cho thuật toán giao cắt đường

Ngoài ra, tín dụng cho tất cả các nhà bình luận hữu ích về cách chơi golf này hơn nữa.


Dòng đầu tiên có thể trở thành from random import*(ngắt dòng) u=uniformcho -2 byte
Conor O'Brien

1
bạn có thể cạo thêm một số byte bằng cách thay thế mỗi trong số 4 khoảng trắng trong hàm bằng một khoảng trắng và xóa khoảng trống saug=lambda i:
Conor O'Brien

nphải là một sức mạnh của 10? Nếu không, bạn có thể tiết kiệm một byte bằng cách sử dụng sức mạnh 9.
Neil A.

Không, quyền hạn 10 không bắt buộc. Tôi sẽ đưa vào tất cả các đề xuất của bạn vào ngày mai! Cho đến lúc đó, chúc mừng ngày lễ tình nhân nhé mọi người!
JPeroutek

Như @ ConorO'Brien đã đề cập, bạn có thể loại bỏ vô số khoảng trắng hàng đầu. Và ngoài không gian tại i:(min, không gian tại x[i]forcũng có thể được loại bỏ. Ngoài ra, return float(s/n)*(r*t)có thể được return(r*t)*float(s/n). Và tôi không hoàn toàn chắc chắn, nhưng không thể loại bỏ các biến ređược sử dụng trực tiếp, vì bạn chỉ sử dụng chúng một lần? Nó bằng cách nào đó cho kết quả hơi khác nhau mặc dù gkhông được sửa đổi, do đó phần đó làm tôi bối rối một chút (Tôi không quá quen thuộc với Python để hiểu tại sao kết quả hơi khác nhau).
Kevin Cruijssen

5

Haskell , 559 618 632 byte

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

Giải pháp chính xác (chặn lỗi). Haskell đã tích hợp số học hợp lý chính xác. Hãy thử trực tuyến!

Lưu ý rằng điều này mang lại 815523/6710, không 814643/6710, cho phòng ví dụ và giao lộ tường đầu tiên được tính như (55/61, 363/61). Tôi khá chắc chắn rằng điều này là chính xác bởi vì mục nhập Monte Carlo (chậm) hội tụ đến cùng một kết quả.

Huyền thoại:

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

Phần thưởng: Gloss GUI để thử nghiệm. Nhấp vào bên cạnh các điểm để di chuyển chúng.

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)

Ảnh chụp màn hình


Thật ra, bạn nói đúng. Tôi phải làm một lỗi đánh máy lol. Sẽ cập nhật những con số này một chút
lận

1

APL + THẮNG

Đây là một phiên bản vô căn cứ của thử thách thú vị này mà tôi đưa ra để thể hiện logic của mình. Phiên bản cổ của APL + WIN của tôi không phù hợp với các cấu trúc điều khiển lồng nhau. Các APL hiện đại hơn có thể làm tốt hơn - thách thức?

Nếu độc giả xác nhận logic, tôi sẽ chơi golf giải pháp này. Nếu logic sai tôi sẽ xóa.

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 , 296 255 byte

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)

Hãy thử trực tuyến!

Đây là một phiên bản rút gọn hơn nữa của câu trả lời Python . Phương pháp lõi Monte Carlo là như nhau, nhưng tôi đã sắp xếp lại một số hàm để làm cho chúng ngắn hơn. Trong lần lặp đầu tiên của tôi, tôi đã quá tích cực trong việc sắp xếp lại và sau đó tôi nhận ra rằng tôi có thể tối ưu hóa cả chiều dài và tốc độ bằng cách quay lại phiên bản của thuật toán giao cắt gần hơn với con trăn.

Đây là một phiên bản không có bản quyền cũng vẽ kết quả:

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)

Lô ánh sáng trong phòng

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.