图上的张力,第一部分:波浪线


21

让我们在域[ -3,3 ]上绘制函数f(x)= sin(πx)+ 0.5 sin(3πx)。我们可以将其解释为躺在板上的松散字符串。现在,让我们驱动Ñ指甲到板在位置(X 1,Y 1(X Ñ,Y Ñ,其中X ∈(-3,3)ÿ ∈[-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), (1.5,-0.6),(1.8,-0.8)。以下三个图显示了上述过程:

在此处输入图片说明

对于较大的版本:右键单击->在新选项卡中打开

这是一个字符串收紧的动画,如果您在可视化上有一些困难的话:

在此处输入图片说明

挑战

给定“指甲”列表(不一定要排序),如果它们从上述函数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}}

在此处输入图片说明

想要另一个挑战?

这是第二部分!


我们可以假设指甲是从左到右排序的吗?
2014年

@Ell啊,很好。由于我没有指定它开始,所以不。我会澄清这一点。
Martin Ender 2014年

Answers:


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是钉子,我们知道拉紧的弦必须穿过Ç,除。现在,我们可以对ACCB段重复此过程,并以此方式继续进行,直到最终不再有任何其他问题。 图1

这是一种在每个步骤中进行线性扫描的二进制分治算法,因此它的最佳情况复杂度为O(n log n),最坏情况复杂度为O(n 2


如果点列表为空,则会出错。但是除了那个矿山,显然是没有希望的!
feersum 2014年

@feersum好收获。固定。
2014年

3

Python + 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), 

me脚的例子

打高尔夫球

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肯定是更明智的选择:)
埃尔
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.