Напруга на графіку, частина II: Гумка


13

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

Введемо m цвяхів у дошку у положеннях (x 1 , y 1 ) до (x m , y m ) . Прив’яжіть гумку до першого і останнього з них і натягніть навколо інших цвяхів, таким чином, щоб стрічка перетинала всі цвяхи в порядку. Зауважимо, що резинка тепер описує кусково-лінійну параметризовану функцію (x (t), y (t)) у двовимірному просторі.

Тепер введіть ще п цвяхів у дошку, у положеннях (x 1 , y 1 ) до (x n , y n ) . Якщо ми тепер видалити всі оригінальні м нігтів , за винятком першого і останнього (що кінці гуми прив'язаних до), гумка буде скоротити до тих пір, поки бреше тугу навколо нових цвяхів, отримуючи інше кусочно - лінійну функцію.

Як приклад, візьміть 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% горизонтальної та вертикальної протяжності зображення. Масштаби довжини x і y повинні бути однаковими. Потрібно показати цвяхи у другому наборі (використовуючи не менше 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}}

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

І ось один приклад, який показує значення двох початкових цвяхів, що залишилися. Результат повинен бути б і НЕ :

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

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

Дякуємо Ell, що надав цей приклад.


@laurencevs Рядок є однозначним, що значно спрощує речі, оскільки є очевидний напрямок, в якому обробляти функції та нігті. Цей може містити петлі та зигзаги, а форма функції значно відрізняється (і змінюється), що означає, що рішення повинні бути значно різними.
Мартін Ендер

Який результат цього ?
Ell

@ Все, дуже приємний тестовий випадок. Я думаю, що для послідовності це повинно бути b , але мені дійсно потрібно уточнити питання з цього приводу. Зробимо це незабаром. Спасибі!
Мартін Ендер

Відповіді:


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

Зауважте, що:

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

  • Ми тримаємо навколо нігтів значення нуля, тобто нігті, які раніше, але більше не тримають смугу, замість того, щоб їх відразу видаляти. Якби ми це зробили, це може викликати небажану ланцюгову реакцію, тоді як ми намагаємось підтримувати ефекти кожного кроку локалізованим. Натомість цвяхи зі значенням нуля вважаються прийнятними для видалення як частина більшого процесу.

Тепер ми можемо описати, що відбувається на кожному кроці:

  • Вибираємо цвях, який потрібно видалити з поточного шляху гурту. Цвях можна видалити, якщо він є частиною першого набору цвяхів (крім кінцевих точок) або якщо його значення дорівнює нулю.

  • Прикидаючись, що два сусідні цвяхи зафіксовані, ми з'ясовуємо, через які цвяхи з другого набору або пари кінцевих точок буде проходити смуга, коли вибраний цвях буде видалений (ми не турбуємося з рештою цвяхів з інших Перший набір, оскільки вони згодом усі будуть вилучені.) Ми робимо це аналогічно до рішення I частини . Усі ці цвяхи знаходяться на одній стороні смуги, тому ми присвоюємо всім їм значення 1 або -1 , залежно від сторони.

  • Ми оновлюємо значення двох сусідніх цвяхів, щоб відобразити зміни в шляху гурту (легко найскладніша частина!)

Цей процес повторюється, поки не буде більше нігтів для видалення:

Малюнок 3

Ось більш складний приклад, який ілюструє смугу, яка обгортається нігтем кілька разів:

Малюнок 4


Дивовижний! Тільки одне: чи є ця графіка растрованою чи це векторна графіка? У першому випадку мені доведеться вказати на "Масштаби довжини х і у повинні бути однаковими". Крім того, що ви використовуєте для створення всіх тих графічних зображень, які ви використовуєте у своїх поясненнях. matplotlib теж?
Мартін Ендер

Спасибі! Помилка ... Оскільки matplotlib давайте мені масштабувати сюжет на льоту, я збираюся перейти з векторною графікою :) Для ілюстрацій я в основному використовую GeoGebra . Це трохи химерно, але це робить роботу.
Ел

Так добре, якщо ви можете змінити їх розмір як завгодно, це добре. Дякую за посилання, я перевірю це!
Мартін Ендер

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.