Tensione su un grafico, parte II: un elastico


13

Questa è la seconda delle due sfide su "tirare le funzioni tese". Ecco la parte I leggermente più semplice .

Guidiamo m chiodi in una tavola in posizioni (x 1 , y 1 ) a (x m , y m ) . Lega un elastico al primo e all'ultimo di quelli e allunga attorno alle altre unghie, in modo che la banda attraversi tutte le unghie in ordine. Si noti che l'elastico ora descrive una funzione parametrica lineare a tratti (x (t), y (t)) nello spazio 2D.

Ora guida un altro n chiodi nel tabellone, nelle posizioni (x 1 , y 1 ) a (x n , y n ) . Se ora rimuoviamo tutti i chiodi m originali tranne il primo e l'ultimo (a cui sono legate le estremità della gomma), l'elastico si accorcerà fino a quando non sarà teso attorno ai nuovi chiodi, dando un'altra funzione lineare a tratti.

Ad esempio, prendi m = 12 chiodi iniziali nelle posizioni (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) e n = 10 ulteriori chiodi nelle posizioni (1, 1), (3, 1), (4, 4), (1, 3), (2, 2), (5, -1), (5, 0 ), (6, 2), (7, 1), (6, 0) . I seguenti tre grafici mostrano il processo sopra descritto:

inserisci qui la descrizione dell'immagine

Per versioni più grandi: fare clic con il pulsante destro del mouse -> Apri in una nuova scheda

Ed ecco un'animazione del serraggio dell'elastico se hai qualche difficoltà a visualizzarlo:

inserisci qui la descrizione dell'immagine

La sfida

Dati due elenchi di "chiodi", tracciare l'elastico teso attorno al secondo elenco se inizia dalla forma che attraversa tutti i chiodi del primo elenco.

È possibile scrivere un programma o una funzione e accettare input tramite STDIN, ARGV o argomento della funzione. È possibile visualizzare il risultato sullo schermo o salvare un'immagine in un file.

Se il risultato è rasterizzato, deve essere di almeno 300 pixel su ciascun lato. L'elastico finale e i chiodi devono coprire almeno il 75% dell'estensione orizzontale e verticale dell'immagine. Le scale di lunghezza x ed y devono essere uguali. Devi mostrare i chiodi nel secondo set (usando almeno 3x3 pixel) e la stringa (almeno 1 pixel di larghezza). È possibile includere o meno gli assi.

I colori sono la tua scelta, ma hai bisogno di almeno due colori distinguibili: uno per lo sfondo e uno per le unghie e il filo (quelli possono avere colori diversi però).

Puoi presumere che tutti i chiodi nel secondo elenco siano ad almeno 10-5 unità dalla forma iniziale dell'elastico (quindi non devi preoccuparti di imprecisioni in virgola mobile).

Questo è il golf del codice, quindi vince la risposta più breve (in byte).

Altri esempi

Ecco altri due esempi:

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

inserisci qui la descrizione dell'immagine

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

inserisci qui la descrizione dell'immagine

Ed ecco un esempio che mostra il significato di due chiodi iniziali rimasti. Il risultato dovrebbe essere b e non a :

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

inserisci qui la descrizione dell'immagine

Grazie a Ell per aver fornito questo esempio.


@laurencevs La stringa è a valore singolo, il che semplifica notevolmente le cose, perché esiste una direzione ovvia in cui elaborare la funzione e le unghie. Questo può contenere anelli e zigzag e la forma della funzione è considerevolmente diversa (e variabile), il che significa che le soluzioni dovrebbero essere considerevolmente diverse.
Martin Ender,

Qual è l'output di questo ?
Ell

@Ell Ah, test case molto bello. Suppongo che per coerenza, dovrebbe essere b , ma ho davvero bisogno di chiarire la domanda al riguardo. Lo farò presto. Grazie!
Martin Ender,

Risposte:


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

Legge due elenchi di punti da STDIN.

Esempio

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

Figura 1

Come funziona

La chiave della soluzione è lavorare in piccoli passi incrementali. Invece di cercare di capire cosa succede quando rimuoviamo tutte le unghie contemporaneamente, ci concentriamo sugli effetti diretti della rimozione di un solo chiodo. Possiamo quindi rimuovere i chiodi uno per uno in un ordine arbitrario.

Lavorare in modo incrementale significa che dobbiamo tenere traccia dello stato intermedio dell'elastico. Ecco la parte difficile: semplicemente tenere traccia di quali chiodi attraversano la band non è sufficiente. Durante il processo di rimozione delle unghie, la fascia può ferirsi e successivamente srotolarsi attorno a un'unghia. Pertanto, quando la band interagisce con un chiodo, dobbiamo tenere traccia di quante volte e in quale direzione si avvolge attorno ad essa. Lo facciamo assegnando un valore a ciascun chiodo lungo la band in questo modo:

figura 2

Nota che:

  • Iniziamo a contare non appena la fascia è tangente all'unghia, anche se l'unghia non influisce strettamente sulla sua forma. Ricordiamo che, a differenza dell'illustrazione, le nostre unghie non hanno dimensioni. Pertanto, se la banda diventa tangente a un chiodo, non possiamo dire da quale parte del cinturino si trova il chiodo dalla sua sola posizione --- dobbiamo tenere traccia di dove ha usato per essere relativa alla band.

  • Teniamo intorno le unghie con un valore pari a zero, ovvero le unghie che erano solite, ma non reggono più la band, invece di rimuoverle immediatamente. Se lo facessimo, potrebbe innescare una reazione a catena indesiderata, mentre stiamo cercando di mantenere localizzati gli effetti di ogni passaggio. Invece, le unghie con un valore pari a zero sono considerate idonee per la rimozione come parte del processo più ampio.

Ora possiamo descrivere cosa succede ad ogni passaggio:

  • Selezioniamo un chiodo da rimuovere dal percorso corrente della band. Un chiodo può essere rimosso se fa parte del primo set di chiodi (salvo per gli endpoint) o se il suo valore è zero.

  • Fingendo che i due chiodi vicini siano fissi, scopriamo quali chiodi dal secondo set o la coppia di punti finali che la banda attraverserà una volta rimosso l'unghia selezionata (non ci preoccupiamo del resto delle unghie dal primo set, in quanto faranno alla fine tutti essere rimossi.) Noi lo facciamo in un modo simile alla soluzione a Parte I . Tutti questi chiodi sono sullo stesso lato della band, quindi assegniamo a tutti loro un valore di 1 o -1 , a seconda del lato.

  • Aggiorniamo il valore dei due chiodi vicini per riflettere le modifiche al percorso della band (facilmente la parte più difficile!)

Questo processo viene ripetuto fino a quando non ci sono più chiodi da rimuovere:

Figura 3

Ed ecco un esempio più complicato che illustra più volte la fascia che avvolge un chiodo:

Figura 4


Sorprendente! Solo una cosa: quelle grafiche sono rasterizzate o sono grafiche vettoriali? Nel primo caso, dovrò indicarti "Le scale di lunghezza di xey devono essere le stesse". Inoltre, cosa stai usando per creare tutta quella grafica che usi nelle tue spiegazioni. anche matplotlib?
Martin Ender,

Grazie! Err ... Dal momento che matplotlib mi permetta di ridimensionare la trama al volo, vado con la grafica vettoriale :) Per le illustrazioni che uso principalmente GeoGebra . È un po 'eccentrico ma fa il lavoro.
Ell,

Sì ok, se riesci a ridimensionarlo arbitrariamente, va bene. Grazie per il link, lo controllerò!
Martin Ender,

4

Java - 1262 byte

So che probabilmente questo non è il massimo del golf.

Tuttavia, nessun altro sembra fare un passo avanti nel piatto e rispondere a questa domanda, quindi lo farò.

Innanzitutto, la classe "T" - che è una classe punto - 57 byte

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

E la classe principale - 1205 byte

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

Esempio:

inserisci qui la descrizione dell'immagine

Per eseguire, chiama la funzione "d" con un elenco di punti e una serie di chiodi (sì, lo so, strano). Cosa fa:

  • crea un elenco di punti che rappresentano le linee, ovvero tutti i punti tra le linee.
  • ripete ripetutamente un algoritmo su questi punti in modo che ciascun punto sia la media dei due punti attorno ad esso.
  • Quando i punti non sembrano più muoversi molto, disegno tra le unghie che toccano.

Non sono sicuro che gli assi in pixel siano ok. Occuperà sempre più del 75% dello spazio, potrebbe essere solo molto, molto piccolo.

Ecco una bella animazione per dimostrare cosa sto facendo qui:

inserisci qui la descrizione dell'immagine

Alla fine, diventa questo, in cui i punti si muovono appena. Questo è quando vedo quali unghie tocca:

inserisci qui la descrizione dell'immagine

Ecco il codice di animazione ungolfed:

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
Il tuo nome utente è adatto a questo problema.
fosgene

Ell ha fornito un interessante caso limite a cui non avevo pensato. Ho chiarito le specifiche e ho aggiunto quell'esempio. Come funziona il tuo codice su questo esempio? Poiché questo è stato chiarito dopo il tuo post, non sei obbligato a correggere il tuo codice, se non è conforme alle specifiche aggiornate, ma ho pensato di farti sapere.
Martin Ender,

Ho introdotto alcune modifiche per risolverlo, ma ha rivelato un bug nel mio programma (se provi a inserirlo nell'ultimo esempio, mostra solo una riga). Proverò a risolverlo.
Stretch Maniac,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.