Tensione su un grafico, parte I: una corda ondulata


21

Tracciamo una funzione f (x) = sin (πx) + 0,5 sin (3πx) sul dominio [-3,3] . Possiamo interpretarlo come una corda sciolta che giace su una tavola. Ora guidiamo n chiodi nella scacchiera nelle posizioni (x 1 , y 1 ) a (x n , y n ) , dove x i ∈ (-3,3) e y i ∈ [-1,1] . Immagina che ci siano due occhielli all'estremità della corda, cioè nelle posizioni (-3,0) e (3,0). Ora possiamo prendere le estremità della corda e tirare attraverso gli occhielli fino a quando la corda è tesa. Ciò deformerà il nostro grafico in una funzione lineare a tratti.

Alcune immagini potrebbero aiutare. Prendi 8 chiodi a (-2.8, -0.7), (-2.5, -0.9), (-1.2, .2), (-0.5, .8), (0.5, .4), (1.2, -0.9), (1,5, -0,6), (1,8, -0,8) . 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 della stringa se hai qualche difficoltà a visualizzarla:

inserisci qui la descrizione dell'immagine

La sfida

Dato un elenco di "chiodi" (che non è necessariamente ordinato), traccia quei chiodi e la stringa tesa se inizia dalla forma della funzione sopra f .

È 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 largo almeno 300 pixel e alto 100 pixel. L'intervallo di coordinate da (-3, -1,1) a (3,1,1) deve coprire almeno il 75% dell'estensione orizzontale e verticale dell'immagine. Le scale di lunghezza di x ed y non devono essere uguali. Devi mostrare i chiodi (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).

Puoi presumere che tutte le unghie siano almeno 10 -5 unità da f (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 (più semplici):

{{-2.5, 1}, {-1.5, -1}, {-0.5, 1}, {0.5, -1}, {1.5, 1}, {2.5, -1}}

inserisci qui la descrizione dell'immagine

(La stringa coincide con l' asse x .)

{{-2.7, -0.5}, {-2.3, -0.5}, {-1.7, 0.5}, {-1.3, 0.5}, {-0.7, -0.5}, {-0.3, -0.5}, {0.5, 1}, {1.5, -1}, {2.5, 1}}

inserisci qui la descrizione dell'immagine

Vuoi un'altra sfida?

Ecco la parte II!


Possiamo presumere che i chiodi siano ordinati da sinistra a destra?
Ell

@Ell Ah, buona cattura. Dal momento che non l'ho specificato per cominciare, no. Lo chiarirò.
Martin Ender,

Risposte:


8

Pitone + Pycairo, 727 708 608, + PyLab, 383

from pylab import*
def f(N):
 def P(u,w,N):
    T=lambda v,p:(C(v-u,p-u)>0)==(C(w-v,p-v)>0)==(C(u-w,p-w)>0);M=[(i,n)for i,n in enumerate(N)if T(V([n[0],sin(pi*n[0])+sin(3*pi*n[0])/2]),n)]
    if M:i,n=max(M,key=lambda n:C(n[1]-u,w-u)**2);M=P(u,n,N[:i])+[n]+P(n,w,N[i+1:])
    return M
 V=array;C=cross;a=V([3,0]);plot(*zip(*([-a]+P(-a,a,map(V,sorted(N)))+[a])));N and scatter(*zip(*N));show()

Esempio

f([(-2.8,-0.7),(-2.5,-0.9),(-1.2,0.2),(-0.5,0.8),(0.5,0.4),(1.2,-0.9),(1.5, -0.6),(1.8, -0.8)])

Esempio 1

Come funziona

Supponiamo di sapere che la stringa tesa passa attraverso due punti A e B (possiamo sempre iniziare con
A = (-3, 0) e B = (3, 0)) . Mentre tiriamo la stringa, "vuole" prendere il percorso più breve possibile tra A e B , ovvero, idealmente, il segmento AB . Tuttavia, se ci sono dei chiodi nell'area delimitata dalla funzione ( sin πx + ... ) e AB , almeno uno di essi deve bloccare la corda. In particolare, i chiodi più lontani da AB all'interno di detta area devono bloccare la corda. Quindi, se C , oltre ad A e B C è questo chiodo, sappiamo che deve passare la corda tesa . Ora possiamo ripetere il processo per i segmenti AC e CB e continuare in questo modo fino a quando alla fine non ci saranno più chiodi intermedi. Figura 1

Questo è un algoritmo binario di divisione e conquista con una scansione lineare in ogni fase, quindi ha una complessità nel caso migliore di O (n log n) e la complessità nel caso peggiore di O (n 2 ) .


Si verifica un errore se l'elenco di punti è vuoto. Ma a parte quello il mio è ovviamente senza speranza!
feersum

@feersum Buona cattura. Fisso.
Ell

3

Python + pylab, 576 byte

Algoritmo:

Ho interpretato il problema come trovare il percorso più breve da (-3, 0) a (3, 0) in modo tale che un segmento di linea verticale che collega un punto sul percorso a un punto su f (x) non attraversi mai un chiodo.

In ogni x dove esiste almeno un chiodo, trova il limite minimo superiore e il limite inferiore massimo dato dalle unghie su quella x . Considera i punti dati da questi limiti più i punti iniziale e finale come vertici di un grafico. Aggiungi un bordo con il peso dato dalla distanza euclidea tra due vertici se il segmento di linea tra loro cade all'interno dei limiti superiore e inferiore per ciascuna coordinata x interposta. Trova il percorso più breve in questo grafico.

Esempio con 27 punti casuali:

(-0.367534, -0.722751), (-0.710649, -0.701412), (1.593101, -0.484983), (1.771199, 0.681435), (-1.878764, -0.491436), (-0.061414, 0.628570), (-0.326483, -0.512950), (0.877878, 0.858527), (1.256189, -0.300032), (1.528120, -0.606809), (-1.343850, -0.497832), (1.078216, 0.232089), (0.930588, -0.053422), (-2.024330, -0.296681), (-2.286014, 0.661657), (-0.009816, 0.170528), (2.758464, 0.099447), (-0.957686, 0.834387), (0.511607, -0.428322), (-1.657128, 0.514400), (1.507602, 0.507458), (-1.469429, -0.239108), (0.035742, 0.135643), (1.194460, -0.848291), (2.345420, -0.892100), (2.755749, 0.061595), (0.283293, 0.558334), 

esempio zoppo

golfed

Ciò che appare come diversi spazi di rientro nel for j in R(i&~1)loop dovrebbe in realtà essere una scheda.

from pylab import*
P=((3,0),(-3,0))+input()
X=sorted(set(zip(*P)[0]))
l=len(X)*2
if l>4:scatter(*zip(*P[2:]))
f=lambda x:sin(pi*x)+sin(3*pi*x)/2
B=[[max([-9]+[p[1]for p in P if x==p[0]and p[1]<f(x)]),min([9]+[p[1]for p in P if x==p[0]and p[1]>f(x)])]for x in X]
b=zeros(l);b[2:]=inf
v=list(b)
R=range
for i in R(l):
 for j in R(i&~1):
    A=B[j/2][j&1];D,d=B[i/2][i&1]-A,X[i/2]-X[j/2];K=1;c=b[j]+norm((d,D))
    for k in R(j/2+1,i/2):C=A+D/d*(X[k]-X[j/2]);K&=C<B[k][1];K&=C>B[k][0]
    if(c<b[i])&K:b[i]=c;v[i]=j,(X[j/2],A)
l-=2
s=P[:1]
while l/2:l,p=v[l];s+=(p,)
plot(*zip(*s))
show()

Ungolfed

from pylab import*
P = input()
Xn,Yn = zip(*P)
X = set(Xn+(3,-3))
f = lambda x:sin(pi*x)+sin(3*pi*x)/2
ylb = {x: max([-9]+[p[1] for p in P if p[0] == x and p[1] < f(x)]) for x in X}
yub = {x: min([9]+[p[1] for p in P if p[0] == x and p[1] > f(x)]) for x in X}
ylb[-3] = yub[3] = ylb[3] = 0
X = sorted(X)
l = len(X)
best = zeros((l,2))
best[1:] = inf
prev = [ [0,0] for i in range(l) ]
for i in range(l): # calculate min path to X[i] lb or ub
  for ib in 0,1:
    for j in range(i): # point to come from
      for jb in 0,1:
          Y2, Y1 = (ylb, yub)[ib][X[i]], (ylb, yub)[jb][X[j]]
          dy,dx = Y2 - Y1, X[i] - X[j]
          if all([Y1 + dy/dx*(x - X[j]) < yub[x] and Y1 + dy/dx*(x - X[j]) > ylb[x] for x in X[j+1:i]]):
             c = best[j][jb] + (dy**2+dx**2)**.5
             if c < best[i][ib]:
                 best[i][ib] = c
                 prev[i][ib] = j, jb, (X[j], Y1)
j, jb = l-1,0
pts = [(3,0)]
while j:
    j, jb, p = prev[j][jb]
    pts += [p]
plot(*zip(*pts))
scatter(Xn,Yn)
show()

PyLab è stata sicuramente una scelta più intelligente :)
Ell
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.