图上的张力,第二部分:橡皮筋


13

这是有关“拉紧功能”的两个挑战中的第二个挑战。这是稍微简单的第一部分

让我们在位置(x 1,y 1(x m,y m上将m钉子钉入板中。将橡皮筋绑在第一个和最后一个上,并在其他钉子周围伸展,使橡皮筋依次穿过所有钉子。请注意,橡皮筋现在描述了二维空间中的分段线性参数化函数(x(t),y(t))

现在在位置(x 1,y 1(x n,y n处将另外n个钉子钉入板中。如果现在我们除去所有第一个和最后一个钉子(橡胶的末端绑在这两个钉子上)之外的所有m个钉子,橡皮筋将缩短,直到其紧紧缠绕在新钉子上,从而产生另一个分段线性函数。

作为一个例子,取M = 12条初始钉在位置(0,0),(2,-1),(3/2,4/3),(7/2,1/3),(11/2, 16/3),(1、16 / 3),(0、1),(7,-2),(3、4),(8、1),(3,-1),(11、0),并且n = 10个另外的钉子在位置(1、1),(3、1),(4、4),(1、3),(2、2),(5,-1),(5、0 ),(6,2),(7,1),(6,0)。以下三个图显示了上述过程:

在此处输入图片说明

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

如果您在可视化时遇到一些困难,这是橡皮筋收紧的动画:

在此处输入图片说明

挑战

给定两个“钉子”列表,如果绷紧的橡皮筋从横穿第一个列表中所有钉子的形状开始,则在第二个列表周围绘制绷紧的橡皮筋。

您可以编写程序或函数,并通过STDIN,ARGV或函数参数接受输入。您可以在屏幕上显示结果,也可以将图像保存到文件中。

如果将结果栅格化,则每侧至少需要300像素。最终的橡皮筋和钉子必须至少覆盖图像水平和垂直方向的75%。xy的长度比例必须相同。您需要在第二组(至少使用3x3像素)中显示钉子,并在字符串中显示至少1像素宽的指甲。您可以包括也可以不包括轴。

颜色是您的选择,但是您至少需要两种可区分的颜色:一种用于背景,一种用于指甲和细绳(尽管这些颜色可能具有不同的颜色)。

您可能会假设第二个列表中的所有钉子都距离橡皮筋的初始形状至少10 -5个单位(这样就不必担心浮点精度了)。

这是代码高尔夫球,因此最短的答案(以字节为单位)获胜。

更多例子

这是另外两个示例:

{{1, 1}, {3, 3}, {2, 4}, {1, 3}, {4, 0}, {3, -1}, {2, 0}, {4, 2}}
{{2, 1}, {3, 2}, {1, 2}, {4, 1}}

在此处输入图片说明

{{1, 1}, {3, 1}, {3, 3}, {1, 3}, {1, 5}, {3, 5}, {-1, 3}, {-1, 0}, {3, 4}, {5, 1}, {5, -1}, {7, -1}, {3, 7}, {7, 5}}
{{0, 0}, {0, 2}, {0, 4}, {0, 6}, {2, 0}, {2, 2}, {2, 4}, {2, 6}, {4, 0}, {4, 2}, {4, 4}, {4, 6}, {6, 0}, {6, 2}, {6, 4}, {6, 6}}

在此处输入图片说明

这是一个示例,显示了剩下的两个初始钉子的重要性。结果应该是b不是 a

{{0, 0}, {0, 1}, {-1, 1}, {-1, -1}, {1, -1}, {1, 0}}
{{-0.5, 0.5}}

在此处输入图片说明

感谢Ell提供此示例。


@laurencevs字符串one是单值的,这大大简化了事情,因为处理功能和钉子的方向很明显。这个可能包含循环和锯齿形,并且函数的形式有很大不同(和可变),这意味着解决方案应该有很大不同。
Martin Ender 2014年

什么是输出这个
2014年

@Ell啊,非常好的测试用例。我想保持一致,应该为b,但我确实需要澄清有关该问题。会尽快做到的。谢谢!
马丁·恩德2014年

Answers:


11

Python + matplotlib,688

from pylab import*
C=cross
P,M=eval("map(array,input()),"*2)
P,N=[[P[0]]+L+[P[-1]]for L in P,M]
W=[.5]*len(P)
def T(a,c,b):
 I=[(H[0]**2,id(n),n)for n in N for H in[(C(n-a,b-a),C(n-b,c-b),C(n-c,a-c))]if(min(H)*max(H)>=0)*H[1]*H[2]]
 if I:d=max(I)[2];A=T(a,c,d);B=T(d,c,b);return[A[0]+[d]+B[0],A[1]+[sign(C(c-a,b-c))]+B[1]]
 return[[]]*2
try:
 while 1:
    i=[w%1*2or w==0for w in W[2:-2]].index(1);u,a,c,b,v=P[i:i+5];P[i+2:i+3],W[i+2:i+3]=t,_=T(a,c,b);t=[a]+t+[b]
    for p,q,j,k in(u,a,1,i+1),(v,b,-2,i+len(t)):x=C(q-p,c-q);y=C(q-p,t[j]-q);z=C(c-q,t[j]-q);d=sign(j*z);W[k]+=(x*y<=0)*(x*z<0 or y*z>0)*(x!=0 or d*W[k]<=0)*(y!=0 or d*W[k]>=0)*d
except:plot(*zip(*P))
if M:scatter(*zip(*M))
show()

从STDIN读取两个点列表。

[(0, 0), (2, -1), (3.0/2, 4.0/3), (7.0/2, 1.0/3), (11.0/2, 16.0/3), (1, 16.0/3), (0, 1), (7, -2), (3, 4), (8, 1), (3, -1), (11, 0)]
[(1, 1), (3, 1), (4, 4), (1, 3), (2, 2), (5, -1), (5, 0), (6, 2), (7, 1), (6, 0)]

图1

怎么运行的

解决方案的关键是逐步进行小规模工作。与其试图弄清楚一下一次去除所有钉子会发生什么,我们不如专注于仅去除单个钉子的直接效果。然后,我们可以按任意顺序一个个地钉子。

逐步工作意味着我们必须跟踪橡皮筋的中间状态。这是棘手的部分:仅跟踪乐队穿过的指甲是不够的。在去除指甲的过程中,绑带可能会缠绕并随后在指甲周围解开。因此,当带子与钉子相互作用时,我们必须跟踪其环绕钉子的次数和方向。为此,我们为带子上的每个钉子分配了一个值,如下所示:

图2

注意:

  • 当带子与指甲相切时,我们就开始计数,即使指甲并没有严格影响其形状。回想一下,与插图不同,我们的指甲是无量纲的。因此,如果带相切到指甲上,我们不能告诉指甲上带的一侧由它的位置独自---我们必须跟踪它使用的是相对于带。

  • 我们将钉子的值保持为零,即以前但不再握住该带子的钉子,而不是立即将其除去。如果这样做,可能会引发不受欢迎的连锁反应,而我们试图使每个步骤的影响保持局部化。取而代之的是,值为零的钉子有资格作为较大处理的一部分进行去除。

现在我们可以描述每个步骤发生的情况:

  • 我们选择一个钉子从乐队的当前路径中删除。如果钉子是第一组钉子的一部分(端点除外)或其值为零,则可以将其除去。

  • 假设两个相邻的钉子是固定的,我们就可以确定一旦选定的钉子被移除,乐队将穿过第二组或一对端点中的哪一个钉子(我们不会再打扰其余的钉子了)第一组,因为它们最终都将被删除。)我们以类似于第一部分的解决方案的方式进行操作。所有这些钉子都在乐队的同一侧,因此我们将它们的值分别指定为1-1,具体取决于侧面。

  • 我们更新两个相邻钉子的值,以反映乐队路径的变化(最棘手的部分!)

重复此过程,直到没有指甲要去除为止:

图3

这是一个更复杂的示例,说明了绑带多次缠绕在指甲上:

图4


惊人!只是一件事:那些图形被栅格化了还是矢量图形?在前一种情况下,我必须指出“ x和y的长度比例必须相同”。另外,您将使用什么来创建您在解释中使用的所有图形。matplotlib也是吗?
马丁·恩德

谢谢!错...由于matplotlib让我即时缩放绘图,所以我将使用矢量图形:)对于插图,我主要使用GeoGebra。这有点古怪,但可以完成工作。
2014年

是的,如果您可以任意调整其大小,那很好。感谢您的链接,我会检查一下!
Martin Ender 2014年

4

Java-1262字节

我知道这可能不会打得那么多。

但是,似乎没有其他人愿意站出来回答这个问题,所以我会的。

首先,类“ T”-这是一个点类-57字节

class T{double x,y;public T(double a,double b){x=a;y=b;}}

和主类-1205字节

import java.awt.Color;import java.awt.Graphics;import java.util.*;import javax.swing.*;class Q extends JPanel{void d(List<T>a,T[]z){JFrame t=new JFrame();int m=0;int g=0;for(T v:a){if(v.x>m)m=(int)v.x;if(v.y>g)g=(int)v.y;}for(T v:z){if(v.x>m)m=(int)v.x;if(v.y>g)g=(int)v.y;}t.setSize(m+20,g+20);t.setVisible(true);t.getContentPane().add(this);double r=9;while(r>1){r=0;for(int i=0;i<a.size()-1;i+=2){T p1=a.get(i),p2=new T((p1.x+a.get(i+1).x)/2,(p1.y+a.get(i+1).y)/2);a.add(i+1,p2);if(y(p1,p2)>r){r=y(p1,p2);}}}double w=15;List<T>q=new ArrayList<T>();while(w>3.7){w=0;q.clear();for(int e=0;e<a.size()-2;e++){T p1=a.get(e),u=a.get(e+1),p3=a.get(e+2),p2=new T((p1.x+p3.x)/2,(p1.y+p3.y)/2);w+=y(u,p2);int k=0;if(y(p1,a.get(e+1))<.5){a.remove(e);}for(T n:z){if(y(n,p2)<1){k=1;q.add(n);}}if(k==0){a.set(e+1,p2);}}}q.add(a.get(a.size()-1));q.add(1,a.get(0));p=z;o=q.toArray(new T[q.size()]);repaint();}T[]p;T[]o;double y(T a,T b){return Math.sqrt(Math.pow(b.x-a.x,2)+Math.pow(b.y-a.y,2));}public void paintComponent(Graphics g){if(o!=null){for(int i=0;i<o.length-1;i++){g.drawLine((int)o[i].x,(int)o[i].y,(int)o[i+1].x,(int)o[i+1].y);}g.setColor(Color.blue);for(T i:p){g.fillOval((int)i.x-3,(int)i.y-3,6,6);}}}}

例:

在此处输入图片说明

要运行,请使用点列表和钉子阵列调用“ d”函数(是的,我知道,很奇怪)。它能做什么:

  • 创建代表线的点的列表-即线之间的所有点。
  • 重复对这些点重复算法,以便每个点是其周围两个点的平均值。
  • 当这些点似乎不再移动时,我在它们碰到的任何指甲之间画图。

我不确定像素轴是否可以。它总是会占据超过75%的空间,它可能真的很小。

这是一个很好的动画来演示我在这里做什么:

在此处输入图片说明

最终,点几乎没有移动。这是当我看到它触到的指甲时:

在此处输入图片说明

这是未播放的动画代码:

import java.awt.Color;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class Q extends JPanel{
    List<Point>points=new ArrayList<Point>();
    List<Point>n=new ArrayList<Point>();
    public Q() throws InterruptedException{
        double[][]rawPoints={{0, 0}, {2, -1}, {3/2, 4/3}, {7/2, 1/3}, {11/2, 16/3}, {1, 16/3}, {0, 1}, {7, -2}, {3, 4}, {8, 1}, {3, -1}, {11, 0}};
        double[][]rawNails={{1, 1}, {3, 1}, {4, 4}, {1, 3}, {2, 2}, {5, -1}, {5, 0}, {6, 2}, {7, 1}, {6, 0}};
        List<Point>p=new ArrayList<Point>(),nails = new ArrayList<Point>();
        double factor = 50;
        for(double[]rawP:rawPoints){p.add(new Point(rawP[0]*factor+100,rawP[1]*factor+100));}
        for(double[]rawN:rawNails){nails.add(new Point(rawN[0]*factor+100,rawN[1]*factor+100));}
        n=nails;
        JFrame frame=new JFrame();
        frame.setSize(700,500);
        frame.setVisible(true);
        frame.getContentPane().add(this);
        d(p,nails);
    }
    public static void main(String[]a) throws InterruptedException{
        new Q();
    }
    void d(List<Point>a,List<Point>nails) throws InterruptedException{
        //add midpoint every iteration until length of 1 is achieved
        //begin algorithm
        //stop points that are within a small amount of a nail
        double distance=20;
        while(distance>1){
            distance=0;
            for (int i=0;i<a.size()-1;i+=2){
                double fir=a.get(i).x;
                double sec=a.get(i).y;
                double c=(fir+a.get(i+1).x)/2;
                double d=(sec+a.get(i+1).y)/2;
                a.add(i+1,new Point(c,d));
                double dist=distBP(new Point(fir,sec),new Point(c,d));
                if(dist>distance){distance=dist;}
            }
        }
        for(Point p:a){a.set(a.indexOf(p), new Point(p.x,p.y));}
        //algorithm starts here:
        setEqual(a);
        repaint();
        invalidate();
        System.out.println(a);
        int count=0;
        while(true){
            count++;
            for(int index=0;index<a.size()-2;index++){
                double x2=(a.get(index).x+a.get(index+2).x)/2;
                double y2=(a.get(index).y+a.get(index+2).y)/2;
                int pointStable=0;
                if(distBP(a.get(index),a.get(index+1))<.5){a.remove(index);}
                for(Point n:nails){
                    if(distBP(n,new Point(x2,y2))<1){pointStable=1;}
                }
                if(pointStable==0){a.set(index+1, new Point(x2,y2));}
            }
            if(count%10==0){
            setEqual(a);
            invalidate();
            repaint();
            Thread.sleep(5);
            }
        }
        //System.out.println(a);
    }
    void setEqual(List<Point>a){
        points = new ArrayList<Point>();
        for(Point p:a){points.add(p);}
    }
    double distBP(Point a,Point b){
        return Math.sqrt(Math.pow(b.x-a.x, 2)+Math.pow(b.y-a.y, 2));
    }
    @Override
    public void paintComponent(Graphics g){
        g.setColor(Color.white);
        g.fillRect(0,0,getWidth(),getHeight());
        g.setColor(Color.black);
        for(Point p:points){
            g.drawRect((int)p.x, (int)p.y, 1, 1);
        }
        for(Point nail:n){
            g.drawOval((int)nail.x-2, (int)nail.y-2, 4, 4);
        }
    }
}

7
您的用户名非常适合此问题。
光气2014年

Ell提供了一个我没想到的有趣案例。我已经澄清了规范并添加了该示例。您的代码如何在该示例上执行?由于这是在您发布后澄清的,因此,如果代码不符合更新的规范,则您没有义务修复代码,但是我想告诉您。
Martin Ender 2014年

我引入了一些修改来修复它,但是它揭示了我程序中的一个错误(如果您尝试在最后一个示例中输入它,则只显示一行)。我将尝试修复它。
2014年
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.