그래프의 장력, 1 부 : 물결 모양의 끈


21

[ -3,3 ] 영역에 함수 f (x) = sin (πx) + 0.5 sin (3πx)플로팅합시다 . 이것을 보드에 놓인 느슨한 줄로 해석 할 수 있습니다. 이제 n 손톱을 (x 1 , y 1 ) ~ (x n , y n ) 위치에서 x i ∈ (-3,3)y i ∈ [-1,1] 위치로 보드에 넣습니다 . 줄 끝에 두 개의 작은 구멍이 있다고 가정합니다. 즉, 위치 (-3,0)(3,0)에 있습니다. 이제 줄 끝을 잡고 줄이 팽팽해질 때까지 구멍을 잡아 당길 수 있습니다. 이것은 그래프를 부분 선형 함수로 변형시킵니다.

일부 사진이 도움이 될 수 있습니다. (-2.8, -0.7), (-2.5, -0.9), (-1.2, .2), (-0.5, .8), (0.5, .4), (1.2, -0.9) 에서 8 개의 손톱을 잡습니다. (1.5, -0.6), (1.8, -0.8) . 다음 3 가지 플롯은 위에서 설명한 프로세스를 보여줍니다.

여기에 이미지 설명을 입력하십시오

더 큰 버전 : 오른쪽 클릭-> 새 탭에서 열기

그리고 시각화에 어려움이 있다면 끈 강화의 애니메이션이 있습니다.

여기에 이미지 설명을 입력하십시오

도전

"손톱"(필수적으로 정렬되지는 않음)의 목록이 주어지면, 위의 함수 f 의 모양에서 시작하는 손톱과 팽팽한 끈을 그립니다 .

프로그램이나 함수를 작성하고 STDIN, ARGV 또는 함수 인수를 통해 입력 할 수 있습니다. 결과를 화면에 표시하거나 이미지를 파일로 저장할 수 있습니다.

결과가 래스터 화 된 경우 너비는 최소 300 픽셀, 높이는 100 픽셀이어야합니다. (-3, -1.1)에서 (3,1.1)까지의 좌표 범위는 이미지의 가로 및 세로 범위의 75 % 이상을 포함해야합니다. xy 의 길이 스케일이 같을 필요는 없습니다. 손톱 (최소 3x3 픽셀 사용)과 끈 (최소 1 픽셀 너비)을 표시해야합니다. 축을 포함하거나 포함하지 않을 수 있습니다.

색상을 선택할 수 있지만 배경과 손톱, 끈 (색상이 다를 수 있음)에 대해 두 가지 이상의 구별 가능한 색상이 필요합니다.

모든 손톱이 f 에서 최소 10-5 단위 떨어져 있다고 가정 할 수 있습니다 (따라서 부동 소수점 부정확성에 대해 걱정할 필요가 없습니다).

이것은 코드 골프이므로 가장 짧은 대답 (바이트)이 이깁니다.

더 많은 예

다음은 더 간단한 두 가지 예입니다.

{{-2.5, 1}, {-1.5, -1}, {-0.5, 1}, {0.5, -1}, {1.5, 1}, {2.5, -1}}

여기에 이미지 설명을 입력하십시오

(문자열은 x 축 과 일치합니다 .)

{{-2.7, -0.5}, {-2.3, -0.5}, {-1.7, 0.5}, {-1.3, 0.5}, {-0.7, -0.5}, {-0.3, -0.5}, {0.5, 1}, {1.5, -1}, {2.5, 1}}

여기에 이미지 설명을 입력하십시오

또 다른 도전을 원하십니까?

여기에 파트 II가 있습니다!


손톱이 왼쪽에서 오른쪽으로 정렬되어 있다고 가정 할 수 있습니까?
Ell

@Ell Ah, 잘 잡아. 처음부터 지정하지 않았으므로 아니오. 나는 그것을 명확히 할 것이다.
Martin Ender

답변:


8

파이썬 + 피카 이로, 727 708 608, + PyLab, 383

from pylab import*
def f(N):
 def P(u,w,N):
    T=lambda v,p:(C(v-u,p-u)>0)==(C(w-v,p-v)>0)==(C(u-w,p-w)>0);M=[(i,n)for i,n in enumerate(N)if T(V([n[0],sin(pi*n[0])+sin(3*pi*n[0])/2]),n)]
    if M:i,n=max(M,key=lambda n:C(n[1]-u,w-u)**2);M=P(u,n,N[:i])+[n]+P(n,w,N[i+1:])
    return M
 V=array;C=cross;a=V([3,0]);plot(*zip(*([-a]+P(-a,a,map(V,sorted(N)))+[a])));N and scatter(*zip(*N));show()

f([(-2.8,-0.7),(-2.5,-0.9),(-1.2,0.2),(-0.5,0.8),(0.5,0.4),(1.2,-0.9),(1.5, -0.6),(1.8, -0.8)])

실시 예 1

작동 원리

팽팽한 끈이 두 점 AB를 통과한다는 것을 알고 있다고 가정합시다 (우리는 항상
A = (-3, 0)B = (3, 0)으로 시작할 수 있습니다 ) . AB 사이에 가능한 가장 짧은 경로 , 즉 세그먼트 AB 입니다. 그러나 함수 ( sin πx + ... ) 및 AB로 묶인 영역에 손톱이 있으면 그 중 하나 이상이 문자열을 차단해야합니다. 특히, 상기 영역 내 에서 AB 로부터 가장 먼 손톱 (들) 은 줄을 막아야한다. 따라서 C 가이 손톱 이라면 팽팽한 끈이 통과해야한다는 것을 알고 있습니다.C 에 추가하여 및 B . 이제 ACCB 세그먼트에 대해 프로세스를 반복하고 결국 더 이상 개입 손톱이 없을 때까지 이러한 방식으로 계속 진행할 수 있습니다. 이것은 각 단계에서 선형 스캔을 갖는 이진 분할 및 정복 알고리즘이므로 O (n log n) 의 최상의 복잡도 및 O (n 2 ) 의 최악의 복잡도를 갖습니다 .그림 1


점 목록이 비어 있으면 오류가 발생합니다. 그러나 그 외에는 분명히 희망이 없습니다!
feersum

@feersum 잘 잡아. 결정된.
Ell

3

파이썬 + pylab, 576 바이트

연산:

나는 문제를 (-3, 0) 에서 (3, 0) 까지의 최단 경로를 찾는 것으로 해석하여 경로의 한 지점을 f (x)의 한 지점으로 연결하는 수직선 세그먼트 는 못을 넘지 않습니다. 적어도 하나의 손톱이 존재하는

각각의 x 에서, 그 x 의 손톱에 의해 주어진 최소 상한 및 최대 하한을 찾으십시오 . 이 경계에 의해 주어진 점들과 시작점과 끝점을 그래프의 꼭짓점으로 고려하십시오. 두 개의 정점 사이의 유클리드 거리에 의해 주어진 가중치를 가진 모서리를 추가합니다. 두 정점 사이의 선분이 각 중간 x 좌표에 대한 상한 및 하한 내에 속합니다. 이 그래프에서 최단 경로를 찾으십시오.

27 개의 랜덤 포인트가있는 예 :

(-0.367534, -0.722751), (-0.710649, -0.701412), (1.593101, -0.484983), (1.771199, 0.681435), (-1.878764, -0.491436), (-0.061414, 0.628570), (-0.326483, -0.512950), (0.877878, 0.858527), (1.256189, -0.300032), (1.528120, -0.606809), (-1.343850, -0.497832), (1.078216, 0.232089), (0.930588, -0.053422), (-2.024330, -0.296681), (-2.286014, 0.661657), (-0.009816, 0.170528), (2.758464, 0.099447), (-0.957686, 0.834387), (0.511607, -0.428322), (-1.657128, 0.514400), (1.507602, 0.507458), (-1.469429, -0.239108), (0.035742, 0.135643), (1.194460, -0.848291), (2.345420, -0.892100), (2.755749, 0.061595), (0.283293, 0.558334), 

절름발이 예

골프

for j in R(i&~1)루프 에서 몇 개의 들여 쓰기 공간으로 표시되는 것은 실제로 탭이어야합니다.

from pylab import*
P=((3,0),(-3,0))+input()
X=sorted(set(zip(*P)[0]))
l=len(X)*2
if l>4:scatter(*zip(*P[2:]))
f=lambda x:sin(pi*x)+sin(3*pi*x)/2
B=[[max([-9]+[p[1]for p in P if x==p[0]and p[1]<f(x)]),min([9]+[p[1]for p in P if x==p[0]and p[1]>f(x)])]for x in X]
b=zeros(l);b[2:]=inf
v=list(b)
R=range
for i in R(l):
 for j in R(i&~1):
    A=B[j/2][j&1];D,d=B[i/2][i&1]-A,X[i/2]-X[j/2];K=1;c=b[j]+norm((d,D))
    for k in R(j/2+1,i/2):C=A+D/d*(X[k]-X[j/2]);K&=C<B[k][1];K&=C>B[k][0]
    if(c<b[i])&K:b[i]=c;v[i]=j,(X[j/2],A)
l-=2
s=P[:1]
while l/2:l,p=v[l];s+=(p,)
plot(*zip(*s))
show()

언 골프

from pylab import*
P = input()
Xn,Yn = zip(*P)
X = set(Xn+(3,-3))
f = lambda x:sin(pi*x)+sin(3*pi*x)/2
ylb = {x: max([-9]+[p[1] for p in P if p[0] == x and p[1] < f(x)]) for x in X}
yub = {x: min([9]+[p[1] for p in P if p[0] == x and p[1] > f(x)]) for x in X}
ylb[-3] = yub[3] = ylb[3] = 0
X = sorted(X)
l = len(X)
best = zeros((l,2))
best[1:] = inf
prev = [ [0,0] for i in range(l) ]
for i in range(l): # calculate min path to X[i] lb or ub
  for ib in 0,1:
    for j in range(i): # point to come from
      for jb in 0,1:
          Y2, Y1 = (ylb, yub)[ib][X[i]], (ylb, yub)[jb][X[j]]
          dy,dx = Y2 - Y1, X[i] - X[j]
          if all([Y1 + dy/dx*(x - X[j]) < yub[x] and Y1 + dy/dx*(x - X[j]) > ylb[x] for x in X[j+1:i]]):
             c = best[j][jb] + (dy**2+dx**2)**.5
             if c < best[i][ib]:
                 best[i][ib] = c
                 prev[i][ib] = j, jb, (X[j], Y1)
j, jb = l-1,0
pts = [(3,0)]
while j:
    j, jb, p = prev[j][jb]
    pts += [p]
plot(*zip(*pts))
scatter(Xn,Yn)
show()

PyLab은 더 현명한 선택이었습니다 :)
Ell
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.