Grafikte Gerilim, Bölüm II: Bir Lastik Bant


13

Bu "fonksiyonları gergin çekmek" ile ilgili iki zorluğun ikincisidir. İşte biraz daha basit Bölüm I olduğunu .

Hadi sürücü m pozisyonlarda bir tahta içine çivi (x 1 , y 1 ) için (x m , y m ) . Bunların ilkine ve sonuna bir lastik bant bağlayın ve diğer tırnakların etrafında gerdirin, böylece bant tüm tırnakları sırayla geçirir. Lastik bandın artık 2B alanda parçalı doğrusal parametreleştirilmiş bir işlevi (x (t), y (t)) açıkladığını unutmayın .

Şimdi tahtaya (x 1 , y 1 ) ila (x n , y n ) konumlarında başka bir n çivi sürün . Şimdi ilk ve sonuncusu (kauçuğun uçları bağlı) hariç tüm orijinal m tırnaklarını çıkarırsak , lastik bant yeni tırnakların etrafında gerginleşene kadar kısalır ve başka bir parçalı doğrusal işlev verir.

Bir örnek olarak, almak , m = 12 pozisyonlarda başlangıç çivi (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) ve n = (1, 1), (3, 1), (4, 4), (1, 3), (2, 2), (5, -1), (5, 0 konumlarında 10 çivi daha ), (6, 2), (7, 1), (6, 0) . Aşağıdaki üç grafik, yukarıda açıklanan işlemi göstermektedir:

resim açıklamasını buraya girin

Daha büyük sürüm için: Sağ tıklayın -> Yeni sekmede aç

Ve burada görselleştirmekte zorluk çekiyorsanız lastik bant sıkma animasyonudur:

resim açıklamasını buraya girin

Meydan okuma

İki "çivi" listesi verildiğinde, ilk listedeki tüm çivilerden geçen şekilden başlıyorsa gergin lastik bandı ikinci listenin etrafına çizin.

Bir program veya işlev yazabilir ve STDIN, ARGV veya işlev bağımsız değişkeni ile girdi alabilirsiniz. Sonucu ekranda görüntüleyebilir veya görüntüyü bir dosyaya kaydedebilirsiniz.

Sonuç rasterleştirilirse, her iki tarafta da en az 300 piksel olması gerekir. Son lastik bant ve çiviler görüntünün yatay ve dikey boyutunun en az% 75'ini kapsamalıdır. X ve y uzunluk ölçekleri aynı olmak zorundadır. Çivileri ikinci sette (en az 3x3 piksel kullanarak) ve dizede (en az 1 piksel genişliğinde) göstermeniz gerekir. Eksenleri dahil edebilir veya eklemeyebilirsiniz.

Renkler seçiminizdir, ancak en az iki ayırt edilebilir renge ihtiyacınız vardır: biri arka plan için, diğeri tırnaklar ve dize için (bunlar farklı renklere sahip olabilir).

İkinci listedeki tüm çivilerin lastik bandın başlangıç ​​şeklinden en az 10-5 birim uzakta olduğunu varsayabilirsiniz (böylece kayan nokta yanlışlığı konusunda endişelenmenize gerek yoktur).

Bu kod golf, yani en kısa cevap (bayt cinsinden) kazanır.

Daha fazla örnek

İşte iki örnek daha:

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

resim açıklamasını buraya girin

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

resim açıklamasını buraya girin

Ve burada kalan ilk çivilerin ikisinin önemini gösteren bir örnek var. Sonucu olmalıdır b ve değil bir :

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

resim açıklamasını buraya girin

Bu örneği verdiği için Ell'e teşekkürler.


@ laurencevs Dize tek değerli, işleri önemli ölçüde basitleştiriyor, çünkü fonksiyon ve tırnakları işlemek için açık bir yön var. Bu döngüler ve zikzaklar içerebilir ve fonksiyonun şekli oldukça farklıdır (ve değişkendir), bu da çözümlerin oldukça farklı olması gerektiği anlamına gelir.
Martin Ender

Çıktısı nedir bu ?
Ell

@ Ah, çok güzel bir test çantası. Ben tutarlılık için, b olmalı , ama gerçekten bu konuda soruyu netleştirmek gerekir. Yakında yapacağım. Teşekkürler!
Martin Ender

Yanıtlar:


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'den iki nokta listesi okur.

Misal

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

Şekil 1

Nasıl çalışır

Çözümün anahtarı küçük, artımlı adımlarla çalışmaktır. Tüm tırnakları bir kerede çıkardığımızda ne olacağını anlamaya çalışmak yerine, sadece tek bir çiviyi çıkarmanın doğrudan etkilerine odaklanıyoruz. Daha sonra tırnakları keyfi bir sırayla çıkarabiliriz.

Kademeli olarak çalışmak, lastik bandın ara durumunu takip etmemiz gerektiği anlamına gelir. İşte zor kısmı: sadece bandın hangi çivilerin içinden geçtiğini takip etmek yeterli değildir. Çivileri çıkarma işlemi sırasında bant yaralanabilir ve daha sonra bir çivinin etrafına sarılabilir. Bu nedenle, bant bir çiviyle etkileşime girdiğinde, kaç kez ve hangi yönde sarıldığını takip etmeliyiz. Bunu, bant boyunca her çiviye bir değer atayarak yapıyoruz:

şekil 2

Bunu not et:

  • Çivi şeklini kesinlikle etkilemese de, bant çiviye teğet olur olmaz saymaya başlarız. Resimden farklı olarak tırnaklarımızın boyutsuz olduğunu hatırlayın. Bant çivi teğet olur nedenle, biz tırnak biz nerede izlemek gerekir --- yalnız konumunu olarak açıktır bandın hangi tarafı söyleyemem kullanılan banda göreceli olarak.

  • Tırnakları sıfır değerinde tutarız, yani eskiden çivileri çıkarırız, ancak bandı hemen kaldırmak yerine artık tutmazız. Yapsaydık, her adımın etkilerini yerel tutmaya çalışırken, istenmeyen bir zincirleme reaksiyonu tetikleyebilir. Bunun yerine, sıfır değerine sahip tırnaklar, daha büyük işlemin bir parçası olarak çıkarılmaya uygun olarak kabul edilir.

Şimdi her adımda neler olduğunu açıklayabiliriz:

  • Grubun mevcut yolundan kaldırılacak bir çivi seçiyoruz. Bir çivi, ilk çivi setinin bir parçasıysa (uç noktalar için kaydedin) veya değeri sıfırsa, çıkarılmaya uygundur.

  • İki komşu çivinin sabit olduğunu iddia ederek, ikinci çividen hangi çivilerin veya seçilen çivi çıkarıldıktan sonra bandın geçeceği uç nokta çiftinin olduğunu anlıyoruz (diğer çivi ile ilk set, çünkü sonunda hepsi kaldırılacak.) Bunu Bölüm I'in çözümüne benzer şekilde yapıyoruz . Tüm bu çiviler bandın aynı tarafındadır, bu nedenle hepsine tarafa bağlı olarak 1 veya -1 değeri atarız .

  • Grubun yolundaki değişiklikleri yansıtmak için iki komşu çivinin değerini güncelliyoruz (kolayca en zor kısım!)

Kaldırılacak çivi kalmayıncaya kadar bu işlem tekrarlanır:

Figür 3

Ve burada çivinin etrafına birkaç kez sarılan bandı gösteren daha karmaşık bir örnek:

Şekil 4


İnanılmaz! Sadece bir şey: bu grafikler rasterleştirildi mi yoksa vektör grafikleri mi? Önceki durumda, sizi "x ve y'nin uzunluk ölçekleri aynı olmak zorundadır." Ayrıca, açıklamalarınızda kullandığınız tüm grafikleri oluşturmak için ne kullanıyorsunuz? matplotlib de mi?
Martin Ender

Teşekkürler! Hata ... Matplotlib hatayı anında ölçeklendireyim. Vektör grafikleri ile gideceğim :) Çizimler için çoğunlukla GeoGebra kullanıyorum . Biraz tuhaf ama işi hallediyor.
Ell

Evet, eğer keyfi olarak yeniden boyutlandırabilirseniz, sorun değil. Bağlantı için teşekkürler, kontrol edeceğim!
Martin Ender

4

Java - 1262 bayt

Bu muhtemelen olabildiğince golf değil biliyorum.

Ancak, hiç kimse tabağa çıkıp bu soruyu cevaplıyor gibi görünmüyor, bu yüzden yapacağım.

İlk olarak, bir nokta sınıfı olan "T" sınıfı - 57 bayt

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

Ve ana sınıf - 1205 bayt

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);}}}}

Misal:

resim açıklamasını buraya girin

Çalıştırmak için, bir nokta listesi ve bir çivi dizisiyle "d" işlevini çağırın (evet, biliyorum, garip). Bu ne yapar:

  • çizgileri temsil eden bir nokta listesi oluşturur - yani, çizgiler arasındaki tüm noktalar.
  • her nokta etrafındaki iki noktanın ortalaması olacak şekilde bir algoritmayı tekrar tekrar bu noktalara tekrarlar.
  • Noktalar artık çok fazla hareket etmediği zaman, dokundukları tırnakların arasına çiziyorum.

Piksellerdeki eksenlerin iyi olup olmadığından emin değilim. Her zaman alanın% 75'inden fazlasını kaplar, sadece gerçekten çok küçük olabilir.

Burada ne yaptığımı göstermek için güzel bir animasyon:

resim açıklamasını buraya girin

Sonunda, noktaların zar zor hareket ettiği bu olur. Bu, hangi tırnaklara dokunduğunu gördüğümde:

resim açıklamasını buraya girin

İşte ungolfed, animasyon kodu:

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
Kullanıcı adınız bu soruna çok uygundur.
fosgen

Ell hakkında düşünmediğim ilginç bir son dava sağladı. Spesifikasyonu açıklığa kavuştum ve bu örneği ekledim. Kodunuz bu örnekte nasıl çalışır? Bu, gönderinizin ardından açıklığa kavuşturulduğundan, güncellenmiş spesifikasyona uymuyorsa, kodunuzu düzeltmek zorunda değilsiniz, ancak size bildireceğimi düşündüm.
Martin Ender

Düzeltmek için bazı değişiklikler yaptım, ancak programımda bir hata ortaya çıkardı (son örneği girmeye çalışırsanız, sadece bir satır gösterir). Düzeltmeye çalışacağım.
Stretch Maniac
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.