그래프의 장력, 파트 II : 고무 밴드


13

이것은 "풀링 기능 긴장"에 대한 두 가지 도전 과제 중 두 번째입니다. 여기 조금 더 간단한 Part I이 있습니다.

하자 구동 m의 위치에서 기판에 손톱 (X 1 , Y 1 )(X의 M , Y, m ) . 고무 밴드를 첫 번째와 마지막에 묶고 다른 손톱 주위로 펴서 밴드가 모든 손톱을 순서대로 가로지 릅니다. 고무 밴드는 이제 2D 공간에서 부분 선형 매개 변수화 된 함수 (x (t), y (t)) 를 설명합니다.

이제 다른 n 손톱을 (x 1 , y 1 ) ~ (x n , y n ) 위치로 보드에 넣습니다 . 우리는 이제 원래 모두 제거하면 m에제외 (고무의 단부가 묶여있다) 첫 번째와 마지막 하나는 새로운 손톱 주위 팽팽 누워 함수 선형 다른 절편을 수득 될 때까지 고무 밴드가 단축된다.

일례로서, 수행 m = 12 개 의 위치에서 최초의 손톱 (2, -1) (0, 0), (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) . 다음 3 가지 플롯은 위에서 설명한 프로세스를 보여줍니다.

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

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

다음은 고무 밴드를 조이는 데 어려움이있는 경우의 조임 애니메이션입니다.

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

도전

두 개의 "손톱"목록이 주어지면 첫 번째 목록의 모든 못을 가로 지르는 모양에서 시작하는 두 번째 목록 주위에 팽팽한 고무 밴드를 그립니다.

프로그램이나 함수를 작성하고 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하지 :

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

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

이 예제를 제공 한 Ell에게 감사합니다.


@laurencevs 문자열 하나는 단일 값으로, 기능과 손톱을 처리하는 명확한 방향이 있기 때문에 일을 상당히 단순화합니다. 이것은 루프와 지그재그를 포함 할 수 있으며 함수의 형태는 상당히 다르고 (그리고 가변적) 솔루션이 상당히 달라야 함을 의미합니다.
Martin Ender

이것 의 결과는 무엇입니까 ?
Ell

@Ell Ah, 아주 좋은 테스트 케이스. 일관성을 유지하려면 b 이어야 하지만 실제로 그것에 대한 질문을 명확히해야합니다. 곧 그렇게 할 것입니다. 감사!
Martin Ender

답변:


11

파이썬 + 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

참고 :

  • 손톱이 모양에 엄격하게 영향을 미치지 않더라도 밴드가 손톱에 접하는 즉시 계산을 시작합니다. 그림과 달리 손톱은 치수가 없습니다. 밴드가 못에 접선이되면 따라서, 우리는 못 우리가 어디를 추적 유지해야 --- 혼자의 위치에 의해 주파수 대역의 어느 쪽 말할 수 없다 사용 밴드를 기준으로합니다.

  • 우리는 손톱을 0의 값으로 유지합니다. 즉, 손톱을 즉시 제거하는 대신 밴드를 붙 잡지 않은 손톱을 유지합니다. 우리가 그렇게한다면, 환영받지 못하는 연쇄 반응을 일으킬 수 있으며, 각 단계의 효과를 국지적으로 유지하려고합니다. 대신, 값이 0 인 손톱은 더 큰 공정의 일부로 제거 할 수있는 것으로 간주됩니다.

이제 각 단계에서 어떤 일이 발생하는지 설명 할 수 있습니다.

  • 밴드의 현재 경로에서 제거 할 못을 선택합니다. 손톱은 첫 번째 손톱 세트의 일부이거나 (끝 점용으로 저장 됨) 값이 0 인 경우 제거 할 수 있습니다.

  • 인접한 두 손톱이 고정 된 척하면서, 선택된 손톱이 제거되면 밴드가 통과 할 두 번째 세트 또는 엔드 포인트 쌍에서 어떤 손톱을 찾아냅니다 (우리는 손톱의 나머지 부분을 귀찮게하지 않습니다) 첫 번째 세트는 결국 모두 제거 될 것입니다.) 우리는 Part I 솔루션 과 유사한 방식으로 수행 합니다 . 이 모든 손톱은 밴드의 같은쪽에 있으므로 측면에 따라 1 또는 -1 의 값을 모두 할당합니다 .

  • 인접한 두 손톱의 값을 업데이트하여 밴드의 경로 변경을 반영합니다 (가장 까다로운 부분).

더 이상 손톱을 제거 할 때까지이 과정을 반복합니다.

그림 3

그리고 손톱 주위에 여러 번 감싸는 밴드를 보여주는 더 복잡한 예가 있습니다.

그림 4


놀랄 만한! 한가지 : 그래픽이 래스터 화되었거나 벡터 그래픽입니까? 전자의 경우, "x와 y의 길이 스케일이 같아야합니다." 또한 설명에 사용하는 모든 그래픽을 만드는 데 사용하고있는 것입니다. matplotlib도?
Martin Ender

감사! 하기 matplotlib의하자가 내가 벡터 그래픽과 함께 갈거야 날에 나를 음모를 확장 :)입니다 때문에 잘못 ... 삽화를 위해 나는 주로 사용 되는 GeoGebra를 . 약간 기발하지만 작업이 완료됩니다.
Ell

예, 임의로 크기를 조정할 수 있다면 괜찮습니다. 링크 주셔서 감사합니다, 나는 그것을 확인합니다!
Martin Ender

4

자바-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
사용자 이름은이 문제에 적합합니다.
phosgene

엘은 내가 생각하지 않은 흥미로운 최첨단 사례를 제공했다. 사양을 명확히하고 해당 예를 추가했습니다. 이 예제에서 코드는 어떻게 수행됩니까? 게시물 후에이 내용이 명확 해졌으므로 업데이트 된 사양을 준수하지 않는 경우 코드를 수정해야 할 의무는 없지만 알려 드리겠습니다.
Martin Ender

문제를 해결하기 위해 변경 사항을 소개했지만 내 프로그램의 버그가 나타났습니다 (마지막 예제를 입력하려고하면 한 줄만 표시됨). 나는 그것을 고치려고 노력할 것이다.
스트레칭 미치 광
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.