Căng thẳng trên đồ thị, Phần I: Chuỗi lượn sóng


21

Hãy vẽ một hàm f (x) = sin (πx) + 0,5 sin (3πx) trên miền [-3,3] . Chúng ta có thể giải thích điều này như một chuỗi lỏng lẻo nằm trên một bảng. Bây giờ, hãy lái n đinh vào bảng ở các vị trí (x 1 , y 1 ) đến (x n , y n ) , trong đó x i ∈ (-3,3)y i [-1,1] . Hãy tưởng tượng rằng có hai khoen ở cuối chuỗi, đó là tại các vị trí (-3,0)(3,0). Bây giờ chúng ta có thể lấy các đầu của chuỗi và kéo qua các lỗ nhỏ cho đến khi chuỗi được căng. Điều này sẽ biến dạng đồ thị của chúng ta thành một hàm tuyến tính piecewise.

Một số hình ảnh có thể giúp đỡ. Lấy 8 cái đinh ở (-2,8, -0,7), (-2,5, -0,9), (-1,2, .2), (-0,5, .8), (0,5, .4), (1.2, -0.9), (1.5, -0.6), (1.8, -0.8) . Ba lô sau đây cho thấy quá trình được mô tả ở trên:

nhập mô tả hình ảnh ở đây

Đối với phiên bản lớn hơn: Nhấp chuột phải -> Mở trong tab mới

Và đây là một hình ảnh động của chuỗi thắt chặt nếu bạn gặp khó khăn khi hình dung nó:

nhập mô tả hình ảnh ở đây

Các thách thức

Đưa ra một danh sách "đinh" (không nhất thiết phải được sắp xếp), vẽ các móng đó và chuỗi taut nếu nó bắt đầu từ hình dạng của hàm trên f .

Bạn có thể viết chương trình hoặc hàm và nhận đầu vào thông qua STDIN, ARGV hoặc đối số hàm. Bạn có thể hiển thị kết quả trên màn hình hoặc lưu hình ảnh vào một tệp.

Nếu kết quả được rasterized, nó cần phải rộng tối thiểu 300 pixel và cao 100 pixel. Phạm vi tọa độ từ (-3, -1.1) đến (3,1.1) phải bao phủ ít nhất 75% phạm vi ngang và dọc của hình ảnh. Tỷ lệ chiều dài của xy không nhất thiết phải giống nhau. Bạn cần hiển thị móng tay (sử dụng ít nhất 3x3 pixel) và chuỗi (rộng ít nhất 1 pixel). Bạn có thể hoặc không bao gồm các trục.

Màu sắc là sự lựa chọn của bạn, nhưng bạn cần ít nhất hai màu có thể phân biệt: một cho nền và một cho móng và chuỗi (những màu này có thể có màu khác nhau).

Bạn có thể giả định rằng tất cả các móng cách xa ít nhất 10 -5 đơn vị so với f (do đó bạn không cần phải lo lắng về sự thiếu chính xác của dấu phẩy động).

Đây là mã golf, vì vậy câu trả lời ngắn nhất (tính bằng byte) sẽ thắng.

Thêm ví dụ

Dưới đây là hai ví dụ (đơn giản hơn):

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

nhập mô tả hình ảnh ở đây

(Chuỗi trùng với x -axis.)

{{-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}}

nhập mô tả hình ảnh ở đây

Muốn thử thách khác?

Đây là Phần II!


Chúng ta có thể cho rằng móng được sắp xếp từ trái sang phải?
Ell

@Ell Ah, bắt tốt. Vì tôi đã không chỉ định nó để bắt đầu, không. Tôi sẽ làm rõ điều đó.
Martin Ender

Câu trả lời:


8

Con trăn + Pycairo, 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()

Thí dụ

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

ví dụ 1

Làm thế nào nó hoạt động

Giả sử chúng ta biết rằng chuỗi taut đi qua hai điểm AB (chúng ta luôn có thể bắt đầu bằng
A = (-3, 0)B = (3, 0) .) Khi chúng ta kéo chuỗi, nó "muốn" lấy con đường ngắn nhất có thể giữa AB , đó là, lý tưởng nhất là đoạn AB . Tuy nhiên, nếu có bất kỳ đinh nào trong khu vực giới hạn bởi hàm ( sin πx + ... ) và AB , thì ít nhất một trong số chúng phải chặn chuỗi. Cụ thể, (các) móng cách xa AB trong khu vực nói trên phải chặn chuỗi. Do đó, nếu C là móng này, chúng ta biết rằng chuỗi taut phải đi quaC , ngoài AB . Bây giờ chúng ta có thể lặp lại quá trình cho các phân đoạn ACCB , và tiếp tục theo cách này cho đến khi cuối cùng không còn móng tay can thiệp nào nữa. Hình 1

Đây là một thuật toán phân chia và chinh phục nhị phân với quét tuyến tính trong mỗi bước, do đó, nó có độ phức tạp trong trường hợp tốt nhất là O (n log n) và độ phức tạp trong trường hợp xấu nhất của O (n 2 ) .


Nó lỗi nếu danh sách các điểm trống. Nhưng khác hơn là của tôi rõ ràng là vô vọng!
frageum

@feersum Bắt tốt. Đã sửa.
Ell

3

Python + pylab, 576 byte

Thuật toán:

Tôi đã giải thích vấn đề là tìm đường đi ngắn nhất từ (-3, 0) đến (3, 0) sao cho một đoạn thẳng đứng nối một điểm trên đường dẫn đến một điểm trên f (x) không bao giờ đi qua một cái đinh.

Tại mỗi x nơi tồn tại ít nhất một móng, tìm giới hạn trên nhỏ nhất và giới hạn dưới lớn nhất được cung cấp bởi các móng tại x đó . Coi các điểm được cho bởi các giới hạn này cộng với điểm bắt đầu và điểm kết thúc là các đỉnh trên biểu đồ. Thêm một cạnh có trọng số được cho bởi khoảng cách Euclide giữa hai đỉnh nếu đoạn đường giữa chúng nằm trong giới hạn trên và dưới cho mỗi tọa độ x xen kẽ. Tìm con đường ngắn nhất trên biểu đồ này.

Ví dụ với 27 điểm ngẫu nhiên:

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

ví dụ khập khiễng

Chơi gôn

Những gì xuất hiện dưới dạng một số khoảng cách thụt vào trong for j in R(i&~1)vòng lặp thực sự phải là một tab.

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

Ung dung

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 chắc chắn là một lựa chọn thông minh hơn :)
Ell
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.