Dove devo mettere il mio ristorante?


15

Sei il proprietario di un ristorante. Stai aprendo una nuova area in Cartesia dove c'è solo una strada principale, nota come asse y. Vuoi posizionare il tuo ristorante in modo tale da ridurre al minimo la distanza totale dal tuo ristorante e da ciascuna delle case in quella zona.

Input :

L'input sarà

n, the number of houses
house1
house2
house3
...
houseN

dove ogni casa è una coordinata nel modulo x y. Ogni unità rappresenta un chilometro.

Puoi prendere l'input come una stringa o fornire una funzione che accetta l'input, in qualunque formato tu scelga, come argomenti.

Output : la coordinata y del tuo ristorante (ricorda, sarà posizionata sull'asse y). In realtà, sarà situato sul lato della strada, ma la differenza è trascurabile.

In sostanza, se l'ennesima casa è h_ned Dè la funzione della distanza, allora vuoi trovare ktale che D(h_0, (0, k)) + D(h_1, (0, k)) + D(h_2, (0, k)) + ... + D(h_n, (0, k))sia minimizzata.

Si noti che la distanza viene calcolata come se il cliente viaggiasse esattamente in linea retta da casa al ristorante. Questa è la distanza dal (x, y)tuo ristorante sqrt(x^2 + (y - k)^2).

L'output deve essere accurato con almeno 2 cifre decimali.

L'output può essere stampato come una stringa o può essere restituito dalla funzione.

Esempio di input / output:

Input:
2
5.7 3.2
8.9 8.1
Output:
5.113013698630137

La distanza totale in questo esempio è di circa 15.4003chilometri.

Questo è il codice golf - vince il codice più corto.

PS Sono anche interessato a una soluzione matematica che non è solo forza bruta. Non vincerà il codice golf ma otterrà alcuni voti. Ecco come ho fatto il problema di esempio:

Sia il punto A posizionato in A (5.7, 3.2) e B in B (8.9, 8.1). Lascia che la soluzione punti a (0, k) sia C. Rifletti A sull'asse y per fare A 'a (-5.7, 3.2). La distanza da A 'a C è uguale alla distanza da A a C. Pertanto, il problema può essere ridotto al punto C in modo tale che A'C + CB sia ridotto al minimo. Ovviamente, questo sarebbe il punto C che si trova sulla linea A'B.

Non so se questo generalizzerebbe bene a 3 o più punti.


Quale metrica viene utilizzata per la funzione di distanza D? Euclideo?
Reto Koradi,

1
Anche se esiste solo una strada principale, supponiamo che un cliente viaggi in linea retta da casa al ristorante? O viaggiano prima direttamente sull'asse y? (O in altre parole, usiamo la distanza euclidea o Manhattan per D?)
trichoplax,

1
(Questo può essere risolto dall'esempio ma sarebbe bello averlo dichiarato esplicitamente.)
trichoplax,

@trichoplax Euclidean? Euclideo significa sqrt(diffX^2 + diffY^2)? Quindi euclideo. So che non si adatta perfettamente allo scenario, ma presumo che il cliente viaggi in linea retta in qualche modo da casa sua.
soktinpk,

5
Accettare input come un elenco di numeri complessi che rappresentano le posizioni delle case sul piano complesso è accettabile?
lirtosiast

Risposte:


27

C, 315 302 byte

t,i;double o,w,h,x,y,k,a,b,c;double g(N,S)double N,S[][2];{for(t=0;t<N;t++)k+=S[t][1];k/=N;for(i=0;i<9;i++){o=w=h=0;for(t=0;t<N;t++)x=S[t][0],y=S[t][1],a=y-k,c=k*k-2*k*y+x*x+y*y,o+=-a/sqrt(x*x+a*a),w+=x*x/pow(c,1.5),h+=3*x*x*a/pow(c,2.5);a=h/2;b=w-h*k;c=o-w*k+a*k*k;k=(-b+sqrt(b*b-4*a*c))/h;}return k;}

Questo è tutt'altro che carino, e non è neanche breve. Ho pensato che dal momento che non vincerò il contest di lunghezza, posso provare a vincere il contest di precisione (teorico)! Il codice è probabilmente un ordine di grandezza o due più veloce della soluzione bruteforce e si basa su un po 'di sciocchezze matematiche.

Definiamo una funzione g(N,S)che accetta come input il numero di case,N e una matrice di case S[][2].

Qui viene svelato, con un caso di prova:

t,i;
double o,w,h,x,y,k,a,b,c;
double g(N,S)double N,S[][2];{
    /* Initially, let k hold the geometric mean of given y-values */
    for(t=0;t<N;t++)
        k+=S[t][1];
    k/=N;

    /* We approximate 9 times to ensure accuracy */
    for(i=0;i<9;i++){
        o=w=h=0;
        for(t=0;t<N;t++)
            /* Here, we are making running totals of partial derivatives */
            /* o is the first, w the second, and h the third*/
            x=S[t][0],
            y=S[t][1],
            a=y-k,
            c=k*k-2*k*y+x*x+y*y,
            o+=-a/sqrt(x*x+a*a),
            w+=x*x/pow(c,1.5),
            h+=3*x*x*a/pow(c,2.5);
        /* We now use these derivatives to find a (hopefully) closer k */
        a=h/2;
        b=w-h*k;
        c=o-w*k+a*k*k;
        k=(-b+sqrt(b*b-4*a*c))/h;
    }
    return k;
}
/* Our testing code */
int main(int argc, char** argv) {
    double test[2][2] = {
        {5.7, 3.2},
        {8.9, 8.1}
    };    
    printf("%.20lf\n", g(2, test));
    return 0;
}

Quali uscite:

5.11301369863013732697

Avvertenza: potrebbe essere necessaria la conoscenza di alcuni calcoli per una comprensione completa!

Quindi, parliamo di matematica.

Conosciamo la distanza dal nostro punto desiderato (0, k)e una casa i:

Definizione di D_i

E quindi la distanza totale Ddalle ncase può essere definita come segue:

Definizione di D

Quello che vorremmo fare è minimizzare questa funzione prendendo un derivato rispetto ke impostandolo uguale 0. Proviamolo. Sappiamo che i derivati ​​di Dpossono essere descritti come segue:

Derivata di D

Ma la prima derivata parziale di ciascuna Diè piuttosto negativa ...

Derivata 1 di Di

Sfortunatamente, anche con n == 2, impostando questi derivati 0e risolvendolik diventa disastroso molto rapidamente. Abbiamo bisogno di un metodo più solido, anche se richiede una certa approssimazione.

Inserisci i polinomi di Taylor.

Se conosciamo il valore D(k0)e tutti Di derivati ​​su k0, possiamo riscrivere Dcome una serie di Taylor:

Definizione di Taylor Series

Ora, questa formula contiene un sacco di cose e i suoi derivati ​​possono diventare piuttosto ingombranti, ma ora abbiamo un'approssimazione polinomiale di D !

Facendo un po 'di calcolo, troviamo i prossimi due derivati Dvalutando i derivati ​​di Di, proprio come prima:

Derivata 2 di Di

Derivata 3 di Di

Troncando e valutando i derivati, ora possiamo approssimare Dcome un polinomio di 3 ° grado del modulo:

Forma approssimativa di D

Dove A, B, C, D sono semplicemente numeri reali.

Ora questo possiamo minimizzare. Quando prendiamo una derivata e la impostiamo uguale a 0, finiremo con un'equazione della forma:

Approssimazione di D '

Facendo il calcolo e le sostituzioni, creiamo queste formule per a, b, and c:

Valore di a

Valore di b

Valore di c

Ora il nostro problema ci offre 2 soluzioni fornite dalla formula quadratica:

Valore di k

L'intera formula per k sarebbe un enorme onere da scrivere, quindi lo facciamo in pezzi qui e nel codice.

Dal momento che sappiamo che il più alto kcomporterà sempre la distanza minima del nostro approssimativoD (ne ho una prova davvero meravigliosa, che il margine di questo documento è insufficiente per contenere ...) non dobbiamo nemmeno considerare il più piccolo di le soluzioni.

Rimane un problema finale. Ai fini dell'accuratezza, è necessario che iniziamo con un valore k0che sia almeno nel campo di baseball di dove ci aspettiamo che la risposta sia. A tal fine, il mio codice sceglie la media geometrica dei valori y di ogni casa.

Come fail-safe, ripetiamo di nuovo l'intero problema 9 volte, sostituendolo k0conk ogni iterazione, per garantire l'accuratezza.

Non ho fatto i calcoli su quante iterazioni e quanti derivati ​​sono veramente necessari, ma ho scelto di sbagliare dal lato della cautela fino a quando non posso confermare l'accuratezza.

Se ce l'hai fatta con me, grazie mille! Spero che tu abbia capito, e se noti qualche errore (di cui probabilmente ce ne sono molti, sono molto stanco), per favore fammi sapere!


2
Io, per esempio, mi piacerebbe vedere la spiegazione della tua matematica.
DLosc,

2
@DLosc Il tuo desiderio è il mio comando.
BrainSteel,

4
È davvero meraviglioso. Ho pensato di provare il metodo di Newton, ma non ho pensato alla serie di Taylor.
DLosc,

5
Vorrei poter votare di più.
Alex A.

@AlexA. Vorrei che anche tu potessi votarmi di più; D Entro circa un giorno, rimuoverò l'ultimo riferimento al teorema del Fermat e lo sostituirò con una prova. Appena ne trovo uno.
BrainSteel,

13

TI-BASIC, 20

fMin(sum(abs(iX-Ans)),X,~E99,E99

Prende input sulla schermata iniziale della calcolatrice serie TI-83 o 84 in questo modulo (è possibile digitare un 2:primo, che verrebbe ignorato):

{5.7+3.2i,8.9+8.1i}:[program name]

Se le case sono sempre a meno di un miliardo di km dall'origine, E99 può essere sostituito con E9 per una dimensione di 18 byte.

Se esistesse un linguaggio golfistico basato su Mathematica, potrebbe vincere questa sfida in 10-14 byte.


10

Mathematica, 42 byte

k/.Last@Minimize[Tr[Norm[#-{0,k}]&/@#],k]&

Questa è una funzione anonima che prende un elenco di coppie come coordinate della casa e restituisce la coordinata y desiderata.

È un'implementazione abbastanza semplice. Mappiamo Norm[#-{0,k}]&su ciascuna coordinata della casa (che calcola la distanza su un punto indeterminato {0,k}sull'asse y) e li sommiamo tutti con Tr[...](per traccia, che è equivalente a Totalper le liste 1-d). Quindi usiamo il conveniente Minimizeper trovare il minimo di questa somma in k. Questo dà un risultato del modulo {distance, {k -> position}, quindi dobbiamo k/.Last@estrarre quello positionche stiamo cercando.


6

Pyth, 33 byte

hosm.a,d,0NQcR^T3rhKSms*^T3ekQheK

Questa è la soluzione della forza bruta: ordina tutte le possibili posizioni del ristorante, con una risoluzione di 0,001 km, in base alla loro distanza totale dalle case, quindi seleziona quella con la minima distanza totale. Prende le posizioni delle case come un elenco di 2 elenchi di voci di float su STDIN.

Dimostrazione.

La risoluzione può essere impostata da 1e-2 km a 1e-10 km alla stessa lunghezza del codice, ma con rallentamenti esponenziali in fase di esecuzione.

Sento che questo potrebbe essere ancora un po 'golfato, lo rivedrò più tardi.


2
Lol! Hai copiato la mia soluzione? ;-)
Jakube,

@Jakube La corrispondenza ^T3è particolarmente impressionante.
isaacg,

Abbiamo davvero bisogno di una gamma galleggiante.
Maltysen,

3

Python 2, 312

from math import*;A,L,p=[map(float,raw_input().split()) for i in range(input())],lambda a:a[1],0.001
def R(m,M,s):
 while m<=M:yield m;m+=s
m=min(A,key=L)[1];M=max(A,key=L)[1];r=(m+M)/2;s=r-m
while s>p:D={y:sum([sqrt(X*X+(Y-y)**2)for X,Y in A])for y in R(r-s,r+s,s*p)};r=min(D,key=D.get);s*=p;m=r-s;M=r+s
print r

3

R, 145 143 126

Sospetto che ci sia un sacco di spazio per giocare a golf. Praticamente un metodo di forza bruta. Vorrei trovare un modo migliore per farlo. Pensavo che i mezzi geometrici potessero aiutare, ma purtroppo no.

r=sapply(seq(min((p=matrix(scan(),nr=2))),max(p),.001),function(X,p)c(X,sum((p[1,]^2+(p[2,]-X)^2)^.5)),p);r[1,order(r[2,])[1]]

Prova

> r=sapply(seq(min((p=matrix(scan(),nr=2))),max(p),.001),function(X,p)c(X,sum((p[1,]^2+(p[2,]-X)^2)^.5)),p);r[1,order(r[2,])[1]]
1: 5.7 3.2
3: 8.9 8.1
5: 
Read 4 items
[1] 5.113
> 

Per motivi di interesse, se ci sono solo due case da considerare, ciò che segue restituirà un risultato accettabile. Tuttavia cade su tre. Al momento non posso andare oltre, ma ho pensato che alcuni cervelli qui potessero farci qualcosa.

p=matrix(scan(),nr=2);weighted.mean(p[2,],sum(p[1,])-p[1,])

2

MATLAB, 42

Se è OK accettare input come

I=[5.7 3.2
    8.9 8.1]

quindi questa affermazione

fminunc(@(y)sum(hypot(I(:,1),I(:,2)-y)),0)

ritorna 5.113014445748538.

Rubando senza vergogna il metodo di Thomas Kwa, potresti arrivare almeno a 30:

I=[5.7+3.2i 8.9+8.1i];
fminunc(@(y)sum(abs(I-i*y)),0)

1
Può essere esteso per lavorare con il nnumero di casa? Dal momento che è quello che la domanda sta chiedendo.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

Sì, funziona con qualsiasi numero di righe in I.
David,
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.