この部屋はどれくらい明るくなっていますか?🔥pt。1


25

この質問に関連しています

部屋は、 2次元座標の順序付けられたリストとして表現(必ずしも凸ではない)の非交差多角形であると定義されます。十分に明るい電球が部屋の中の特定のポイントに置かれ、あらゆる方向に光を放射します。あなたの仕事は、部屋の照明エリア全体を見つけることです。合理的な形式で入力を行うことができます。ポリゴン/部屋の点と光源の座標は有理数です。時計回りまたは反時計回りに撮影できますが、どちらの形式でも構いません。問題のテストケースは反時計回りに与えられます。

次の図は、紫のドットが光源を表し、影付きの領域が照らされた領域を表す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

以下に、そのテストケースのソリューションを図で示します。元のポリゴン上にないソリューションを定義する2つのポイントは、(55 / 61、363 / 61)と(856 / 55、357 / 55)です。 ここに画像の説明を入力してください

この式は、面積の計算に役立つ場合があります。 https://en.wikipedia.org/wiki/Shoelace_formula

これはであるため、バイト単位の最短コードが優先されます。


好奇心those盛な人のために、第2部は投稿するのに時間がかかるかもしれません。なぜなら、絵を描くのに永遠に時間がかかるからです。また、解決方法もわかりません。
リギング

ポリゴン/部屋の点と光源の座標は有理数です。
リグ

頂点の数に上限はありますか、それとも理論的にプログラムで無制限の数を処理できる必要がありますか?また、コードゴルフタグが壊れています。それは[tag:code-golf]
ヴェスカー

3
ああ、古き良き靴ひも式!ちなみに、実際にはMathJaxがあるので、数式を画像として埋め込む必要はありません。
ジュゼッペ

1
はい、時計回りに注文することが保証されます。テストケースは、反時計回りに命じているが、私はこれが該当すると思う「いずれかの合理的な形式。」
不正

回答:


12

Python 3、388 398 408 409 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ます
コナーオブライエン

1
関数内の4つのスペースをそれぞれ1つのスペースに置き換えて、さらにバイトを削り、その後にスペースを削除できますg=lambda i:
Conor O'Brien

ないn10の力でなければなりませんか?そうしないと、9の力を利用してバイトを保存することができます
ニールA.

いいえ、10の累乗は必要ありません。明日、すべての提案を掲載します!それまで、みなさん、ハッピーバレンタインデー!
JPeroutek

以下のようConorO'Brienは@言及、あなたは一流の空白の負荷を削除することができます。そして、のスペースに加えてi:(minのスペース、x[i]for同様に除去することができます。また、return float(s/n)*(r*t)することができますreturn(r*t)*float(s/n)。そして、私は完全に確信していませんが、変数re使用して、一度だけ使用するので、削除して直接使用することができませんか?g変更されていなくても、どういうわけか結果が少し異なるので、その部分は少し混乱します(結果がわずかに異なる理由を理解するにはPythonにあまり詳しくありません)。
ケビンクルーッセン

5

Haskell、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

正確な解決策(バグがない場合)。Haskellには正確な有理数演算が組み込まれています。オンラインでお試しください!

これは815523/6710814643/6710例の部屋ではなくを与え、最初の壁の交差はとして計算されることに注意してください(55/61, 363/61)。モンテカルロエントリが(ゆっくり)同じ結果に収束するため、これが正しいと確信しています。

伝説:

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

R296 255バイト

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)

オンラインでお試しください!

これは、Pythonの回答をさらに縮小したバージョンです。コアモンテカルロ法は同じですが、いくつかの関数を再配置して短くしました。最初のイテレーションでは、再配置に積極的でしたが、Pythonに近いバージョンの交差アルゴリズムに戻ることで、長さと速度の両方を最適化できることに気付きました。

以下は、結果もプロットした、無料版です。

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.