グラフの張力、パートII:ラバーバンド


13

これは、「関数をピンと張る」ことに関する2つの課題のうちの2番目です。これが少し単純なパートIです。

(x 1、y 1から(x m、y m)の位置でm個の釘をボードに打ち込みましょう。それらの最初と最後にゴムバンドを結び、他の釘の周りに伸びて、バンドがすべての釘を順番に横切るようにします。ラバーバンドは、2D空間で区分的線形パラメーター化関数(x(t)、y(t))を記述することに注意してください。

次に、位置(x 1、y 1から(x n、y nで、ボードに別のn個の釘を打ち込みます。最初と最後の(ゴムの端が結び付けられている)爪以外のすべての元削除すると、ゴムバンドは新しい爪の周りにぴんと張るまで短くなり、別の区分的線形関数が生成されます。

一例として、取る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)。次の3つのプロットは、上記のプロセスを示しています。

ここに画像の説明を入力してください

大きなバージョンの場合:右クリック->新しいタブで開く

また、ラバーバンドを視覚化するのが困難な場合の締め付けのアニメーションを次に示します。

ここに画像の説明を入力してください

チャレンジ

「釘」の2つのリストが与えられた場合、最初のリストのすべての釘を横断する形状から始まる場合、2番目のリストの周りにピンと張った輪ゴムをプロットします。

プログラムまたは関数を記述し、STDIN、ARGV、または関数引数を介して入力を取得できます。結果を画面に表示するか、画像をファイルに保存できます。

結果をラスタライズする場合、各辺が少なくとも300ピクセルである必要があります。最終的な輪ゴムと爪は、画像の水平および垂直範囲の少なくとも75%をカバーする必要があります。xyの長さスケールは同じでなければなりません。2番目のセット(少なくとも3x3ピクセルを使用)と文字列(少なくとも1ピクセルの幅)に爪を表示する必要があります。軸を含めても含めなくてもかまいません。

色は選択できますが、少なくとも2つの区別できる色が必要です。1つは背景用、もう1つは爪と文字列用です(ただし、色は異なる場合があります)。

2番目のリストのすべての爪は、ゴムバンドの初期形状から少なくとも10 -5ユニット離れていると仮定できます(そのため、浮動小数点の不正確さを心配する必要はありません)。

これはコードゴルフなので、最短の回答(バイト単位)が勝ちです。

その他の例

さらに2つの例を示します。

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

ここに画像の説明を入力してください

そして、ここに残っている最初の釘のうちの2つの重要性を示す1つの例があります。その結果、あるべきB及びません

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

ここに画像の説明を入力してください

この例を提供してくれたエルに感謝します。


@laurencevs文字列1は単一値であり、関数と爪を処理する明確な方向性があるため、物事をかなり単純化します。これにはループとジグザグが含まれている可能性があり、関数の形式はかなり異なります(そして変数)。つまり、解はかなり異なるはずです。
マーティンエンダー14年

出力何これは
エル14年

@Ell Ah、非常に素晴らしいテストケース。一貫性を保つために、bにする必要がありますが、それについての質問を明確にする必要があります。すぐにそれを行います。ありがとう!
マーティンエンダー14年

回答:


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からポイントの2つのリストを読み取ります。

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

使い方

ソリューションの鍵は、小さな段階的なステップで作業することです。一度にすべての爪を除去すると何が起こるかを理解しようとする代わりに、1本の爪を除去する直接的な効果に集中します。その後、任意の順序で爪を1つずつ削除できます。

漸進的に作業するということは、ラバーバンドの中間状態を追跡する必要があることを意味します。ここで注意が必要な部分があります。バンドがどの釘を貫くかを追跡するだけでは不十分です。釘を除去する過程で、バンドは傷つき、後に釘の周りに巻き戻される可能性があります。したがって、バンドが爪と相互作用するとき、それが何回、どの方向に巻き付いているかを追跡する必要があります。次のように、バンドに沿って各爪に値を割り当てることでそれを行います。

図2

ご了承ください:

  • バンドが爪に厳密に影響を与えない場合でも、バンドが爪に接するとすぐにカウントを開始します。図とは異なり、爪は無次元であることを思い出してください。バンドは、我々は爪だけではその位置でオンになっているバンドの側に伝えることができない爪に接線となった場合そのため、---我々はそれがどこのトラックおく必要があります使用バンドに対する相対することに。

  • 値0の釘、つまり、バンドをすぐに削除するのではなく、以前はバンドを保持していなかった釘を保持します。そうした場合、歓迎されない連鎖反応を引き起こす可能性がありますが、各ステップの効果をローカライズしようとしています。代わりに、値がゼロの釘は、より大きなプロセスの一部として削除の対象と見なされます。

これで、各ステップで何が起こるかを説明できます。

  • バンドの現在のパスから削除する釘を選択します。爪は、爪の最初のセットの一部である場合(エンドポイント用に保存)、または値がゼロの場合、除去の対象となります。

  • 2つの隣接する爪が固定されているふりをして、選択された爪が削除されるとバンドが2番目のセットまたはエンドポイントのペアのどの爪を通過するかを把握します(残りの爪は気にしません)最終的にすべて削除されるため、最初のセットです。)パートIのソリューションと同様の方法でそれを行います。これらのすべての爪はバンドの同じ側にあるため、側に応じて1または-1の値をすべてに割り当てます。

  • 隣接する2つの釘の値を更新して、バンドのパスの変更を反映します(最も扱いにくい部分です!)。

このプロセスは、削除する釘がなくなるまで繰り返されます。

図3

そして、爪を数回巻き付けるバンドを示すより複雑な例です:

図4


すごい!ただ一つ:グラフィックはラスタライズされているのですか、それともベクターグラフィックですか?前者の場合、「xとyの長さスケールは同じでなければなりません」を指摘する必要があります。また、説明で使用するすべてのグラフィックを作成するために何を使用していますか。matplotlibもですか?
マーティンエンダー14年

ありがとう!Err ... matplotlibではその場でプロットをスケーリングできるので、ベクターグラフィックスを使用します:)イラストでは、ほとんどGeoGebraを使用しています。少し風変わりですが、仕事は完了です。
エル14年

ええ、あなたがそれを勝手にリサイズできるなら、それは大丈夫です。リンクをありがとう、私はそれをチェックします!
マーティンエンダー14年

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」関数を呼び出します(そうですね、変です)。それが何をする:

  • 線を表す点のリスト、つまり線の間にあるすべての点を作成します。
  • これらのポイントに対してアルゴリズムを繰り返し繰り返し、各ポイントが周囲の2つのポイントの平均になるようにします。
  • ポイントがもう動いていないように見えるとき、私はそれらが触れている爪の間を描きます。

ピクセル単位の軸が適切かどうかわかりません。常にスペースの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
ユーザー名はこの問題に適しています。
ホスゲン14年

エルは、私が考えもしなかった興味深いエッジケースを提供してくれました。仕様を明確にし、その例を追加しました。あなたのコードはその例でどのように動作しますか?これは投稿後に明らかになったため、更新された仕様に準拠していない場合、コードを修正する義務はありませんが、お知らせします。
マーティンエンダー14年

それを修正するためにいくつかの変更を導入しましたが、プログラムのバグを明らかにしました(最後の例を入力しようとすると、1行しか表示されません)。私はそれを修正しようとします。
ストレッチマニア14年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.