Где я должен поставить свой ресторан?


15

Вы владелец ресторана. Вы открываете в новой области в Cartesia, где есть только одна главная дорога, известная как ось Y. Вы хотите разместить свой ресторан таким образом, чтобы минимизировать общее расстояние от вашего ресторана и каждого из домов в этом районе.

Вход :

Вход будет

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

где каждый дом является координатой в форме x y. Каждая единица представляет один километр.

Вы можете принять входные данные как строку или предоставить функцию, которая принимает входные данные в любом формате в качестве аргументов.

Вывод : Y-координата вашего ресторана (помните, он будет расположен на оси Y). На самом деле, он будет расположен на обочине дороги, но разница незначительна.

По сути, если n-й дом есть h_nи Dесть функция расстояния, то вы хотите найти kтакую, которая D(h_0, (0, k)) + D(h_1, (0, k)) + D(h_2, (0, k)) + ... + D(h_n, (0, k))сведена к минимуму.

Обратите внимание, что расстояние рассчитывается так, как если бы клиент путешествовал по прямой линии от дома до ресторана. Это расстояние от (x, y)вашего ресторана sqrt(x^2 + (y - k)^2).

Вывод должен быть точным, по крайней мере, до 2 десятичных знаков.

Вывод может быть напечатан в виде строки или может быть возвращен из функции.

Пример ввода / вывода:

Input:
2
5.7 3.2
8.9 8.1
Output:
5.113013698630137

Общее расстояние в этом примере составляет около 15.4003километров.

Это код гольф - самый короткий код выигрывает.

PS Меня также интересует математическое решение, которое не просто грубая сила. Он не выиграет гольф-код, но получит несколько голосов. Вот как я сделал пример задачи:

Пусть точка A находится в точке A (5.7, 3.2), а точка B в точке B (8.9, 8.1). Пусть точка решения в точке (0, k) равна C. Отразите A по оси y, чтобы получить A 'в точке (-5.7, 3.2). Расстояние от A 'до C равно расстоянию от A до C. Следовательно, проблема может быть сведена к точке C, так что A'C + CB минимизируется. Очевидно, это будет точка C, лежащая на прямой A'B.

Я не знаю, будет ли это обобщать до 3 или более баллов.


Какая метрика используется для функции расстояния D? Евклидовой?
Рето Коради

1
Хотя существует только одна главная дорога, предполагаем ли мы, что клиент путешествует по прямой линии от дома до ресторана? Или они сначала движутся прямо к оси у? (Или, другими словами, мы используем евклидово или манхэттенское расстояние для D?)
trichoplax

1
(Это можно понять из примера, но было бы неплохо, чтобы это было указано явно.)
trichoplax

@trichoplax евклидов? Означает ли евклидов sqrt(diffX^2 + diffY^2)? Тогда евклидов. Я знаю, что это не совсем подходит для сценария, но предположим, что клиент как-то едет по прямой из своего дома.
soktinpk

5
Является ли приемлемым ввод данных в виде списка комплексных чисел, представляющих позиции домов на комплексной плоскости?
lirtosiast

Ответы:


27

C 315 302 байта

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

Это далеко не красиво, и это не короткий. Я рассчитывал, так как я не собираюсь выигрывать соревнования по длине, я могу попытаться выиграть (теоретический) конкурс на точность! Код, вероятно, на порядок или два быстрее, чем брутфорс-решение, и опирается на немного математического дурачества.

Мы определяем функцию, g(N,S)которая принимает в качестве входных данных количество домов Nи массив домов S[][2].

Вот она, с тестовым примером:

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

Какие выводы:

5.11301369863013732697

Предупреждение: знание полного исчисления может потребоваться для полного понимания!

Итак, давайте поговорим о математике.

Мы знаем расстояние от желаемой точки (0, k)и дома i:

Определение D_i

И, таким образом, общее расстояние Dот nдомов можно определить следующим образом:

Определение D

Мы хотели бы минимизировать эту функцию, взяв производную по отношению к ней kи установив ее равной 0. Давай попробуем. Мы знаем, что производные Dмогут быть описаны следующим образом:

Производная D

Но первая частная производная каждого Diдовольно плохая ...

Производная 1 от Di

К сожалению, даже если n == 2установка этих производных 0и их решение kстановятся катастрофическими очень быстро. Нам нужен более надежный метод, даже если он требует некоторого приближения.

Введите полиномы Тейлора.

Если мы знаем значение, D(k0)а также все Dпроизводные в k0, мы можем переписать Dкак ряд Тейлора:

Определение по серии Тейлор

Теперь, в этой формуле есть куча вещей, и ее производные могут быть довольно громоздкими, но теперь у нас есть полиномиальное приближение D !

Проделав небольшое исчисление, мы найдем следующие две производные D, оценивая производные Di, как и раньше:

Производная 2 от Di

Производная 3 от Di

Усечением и вычислением производных мы можем теперь приблизиться Dкак полином 3-й степени вида:

Приблизительная форма D

Где A, B, C, Dпросто реальные цифры.

Теперь это мы можем минимизировать. Когда мы возьмем производную и установим ее равной 0, мы получим уравнение вида:

Аппроксимация D '

Делая исчисление и подстановки, мы придумываем эти формулы для a, b, and c:

Значение

Значение б

Значение с

Теперь наша задача дает нам 2 решения, задаваемые квадратичной формулой:

Значение к

Вся формула kбудет огромным бременем для записи, поэтому мы делаем это по частям здесь и в коде.

Поскольку мы знаем, что более высокое значение kвсегда будет приводить к минимальному расстоянию от нашего приблизительного значения D(у меня есть поистине изумительное доказательство этого, которого недостаточно для описания этой статьи ...), нам даже не нужно рассматривать меньшее из решения.

Последняя проблема остается. В целях точности необходимо, чтобы мы начали с a k0, которое, по крайней мере, находится в той точке, где мы ожидаем, что ответ будет. Для этого мой код выбирает среднее геометрическое значений y каждого дома.

В отказоустойчивый, мы повторяем всю проблему еще раз в 9 раз, заменяя k0с kна каждой итерации, чтобы обеспечить точность.

Я не подсчитал, сколько итераций и сколько производных действительно необходимо, но я решил ошибиться из-за осторожности, пока не смог подтвердить точность.

Если ты справился со мной, спасибо тебе большое! Я надеюсь, что вы поняли, и если вы заметите какие-либо ошибки (которых, вероятно, много, я очень устал), пожалуйста, дайте мне знать!


2
Я, например, хотел бы увидеть объяснение вашей математики.
DLosc

2
@DLosc Ваше желание - моя команда.
BrainSteel

4
Это действительно изумительно. Я подумывал попробовать метод Ньютона, но не думал о серии Тейлора.
DLosc

5
Я бы хотел, чтобы я высказался об этом больше.
Алекс А.

@AlexA. Я хотел бы, чтобы вы тоже могли выразить мне свое мнение; D В течение дня я удалю ссылку на последнюю теорему Ферма и заменю ее доказательством. Как только я найду один.
BrainSteel

13

TI-BASIC, 20

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

Вводит данные на домашний экран вашего калькулятора серии TI-83 или 84 в этой форме (вы можете ввести 2:первое, что будет игнорироваться):

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

Если дома всегда находятся на расстоянии менее миллиарда километров от источника, E99 можно заменить на E9 для размера 18 байт.

Если бы существовал язык игры в гольф, основанный на Mathematica, он мог бы выиграть этот вызов в 10-14 байтах.


10

Mathematica, 42 байта

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

Это анонимная функция, принимающая список пар в качестве координат дома и возвращающая желаемую координату y.

Это довольно простая реализация. Мы отображаем Norm[#-{0,k}]&на каждую координату дома (которая вычисляет расстояние до неопределенной точки {0,k}на оси Y) и суммируем их все с Tr[...](для трассы, которая эквивалентна Totalдля 1-го списка). Тогда мы используем удобный, Minimizeчтобы найти минимум этой суммы в k. Это дает результат формы {distance, {k -> position}, поэтому нам нужно k/.Last@извлечь positionискомое.


6

Pyth, 33 байта

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

Это решение грубой силы: оно упорядочивает все возможные местоположения ресторана с разрешением 0,001 км по их общему расстоянию от домов, а затем выбирает место с наименьшим общим расстоянием. Это берет местоположения дома как список 2 списков входа плаваний на STDIN.

Демонстрация.

Разрешение может быть установлено в любом месте от 1e-2 км до 1e-10 км при той же длине кода, но с экспоненциальным замедлением во время выполнения.

Я чувствую, что это может быть в гольфе еще немного, я посмотрю на это позже.


2
Лол! Вы скопировали мое решение? ;-)
Якуб

@Jakube Совпадение ^T3особенно впечатляет.
Исаак

Нам действительно нужен диапазон поплавка.
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

Я подозреваю, что на этом осталось много комнаты для игры в гольф. Практически метод грубой силы. Я хотел бы найти более хороший способ сделать это. Я, хотя геометрические средства могут помочь, но увы нет.

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]]

Тестовый забег

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

Интересно, что если есть только два дома для рассмотрения, следующий вернет приемлемый результат. Однако это падает на три. Я не могу продолжать дальше, но я подумал, что некоторые мозги здесь могут что-то с этим сделать.

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

2

МАТЛАБ, 42

Если это нормально, чтобы принять ввод как

I=[5.7 3.2
    8.9 8.1]

тогда это утверждение

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

возвращается 5.113014445748538.

Бесстыдно воровав метод Томаса Ква, вы можете снизить его до 30 как минимум:

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

1
Можно ли его распространить на работу с nномером дома? Так как это то, о чем вопрос.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

Да, это работает с любым количеством строк в I.
Дэвид
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.