Máy vẽ đường cong đại số


14

Đường cong đại số là một "tập hợp con 1D" nhất định của "mặt phẳng 2D" có thể được mô tả là tập hợp các số không {(x,y) in R^2 : f(x,y)=0 }của một đa thức f. Ở đây chúng ta coi mặt phẳng 2D là mặt phẳng thật R^2để chúng ta có thể dễ dàng tưởng tượng đường cong như vậy có thể trông như thế nào, về cơ bản là một thứ bạn có thể vẽ bằng bút chì.

Ví dụ:

  • 0 = x^2 + y^2 -1 một vòng tròn bán kính 1
  • 0 = x^2 + 2y^2 -1 một hình elip
  • 0 = xy một hình chữ thập , về cơ bản là sự kết hợp của trục x và trục y
  • 0 = y^2 - x một parabola
  • 0 = y^2 - (x^3 - x + 1)một đường cong elip
  • 0 = x^3 + y^3 - 3xy folium của Descartes
  • 0 = x^4 - (x^2 - y^2) một quả chanh
  • 0 = (x^2 + y^2)^2 - (x^3 - 3xy^2) một trifolium
  • 0 = (x^2 + y^2 - 1)^3 + 27x^2y^2 một khối u

Bài tập

Đưa ra một đa thức f(như được xác định bên dưới) và phạm vi x / y, xuất ra hình ảnh đen trắng có ít nhất 100x100 pixel hiển thị đường cong dưới dạng đường màu đen trên nền trắng.

Chi tiết

Màu sắc : Bạn có thể sử dụng bất kỳ hai màu nào khác mà bạn chọn, thật dễ dàng để phân biệt chúng.

Cốt truyện : Thay vì hình ảnh pixel, bạn cũng có thể xuất hình ảnh này dưới dạng nghệ thuật, trong đó "pixel" nền phải là khoảng trắng / gạch chân hoặc một ký tự khác "trông trống rỗng" và dòng có thể được tạo từ một ký tự trông " đầy đủ" như Mhoặc Xhoặc #.

Bạn không phải lo lắng về răng cưa.

Bạn chỉ cần vẽ các đường trong đó dấu của đa thức thay đổi từ một bên của dòng này sang bên kia (có nghĩa là bạn có thể sử dụng thuật toán diễu hành vuông), bạn không phải vẽ chính xác "các trường hợp bệnh lý như 0 = x^2khi dấu hiệu đó xảy ra không thay đổi khi đi từ bên này sang bên kia. Nhưng đường nên liên tục và tách các vùng của các dấu hiệu khác nhau f(x,y).

Đa thức : Đa thức được đưa ra dưới dạng (m+1) x (n+1)ma trận / danh sách các danh sách các hệ số (thực), trong ví dụ bên dưới các thuật ngữ của các hệ số được đưa ra ở vị trí của chúng:

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

Nếu bạn thích, bạn có thể giả sử ma trận là hình vuông (luôn có thể được thực hiện với phần đệm không cần thiết) và nếu bạn muốn, bạn cũng có thể giả sử rằng kích thước của ma trận được đưa ra làm đầu vào có tính chất quảng cáo.

Trong phần sau đây, các ví dụ từ trên được biểu diễn dưới dạng ma trận được định nghĩa như sau:

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]

Các trường hợp thử nghiệm với x-Range / y-Range:

(Trong một định dạng không thể đọc nhưng sao chép tốt hơn có thể có ở đây trên pastebin .)

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]

Tôi đã có cảm hứng cho một số đường cong từ pdf này.


Có phải " Bạn không phải lo lắng về việc khử răng cưa " có nghĩa là chúng ta chỉ có thể tô màu từng pixel theo liệu trung tâm của nó có nằm trên đường thẳng không?
Peter Taylor

Tôi không thấy mối liên hệ với răng cưa. Nhưng không, nên có một đường liên tục ngăn cách các khu vực có dấu hiệu khác nhau.
flawr

Ma trận không phải là mx n, mà là (m+1)x (n+1). Chúng ta lấy gì làm đầu vào : m, n, hay m+1,n+1? Hay chúng ta có thể chọn?
Luis Mendo

Chúng ta có thể chỉ hiển thị chức năng đồ thị trong một cửa sổ mới không?
R. Kap

1
@LuisMendo Có, trục có thể theo bất kỳ hướng nào bạn muốn. (Miễn là chúng trực giao =)
flawr

Câu trả lời:


10

Haskell, 283 275 byte

Hàm gnên được gọi với ma trận và hai phạm vi làm đối số. Ma trận chỉ là một danh sách các danh sách, các phạm vi mỗi danh sách hai thành phần.

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

Ở đây, các kết quả đầu ra cho các trường hợp thú vị hơn: Lưu ý rằng tôi phải thu nhỏ kích thước từ 100x100 xuống khoảng 40x40 sao cho phù hợp với giao diện điều khiển (chỉ cần thay đổi 102 mã hóa cứng thành số nhỏ hơn). Cũng lưu ý rằng trục y đang hướng xuống dưới.


Có một vài sân golf nhỏ bạn có thể làm ở đây. Dòng cuối cùng sử dụng parens khi nó có thể sử dụng $để lưu một byte. Cả hai nơi bạn sử dụng đều mapcó thể (<$>)và vì bạn chỉ sử dụng emột lần nên bạn có thể kéo theo (0<)định nghĩa bên trong. Cũng ecó thể được đặt tên (!)để lưu 3 byte.
Đăng Rock Garf Hunter

Và hòa trộn ztrong định nghĩa vcho phép bạn thoát khỏi 4 dấu ngoặc đơn (xung quanh z(&)f g).
Đăng Rock Garf Hunter

Bạn cũng có thể đổi tên #thành một ký tự (ví dụ s) và để mẫu đó khớp với danh sách thay vì g. (ví dụ s[a,b]=[a,a+(b-a)/102..b];g m u i=unlines$v[m!y<$>s u|y<-s i])
Post Rock Garf Hunter

6

Matlab, 114 100 92 byte

Công cụ phù hợp cho công việc? Tôi sử dụng cách thú vị mà Matlab làm printfđể tạo ra một đa thức dưới dạng một chuỗi. Đa thức này có thể được cung cấp để ezplotvẽ đồ thị đường cong ẩn trên miền đã chỉ định. Để dễ đọc, mã được trình bày với các dòng mới sau; không cần thiết và không được tính vào kích thước.

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

Golf tiến bộ như đoạn mở rộng.


Đầu ra của các trường hợp thử nghiệm (bấm vào để xem toàn bộ): Các trường hợp thử nghiệm


2
Giải pháp thực sự tốt đẹp bằng cách sử dụng sprintf/ezplot!
flawr

Sử dụng fixthay vì floorcó thể giúp bạn đạt được số byte gồm hai chữ số :-)
Luis Mendo

Bạn cũng có thể sử dụng [h,w]=size(A);t=0:h*w-1;để lưu ba byte khác!
flawr

@LuisMendo Thật ra, tôi có thể làm tốt hơn. Tôi đã buồn khi printf của Matlab không có chỗ giữ số nguyên, nhưng nó vẫn hỗ trợ những thứ như %.0f. Điều đó có nghĩa là tôi có thể thả sàn hoàn toàn và để printfsửa chữa nó!
algmyr

@flawr Tôi sử dụng phần thứ hai trong các lần lặp lại sau. Tôi nhận ra định dạng của mình với phiên bản tốt nhất vừa qua không hoàn toàn rõ ràng. Chỉnh sửa định dạng để làm cho tinh thể này rõ ràng.
algmyr

6

Python 2, 261 byte

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

Định dạng đầu vào: matrix,xbounds,ybounds(ví dụ [[-1,0,1],[0,0,0],[1,0,0]],[-2,2],[-2,2]). Định dạng đầu ra: PGM đơn giản .

Điều này ước tính khoảng cách từ mọi trung tâm pixel đến đường cong bằng cách sử dụng xấp xỉ bậc một d ( x , y ) = | p ( x , y ) | / | ∇ p ( x , y ) |, trong đó p là độ dốc của đa thức p . (Đây là khoảng cách từ ( x , y ) đến giao điểm của mặt phẳng tiếp tuyến tại ( x , y , p ( x , y )) với xy thủy tinh.) Sau đó, các pixel trong đó d (x , y ) nhỏ hơn một pixel chiều rộng của đường cong tỷ lệ với d ( x , y ), dẫn đến các đường khử răng cưa đẹp (mặc dù đó không phải là một yêu cầu).

đầu ra

Dưới đây là các biểu đồ tương tự với hàm khoảng cách chia cho 16 để hiển thị.


Đợi đã, vậy ở đâu trong mã mà âm mưu đồ họa thực tế xảy ra?
R. Kap

@ R.Kap Mã này viết một hình ảnh ở định dạng PGM đơn giản vào thiết bị xuất chuẩn. Có một printcâu lệnh cho tiêu đề hình ảnh và một printcâu lệnh trong whilevòng lặp cho giá trị của từng pixel.
Anders Kaseorg

Chà, nó thật là tuyệt! Bạn có muốn đi sâu hơn một chút về thuật toán vẽ của bạn không?
flawr

@flawr Tôi đã mở rộng giải thích một chút; điều đó có trả lời câu hỏi của bạn không?
Anders Kaseorg

@AndersKaseorg Vâng, cảm ơn bạn rất nhiều!
flawr

5

Python 3.5 + MatPlotLib + Numpy, 352 byte:

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

Một chức năng được đặt tên. Khá lâu, nhưng này, tôi rất vui vì tôi đã có thể hoàn thành nhiệm vụ. Có 3 đầu vào, đó là m by nma trận, sắp xếp xvà sắp xếp y, tất cả sẽ nằm trong mảng (ví dụ [[-1,0,1],[0,0,0],[1,0,0]],[-2,2],[-2,2]:). Xuất ra biểu đồ đã hoàn thành trong một cửa sổ tương tác mới, đồ họa. Sẽ chơi golf này nhiều thời gian hơn khi tôi có thể, nhưng bây giờ, tôi hài lòng với nó.

Đầu ra cuối cùng cho các trường hợp thử nghiệm:

Đầu ra cuối cùng


5

MATL , 67 61 byte

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

Mã này chạy trong bản phát hành 18.5.0 của ngôn ngữ, trước thách thức. Đầu vào sử dụng các tùy chọn m, ntham số. Ma trận có dấu chấm phẩy là dấu phân cách hàng. Định dạng đầu vào chính xác (sử dụng parabola làm ví dụ) là

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

Mã này tạo ra một hình ảnh với kích thước 255 × 255. Điều này có thể được kiểm tra bằng trình biên dịch MATL Online của @Suever , trong số các tính năng rất thú vị khác, bao gồm đầu ra đồ họa. Xem ví dụ

Trình biên dịch này vẫn đang trong giai đoạn thử nghiệm. Vui lòng báo cáo bất kỳ vấn đề nào với @Suever trong phòng chat MATL . Nếu nút "Chạy" không hoạt động, hãy thử làm mới trang và nhấp lại.

Nếu bạn thích đầu ra ASCII , mã cần được sửa đổi một chút (các thay đổi chỉ ảnh hưởng đến hai ký tự đầu tiên và bốn ký tự cuối cùng của mã ở trên):

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

Điều này tạo ra lưới ASCII 100 × 100 sử dụng ký tự *để biểu thị đường cong. Bạn cũng có thể kiểm tra điều này với @Dennis ' Dùng thử trực tuyến! nền tảng:

Lưu ý rằng tỷ lệ khung hình của đầu ra ASCII bị thay đổi do các ký tự cao hơn một chút so với chiều rộng.

Giải trình

Mã đầu tiên tính đa thức hai biến trên lưới x - y . Điều này làm cho việc sử dụng nặng của phát thanh truyền hình , tính toán một mảng 4D trung gian nơi mỗi chiều đại diện cho x giá trị, y giá trị, x mũ, y số mũ tương ứng.

Từ hàm đó, dòng mức 0 được tính toán. Do thử thách xác định chỉ cần phát hiện các thay đổi dấu hiệu, nên mã áp dụng tích chập 2D với khối 2 × 2 và đánh dấu một pixel là thuộc về dòng nếu không phải bốn giá trị của khối có cùng dấu.

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)

Tất cả các trường hợp thử nghiệm

Dưới đây là tất cả các đầu vào ở định dạng phù hợp, trong trường hợp bạn muốn thử:

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
Vẫn dễ đọc hơn Perl. Công việc tuyệt vời, cũng là một trình biên dịch trực tuyến tốt đẹp!
flawr

@flawr dễ đọc hơn Perl LOL. Đối với trình biên dịch trực tuyến, tất cả đều là công việc của Suever!
Luis Mendo

1
@flawr Bây giờ với tích chập!
Luis Mendo

1
<3 chập!
flawr
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.