Алгебраїчний кривий плотер


14

Алгебраїчна крива - це певне "1D підмножина" "2D-площини", яке можна описати як набір нулів {(x,y) in R^2 : f(x,y)=0 }многочлена f. Тут ми розглядаємо 2D-площину як справжню площину R^2таку, що ми можемо легко уявити, як може виглядати така крива, в основному річ, яку можна намалювати олівцем.

Приклади:

  • 0 = x^2 + y^2 -1 коло радіуса 1
  • 0 = x^2 + 2y^2 -1 еліпс
  • 0 = xyхрест форма, в основному , об'єднання осі абсцис і вісь ординат
  • 0 = y^2 - x парабола
  • 0 = y^2 - (x^3 - x + 1)еліптичної кривої
  • 0 = x^3 + y^3 - 3xy фолія Декарта
  • 0 = x^4 - (x^2 - y^2) лемнікат
  • 0 = (x^2 + y^2)^2 - (x^3 - 3xy^2) трифолій
  • 0 = (x^2 + y^2 - 1)^3 + 27x^2y^2 астроїд

Завдання

Давши поліном f(як визначено нижче) та діапазони x / y, вивести чорно-біле зображення розміром не менше 100x100 пікселів, що показує криву як чорну лінію на білому тлі.

Деталі

Колір : Ви можете використовувати будь-які два інші кольори на ваш вибір, їх просто просто розпізнати.

Сюжет : Замість піксельного зображення ви також можете виводити це зображення як ascii-art, де фоновим "пікселями" повинен бути пробіл / підкреслення або інший символ, який "виглядає порожнім", а лінія може бути складена з символу, який виглядає " повний "як Mабо Xабо #.

Вам не доведеться турбуватися про згладжування.

Вам потрібно лише побудувати лінії, де знак полінома змінюється з однієї сторони лінії на іншу (це означає, що ви могли б, наприклад, використовувати алгоритм квадратичного маршу), вам не потрібно правильно будувати "патологічні випадки, наприклад, 0 = x^2коли це робить знак не змінюватись при переході від однієї сторони лінії до іншої, але ця лінія повинна бути безперервною і розділяти області різних знаків f(x,y).

Поліном : Поліном наводиться у вигляді (m+1) x (n+1)матриці / списку списків (реальних) коефіцієнтів, у прикладі нижче наведені умови коефіцієнтів у їхньому положенні:

[   1 * 1,   1 * x,   1 * x^2,   1 * x^3,  ... , 1 * x^n ]
[   y * 1,   y * x,   y * x^2,   y * x^4,  ... , y * x^n ]
[   ...  ,   ...   ,   ...   ,    ...   ,  ... ,   ...   ]
[ y^m * 1, y^m * x, y^m * x^2, y^m * x^3 , ..., y^m * x^n]

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

Далі приклади зверху представлені у вигляді матриці, визначеної так:

Circle:       Ellipse:      Parabola:  Cross:    Elliptic Curve: e.t.c
[-1, 0, 1]    [-1, 0, 1]    [ 0,-1]    [ 0, 0]   [-1, 1, 0,-1]
[ 0, 0, 0]    [ 0, 0, 0]    [ 0, 0]    [ 0, 1]   [ 0, 0, 0, 0]
[ 1, 0, 0]    [ 2, 0, 0]    [ 1, 0]              [ 1, 0, 0, 0]

Тестові приклади з діапазоном x / y:

(У не такому читабельному, але кращому форматі, який можна скопіювати, вставити тут, на пастібі .)

Circle:     
[-1, 0, 1]   [-2,2]   [-2,2]
[ 0, 0, 0]
[ 1, 0, 0]

Ellipse:
[-1, 0, 1]   [-2,2]   [-1,1]
[ 0, 0, 0]
[ 2, 0, 0]

Cross:
[ 0, 0]      [-1,2]   [-2,1]
[ 0, 1]

Parabola:
[ 0,-1]      [-1,3]   [-2,2]
[ 0, 0]
[ 1, 0]

Elliptic Curve:
[-1, 1, 0,-1]    [-2,2]   [-3,3]
[ 0, 0, 0, 0]  
[ 1, 0, 0, 0]  

Folium of Descartes:
[  0,  0,  0,  1]    [-3,3]   [-3,3]
[  0, -3,  0,  0]
[  0,  0,  0,  0]
[  1,  0,  0,  0]

Lemniscate:
[  0,  0, -1,  0,  1]    [-2,2]   [-1,1]
[  0,  0,  0,  0,  0]
[  1,  0,  0,  0,  0]

Trifolium:
[ 0, 0, 0,-1, 1]    [-1,1]   [-1,1]
[ 0, 0, 0, 0, 0]
[ 0, 3, 2, 0, 0]
[ 0, 0, 0, 0, 0]
[ 1, 0, 0, 0, 0]

Astroid:
[ -1,  0,  3,  0, -3,  0,  1]    [-1,1]   [-1,1]
[  0,  0,  0,  0,  0,  0,  0]
[  3,  0, 21,  0,  3,  0,  0]
[  0,  0,  0,  0,  0,  0,  0]
[ -3,  0,  3,  0,  0,  0,  0]
[  0,  0,  0,  0,  0,  0,  0]
[  1,  0,  0,  0,  0,  0,  0]

Я отримав натхнення для деяких кривих цього PDF-файлу.


Чи означає " Вам не потрібно турбуватися про згладжування ", що ми можемо просто пофарбувати кожен піксель відповідно до того, чи лежить його центр на лінії?
Пітер Тейлор

Я не бачу зв’язку з псевдонімом. Але ні, повинна бути безперервна лінія, що розділяє регіони різних знаків.
недолік

Матриця не mx n, а (m+1)x (n+1). Що ми беремо як вхідні дані: m, nабо m+1,n+1? Або ми можемо вибрати?
Луїс Мендо

Чи можемо ми просто показати захоплену функцію в новому вікні?
Р. Кап

1
@LuisMendo Так, вісь може бути в будь-якому напрямку, яке вам подобається. (Поки вони ортогональні =)
недолік

Відповіді:


10

Haskell, 283 275 байт

Функцію gслід викликати матрицею та двома діапазонами як аргументи. Матриця - це лише список списків, діапазон яких - це список двох елементів.

import Data.List
t=transpose
u=tail
z=zipWith
l%x=sum$z(*)l$iterate(*x)1                                   --generate powers and multiply with coefficients
e m y x=[l%x|l<-m]%y                                         --evaluate the encoded polynomial
a#b=[a,a+(b-a)/102..b]                                       --create a range
g m[u,w][i,j]=unlines$v[map((0<).e m y)$u#w|y<-i#j]          --evaluate the function on the grid, get the sign
f g=u[u$u$map fst$scanl(\(r,l)c->(c==l,c))(1<0,1<0) l|l<-g]  --find +- or -+ transitions within lines
a&b|a&&b=' '|0<1='#'                                         --helper function for creating the string
v g=z(z(&))(f g)(t$f$t g)                                    --create the string

Ось результати для більш цікавих випадків: Зауважте, що мені довелося зменшити результат від 100x100 до приблизно 40x40, щоб він підходив до консолі (просто змініть жорсткий код 102 на меншу кількість). Також зауважте, що вісь y спрямована вниз.


Тут ви можете зробити кілька гарних маленьких гольфів. Останній рядок використовує паролі, коли він міг би використовуватись $для збереження байта. Обидва місця, де ви користуєтесь, mapмогли бути (<$>), і оскільки ви використовуєте лише eодин раз, ви можете втягнути (0<)всередину, це визначення. Також eможна було б назвати, (!)щоб зберегти 3 байти.
Опублікувати Rock Garf Hunter

А інфіксація zу визначенні vдозволяє дозволяє позбутися 4 дужок (навколо z(&)та f g).
Опублікувати Rock Garf Hunter

Ви також можете перейменувати #на один символ (наприклад s) і мати його відповідність у списках замість g. (наприклад s[a,b]=[a,a+(b-a)/102..b];g m u i=unlines$v[m!y<$>s u|y<-s i])
Пост Рок-Гарф Мисливець

6

Матлаб, 114 100 92 байт

Правильний інструмент для роботи? Я використовую цікавий спосіб, який робить Matlab, printfщоб генерувати многочлен як рядок. Цей поліном може бути наданий, до ezplotякого будується неявна крива на зазначеній області. Для читабельності код надається новими рядками після; яка не потрібна і не зараховується до розміру.

function P(A,W,H,h,w)
t=0:h*w-1;
ezplot(sprintf('+%d*x^%.0f*y^%d',[A(:)';t/h;rem(t,h)]),[W,H])

Прогрес в гольфі як розширюваний фрагмент.


Виведення тестових випадків (натисніть для повного перегляду): Тестові справи


2
Дійсно приємне рішення з використанням sprintf/ezplot!
недолік

Використання fixзамість цього floorможе допомогти вам досягти двозначного байтового числа :-)
Луїс Мендо

Ви також можете використати [h,w]=size(A);t=0:h*w-1;для збереження ще три байти!
flawr

@LuisMendo Насправді я можу краще. Мені було сумно, що в printf Matlab немає цілого заповнювача, але він все ще підтримує такі речі %.0f. Це означає, що я можу взагалі скинути підлогу і дозволити printfце виправити!
algmyr

@flawr Я використовую другу частину цього в наступних ітераціях. Я розумію, що моє форматування з найкращою версією останньої не було абсолютно очевидним. Відредаговане форматування, щоб зробити це кристально чистим.
algmyr

6

Python 2, 261 байт

E=enumerate
M,[a,c],[b,d]=input()
e=(c-a)/199.
I=200
J=-int((b-d)/e-1)
print'P2',I,J,255
i=I*J
while i:i-=1;x,y=c-i%I*e,b+i/I*e;u,v,w=map(sum,zip(*((z*p/x,z*q/y,z)for q,R in E(M)for p,t in E(R)for z in[t*x**p*y**q])));print int(255*min(1,(w*w/(u*u+v*v))**.5/e))

Формат введення: matrix,xbounds,ybounds(наприклад [[-1,0,1],[0,0,0],[1,0,0]],[-2,2],[-2,2]). Формат виводу: звичайний PGM .

Це оцінює відстань від кожного піксельного центру до кривої, використовуючи наближення першого порядку d ( x , y ) = | p ( x , y ) | / | ∇ p ( x , y ) |, де ∇ p - градієнт многочлена p . (Це відстань від ( x , y ) до перетину дотичної площини в ( x , y , p ( x , y )) з xy .) Тоді пікселі, де d (x , y) менше однієї піксельної ширини кривої, пропорційно d ( x , y ), в результаті чого виникають приємні антиаліялізовані лінії (навіть якщо це не є вимогою).

вихід

Ось такі ж графіки з функцією відстані, поділеною на 16, щоб зробити її видимою.


Зачекайте, то де ж у коді відбувається власне графічне накреслення?
Р. Кап

@ R.Kap Код записує зображення у звичайному форматі PGM для stdout. Є одне printтвердження для заголовка зображення та одне printтвердження у whileциклі для значення кожного пікселя.
Anders Kaseorg

Нічого собі, це дійсно круто! Ви б не хотіли трохи детальніше розглянути свій алгоритм побудови графіків?
недолік

@flawr Я трохи розширив пояснення; це відповідає на ваші запитання?
Андерс Касеорг

@AndersKaseorg Так, дуже дякую!
недолік

5

Python 3.5 + MatPlotLib + Numpy, 352 байти:

from matplotlib.pyplot import*;from numpy import*
def R(M,S,U,r=range):N=linspace;E='+'.join([str(y)+'*'+m for y,m in[q for i,g in zip(M,[[i+'*'+p for p in['1']+['x^%d'%p for p in r(1,len(M[0]))]]for i in['1']+['y^%d'%i for i in r(1,len(M))]])for q in zip(i,g)if q[0]]]);x,y=meshgrid(N(*S,200),N(*U,200));contour(x,y,eval(E.replace('^','**')),0);show()

Названа функція. Досить довго, але ей, я просто щасливий, що зміг виконати завдання. Бере 3 входи - це m by nматриця, x-range і y-range, які мають бути в масивах (наприклад,[[-1,0,1],[0,0,0],[1,0,0]],[-2,2],[-2,2] ). Виводить готовий графік у новому графічному інтерактивному вікні. Я буду грати в гольф більше часу, коли можу, але наразі я задоволений цим.

Кінцеві результати для тестових випадків:

Кінцевий результат


5

MATL , 67 61 байт

8Wt:qwq/t2:"wid*2M1)+i:q!^]!2&!w[1IK2]&!**ss&eZS5Y62&Y+|4=0YG

Цей код працює у версії 18.5.0 мови, що передує виклику. Input використовує Факультативний m, nпараметри. Матриця має крапки з комою як роздільники рядків. Точний формат введення (використовуючи параболу як приклад)

[-1,3]
3  
[-2,2]
2
[0,-1; 0, 0; 1, 0]

Код створює зображення розміром 255 × 255. Це можна перевірити з допомогою @Suever «S MATL Інтернет компілятор, який, серед інших дуже цікавих функцій, включає в себе графічний висновок. Див. Наприклад

Цей компілятор ще знаходиться в експериментальній стадії. Повідомте про будь-які проблеми до @Suever у чаті MATL . Якщо кнопка "Запустити" не працює, спробуйте оновити сторінку та натисніть ще раз.

Якщо ви віддаєте перевагу виводу ASCII , код потрібно трохи змінити (зміни стосуються лише перших двох та останніх чотирьох символів коду вище):

101t:qwq/t2:"wid*2M1)+i:q!^]!2&!w[1IK2]&!**ss&eZS5Y62&Y+|4<42*c

Це створює сітку 100 × 100 ASCII, яка використовує символ *для зображення кривої. Ви також можете перевірити це за допомогою @Dennis ' Спробуйте в Інтернеті! платформа:

Зауважте, що співвідношення сторін виходу ASCII змінено, оскільки символи трохи вище, ніж широкі.

Пояснення

Код спочатку обчислює двочленний многочлен на сітці x - y . Це широко використовує мовлення , обчислюючи проміжний 4D масив, де кожен вимір представляє значення x, значення y , x експоненти, y експоненти відповідно.

З цієї функції обчислюється лінія нульового рівня. Оскільки виклик передбачає, що потрібно виявляти лише зміни знаків, код застосовує 2D згортку з блоком 2 × 2 і позначає піксель як належний рядку, якщо не чотири значення блоку мають однаковий знак.

8W      % Push 2^8, that is, 256. (The ASCII-output version pushes 101 instead)
t:q     % Duplicate. Push range [0 1 ... 255]
wq      % Swap. Subtract 1 to obtain 255
/       % Divide. Gives normalized range [0 1/255 2/255... 1]
t       % Duplicate
2:"     % For loop: do this twice
  w     %   Swap top two elements in the stack
  i     %   Input two-number array defining x range (resp. y in second iteration)
  d     %   Difference of the two entries
  *     %   Multiply by normalized range
  2M1)  %   Push the array again and get its first entry
  +     %   Add. This gives the range for x values (resp. y)
  i     %   Input m (n in second iteration)
  :q    %   Range [0 1 ...m-1] (resp. [0 1 ...n-1])
  !     %   Convert to column array
  ^     %   Power, element-wise with broadcast. This gives a matrix of size m×256
        %   (resp. n×256) of powers of x (resp. y) for the range of values computed
        %   previously
]       % End for loop
!       % Transpose. This transforms the n×256 matrix of powers of y into 256×n
2       % Push 2
&!      % Permute dimensions 1 and 3: transforms the 256×n matrix into a 4D array
        % of size 1×n×256×1
w       % Swap top two elements in the stack: bring 256×m matrix to top
[1IK2]  % Push vector [1 3 4 2]
&!      % Permute dimensions as indicated by the vector: transforms the m×256 matrix
        % into a 4D array of size m×1×1×256
*       % Multiply element-wise with broadcast: gives 4D array of size m×n×256×256
        % with mixed powers of x and y for at the grid of x, y values
*       % Implicitly input m×n matrix. Multiply element-wise with broadcast: gives
        % 4D array of size m×n×256×256
ss      % Sum along first two dimensions: gives 4D array of size 1×1×256×256
&e      % Squeeze singleton dimensions: gives matrix of size 256×256. This is the
        % two-variable polynomial evaluated at the x, y grid.
        % Now we need to find the zero level curve of this function. We do this by 
        % detecting when the sign of the function changes along any of the two axes
ZS      % Matrix of sign values (1, 0 or -1)
5Y6     % Predefined literal: matrix [1 1; 1 1]
2&Y+    % Compute 2D convolution, keeping only the valid (central) part
|4=     % True if absolute value of result is 4, which indicates no sign changes.
        % (The ASCII version computes a negated version of this, for better display)
0YG     % Display as image. (The ASCII-output version does the following instead:
        % multiply by 42 and convert to char. 42 is ASCII for '*', and character 0 
        % is shown as space. The 2D char array is then implicitly displayed)

Усі тестові справи

Тут ви знайдете всі матеріали у відповідному форматі, якщо ви хочете спробувати:

Circle:
[-2,2]
3
[-2,2]
3
[-1, 0, 1; 0, 0, 0; 1, 0, 0]

Ellipse:
[-2,2]
3
[-1,1]
3
[-1, 0, 1; 0, 0, 0; 2, 0, 0]

Cross:
[-1,2]
2
[-2,1]
2
[0, 0; 0, 1]

Parabola:
[-1,3]
3  
[-2,2]
2
[0,-1; 0, 0; 1, 0]

Elliptic Curve:
[-2,2]
3
[-3,3]
4
[-1, 1, 0,-1; 0, 0, 0, 0; 1, 0, 0, 0]

Folium of Descartes:
[-3,3]
4
[-3,3]
4
[0,  0,  0,  1; 0, -3,  0,  0; 0,  0,  0,  0; 1,  0,  0,  0]


Lemniscate:
[-2,2]
3
[-1,1]
5
[0,  0, -1,  0,  1; 0,  0,  0,  0,  0; 1,  0,  0,  0,  0]

Trifolium:
[-1,1]
5
[-1,1]
5
[0, 0, 0,-1, 1; 0, 0, 0, 0, 0; 0, 3, 2, 0, 0; 0, 0, 0, 0, 0; 1, 0, 0, 0, 0]

Astroid
[-1,1]
7
[-1,1]
7
[-1,  0,  3,  0, -3,  0,  1; 0,  0,  0,  0,  0,  0,  0; 3,  0, 21,  0,  3,  0,  0; 0,  0,  0,  0,  0,  0,  0; -3,  0,  3,  0,  0,  0,  0; 0,  0,  0,  0,  0,  0,  0; 1,  0,  0,  0,  0,  0,  0]

2
Ще легше читати, ніж Perl. Чудова робота, також приємний онлайн-компілятор!
flawr

@flawr легше читати, ніж Perl LOL. Що стосується онлайн-компілятора, то це все робота Suever!
Луїс Мендо

1
@flawr Тепер із згорткою!
Луїс Мендо

1
<3 згортання!
недолік
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.