Напряжение на графике, часть II: резинка


13

Это вторая из двух задач о «подтягивании функций». Вот несколько проще Часть I .

Вбиваем m гвоздей в доску в положениях (x 1 , y 1 ) - (x m , y m ) . Свяжите резиновую ленту с первым и последним из них и растяните вокруг других гвоздей так, чтобы полоса пересекала все гвозди по порядку. Обратите внимание, что резиновая полоса теперь описывает кусочно-линейную параметризованную функцию (x (t), y (t)) в 2D-пространстве.

Теперь проехать еще п гвозди в доску, в положениях 1 , у 1 ) к п , у п ) . Если мы теперь удалить все оригинальные м ногтей , за исключением первого и последней (что концы резины привязанных к), резинка будет сократить до тех пор, пока врет Тугую вокруг новых гвоздей, получая другое кусочно - линейную функцию.

В качестве примера возьмем m = 12 начальных гвоздей в положениях (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) и n = 10 дополнительных гвоздей в положениях (1, 1), (3, 1), (4, 4), (1, 3), (2, 2), (5, -1), (5, 0 ), (6, 2), (7, 1), (6, 0) . Следующие три графика показывают процесс, описанный выше:

введите описание изображения здесь

Для большей версии: щелкните правой кнопкой мыши -> Открыть в новой вкладке

А вот анимация затягивания резинкой, если у вас возникли трудности с ее визуализацией:

введите описание изображения здесь

Соревнование

Учитывая два списка «гвоздей», нарисуйте тугую резиновую полосу вокруг второго списка, если она начинается с формы, пересекающей все гвозди в первом списке.

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

Если результат растеризован, он должен быть не менее 300 пикселей с каждой стороны. Конечная резинка и гвозди должны покрывать не менее 75% горизонтального и вертикального размера изображения. Шкалы длин х и у должны быть одинаковыми. Вам нужно показать гвозди во втором наборе (используя не менее 3х3 пикселей) и строку (не менее 1 пикселя в ширину). Вы можете включать или не включать оси.

Цвета - ваш выбор, но вам нужно как минимум два различимых цвета: один для фона, другой для ногтей и нитки (хотя они могут иметь разные цвета).

Вы можете предположить, что все гвозди из второго списка находятся на расстоянии не менее 10 -5 единиц от первоначальной формы резиновой ленты (так что вам не нужно беспокоиться о неточности с плавающей точкой).

Это код гольф, поэтому самый короткий ответ (в байтах) выигрывает.

Больше примеров

Вот еще два примера:

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

введите описание изображения здесь

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

введите описание изображения здесь

И вот один пример, который показывает значение двух оставшихся начальных гвоздей. Результат должен быть b, а не a :

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

введите описание изображения здесь

Спасибо Элл за предоставленный пример.


@laurencevs Строка 1 является однозначной, что значительно упрощает вещи, потому что есть очевидное направление, в котором нужно обрабатывать функции и ногти. Этот может содержать петли и зигзаги, и форма функции значительно отличается (и варьируется), что означает, что решения должны быть значительно различными.
Мартин Эндер

Какой выход этого ?
Ell

@Ell Ах, очень хороший тестовый пример. Я полагаю, для последовательности, это должно быть б , но мне действительно нужно уточнить вопрос об этом. Сделаем это в ближайшее время. Благодарность!
Мартин Эндер

Ответы:


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.

пример

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

фигура 1

Как это устроено

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

Работа постепенно означает, что мы должны следить за промежуточным состоянием резиновой ленты. Вот сложная часть: недостаточно просто отслеживать, через какие гвозди проходит группа. В процессе удаления ногтей полоса может наматываться, а затем разматываться вокруг ногтя. Поэтому, когда группа взаимодействует с гвоздем, мы должны отслеживать, сколько раз и в каком направлении она оборачивается вокруг него. Мы делаем это, присваивая значение каждому гвоздю вдоль полосы следующим образом:

фигура 2

Обратите внимание, что:

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

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

Теперь мы можем описать, что происходит на каждом этапе:

  • Мы выбираем гвоздь, чтобы удалить из текущего пути группы. Гвоздь может быть удален, если он входит в первый набор гвоздей (за исключением конечных точек) или если его значение равно нулю.

  • Делая вид, что два соседних гвоздя зафиксированы, мы выясняем, какие гвозди из второго набора или пару конечных точек, через которые будет проходить полоса, после удаления выбранного гвоздя (мы не беспокоимся по поводу остальных гвоздей из первый набор, так как они в конечном счете быть удалены.) Мы делаем это таким же образом , чтобы решения первой части . Все эти ногти находятся на одной стороне полосы, поэтому мы присваиваем всем им значение 1 или -1 , в зависимости от стороны.

  • Мы обновляем значение двух соседних гвоздей, чтобы отразить изменения в пути группы (легко самая хитрая часть!)

Этот процесс повторяется до тех пор, пока не останется больше гвоздей для удаления:

Рисунок 3

А вот более сложный пример, иллюстрирующий, как группа несколько раз оборачивается вокруг гвоздя:

Рисунок 4


Удивительный! Только одно: эта графика растрирована или это векторная графика? В первом случае я должен указать вам: «Шкалы длин х и у должны быть одинаковыми». Кроме того, что вы используете для создания всех тех графиков, которые вы используете в своих объяснениях. матплотлиб тоже?
Мартин Эндер

Благодарность! Эээ ... Так как matplotlib позволяет мне масштабировать сюжет на лету, я собираюсь использовать векторную графику :) Для иллюстраций я использую в основном GeoGebra . Это немного странно, но это делает работу.
Ell

Да, хорошо, если вы можете произвольно изменить его размер, это нормально. Спасибо за ссылку, я проверю это!
Мартин Эндер

4

Java - 1262 байта

Я знаю, что это, вероятно, не так много в гольфе, как могло бы быть.

Однако, похоже, никто больше не подходит к тарелке и не отвечает на этот вопрос, поэтому я буду.

Во-первых, класс "T" - точечный класс - 57 байт

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

А основной класс - 1205 байт

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

Пример:

введите описание изображения здесь

Чтобы запустить, вызовите функцию "d" со списком точек и массивом гвоздей (да, я знаю, странно). Что оно делает:

  • создает список точек, представляющих линии, то есть все точки между линиями.
  • повторяет алгоритм несколько раз для этих точек, так что каждая точка является средним из двух точек вокруг нее.
  • Когда кажется, что точки больше не двигаются, я рисую между гвоздями, которых они касаются.

Я не уверен, что оси в пикселях в порядке. Он всегда будет занимать более 75% пространства, он может быть очень, очень маленьким.

Вот хорошая анимация, чтобы продемонстрировать, что я здесь делаю:

введите описание изображения здесь

В конце концов, это становится таким, в котором точки едва двигаются. Это когда я вижу, какие ногти это касается:

введите описание изображения здесь

Вот код без анимации:

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
Ваше имя пользователя подходит для этой проблемы.
Фосген

Элл представил интересный крайний случай, о котором я не думал. Я уточнил спецификацию и добавил этот пример. Как ваш код работает на этом примере? Поскольку это было разъяснено после вашего поста, вы не обязаны исправлять свой код, если он не соответствует обновленной спецификации, но я подумал, что сообщу вам об этом.
Мартин Эндер

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