Tạo ra fractals Newton


24

Tất cả các bạn đều biết phương pháp Newton để tính gần đúng gốc của hàm, phải không? Mục tiêu của tôi trong nhiệm vụ này là giới thiệu cho bạn một khía cạnh thú vị của thuật toán này.

Thuật toán của Newton chỉ hội tụ cho một số nhất định, nhưng hầu hết tất cả các giá trị đầu vào phức tạp. Nếu bạn hình dung sự hội tụ của phương thức cho tất cả các giá trị đầu vào trên mặt phẳng phức, bạn thường nhận được một fractal đẹp như thế này:

Newton fractal cho f (x) = x ^ 3-1 Hình ảnh từ Wikimedia commons

Thông số kỹ thuật

Mục tiêu của nhiệm vụ này là, để tạo ra các fractals như vậy. Điều này có nghĩa là, bạn lấy một đa thức làm đầu vào và phải in ra fractal tương ứng dưới dạng hình ảnh theo định dạng mà bạn chọn làm đầu ra.

Đầu vào

Đầu vào là một danh sách các số phức được phân tách bằng khoảng trắng. Chúng được viết theo phong cách <Real part><iImaginary part>, như số này : 5.32i3.05. Bạn có thể giả sử rằng số đầu vào không quá 4 chữ số thập phân và nhỏ hơn 1000. Đầu tiên trong số chúng không được bằng không. Ví dụ: đây có thể là một đầu vào cho chương trình của bạn:

1 -2i7,5 23.0004i-3,8 i12 0 5.1233i0.1

Các số được hiểu là các hệ số của một đa thức, bắt đầu với công suất cao nhất. Trong suốt phần còn lại của đặc tả này, các đa thức đầu vào được gọi là P . Đầu vào trên bằng với đa thức này:

f (x) = x 5 + (-2 + 7,5 i ) x 4 + (23.0004 - 3,8 i ) x 3 + 12 i x 2 + 5.1233 + 0.1 i

Đầu vào có thể đến với bạn từ stdin, từ một đối số được truyền cho chương trình hoặc từ một dấu nhắc được hiển thị cho chương trình của bạn. Bạn có thể giả sử rằng đầu vào không chứa bất kỳ ký tự khoảng trắng đầu hoặc cuối nào.

Kết xuất

Bạn phải kết xuất fractal theo cách sau:

  • Chọn nhiều màu như gốc của P cộng với một màu phụ cho phân kỳ
  • Đối với mỗi số trong mặt phẳng nhìn thấy, xác định xem phương thức có hội tụ không và nếu có với gốc nào. Tô màu điểm theo kết quả.
  • Không in thước kẻ hoặc những thứ ưa thích khác
  • In một điểm đen tại các điểm, đó là các gốc đa thức để định hướng. Bạn có thể in tối đa bốn pixel xung quanh mỗi gốc.
  • Tìm cách chọn mặt phẳng nhìn thấy theo cách, rằng tất cả các rễ đều có thể phân biệt và lan rộng khắp nó, nếu có thể. Mặc dù không yêu cầu vị trí hoàn hảo của khung đầu ra, tôi có quyền từ chối chấp nhận câu trả lời chọn khung theo cách không được chấp nhận, ví dụ: luôn luôn ở cùng tọa độ, tất cả các gốc nằm trong một điểm, v.v.
  • Hình ảnh đầu ra phải có kích thước 1024 * 1024 pixel.
  • Thời gian kết xuất tối đa là 10 phút
  • Sử dụng các giá trị dấu phẩy động chính xác duy nhất là đủ

Đầu ra

Đầu ra phải là hình ảnh đồ họa raster ở định dạng tệp bạn chọn, có thể đọc được bằng phần mềm tiêu chuẩn cho hệ điều hành thương hiệu X. Nếu bạn muốn sử dụng một định dạng hiếm, hãy xem xét thêm một liên kết đến một trang web nơi người ta có thể tải xuống một trình xem cho nó.

Xuất tập tin ra thiết bị xuất chuẩn. Nếu ngôn ngữ của bạn không hỗ trợ đưa thứ gì đó vào thiết bị xuất chuẩn hoặc nếu bạn thấy tùy chọn này không thuận tiện, hãy tìm một cách khác. Theo bất kỳ cách nào, nó phải có thể lưu hình ảnh được tạo ra.

Hạn chế

  • Không có thư viện xử lý ảnh
  • Không có thư viện tạo fractal
  • Mã ngắn nhất sẽ thắng

Tiện ích mở rộng

Nếu bạn thích nhiệm vụ này, bạn có thể thử tô màu các điểm theo tốc độ hội tụ hoặc một số tiêu chí khác. Tôi muốn thấy một số kết quả thú vị.


6
Tôi không hoàn toàn chắc chắn liệu điều này có phù hợp như một môn đánh gôn không. Trong mắt tôi nhiệm vụ quá phức tạp. Tôi có thể được chứng minh là sai, mặc dù.
Joey

5
@Joey: Thật vậy. Bản thân tôi muốn đây là một thử thách mã .
Joey Adams

2
... Hoặc PPM cho vấn đề đó.
Joey

1
@Joey: Ý định của tôi là tạo ra một nhiệm vụ khá khó khăn, vì nhiều người không thích những nhiệm vụ rất dễ dàng.
FUZxxl

1
Nó dễ dàng được chia thành các nhiệm vụ riêng biệt và nếu ngôn ngữ của bạn hỗ trợ các số dấu phẩy động phức tạp thì bạn có thể tiết kiệm được một khối lớn. Tôi có một phiên bản không hoàn toàn chơi gôn với 1600 ký tự, trong đó 340 là lớp số phức. Nó chưa xác định được gốc rễ và sử dụng màu sắc, nhưng tôi đang cố gắng theo dõi những gì tôi cho là lỗi trong mã NR. (Tìm kiếm gốc của x ^ 3-1 bắt đầu từ -0,5 + 0,866i chắc chắn không nên phân kỳ!)
Peter Taylor

Câu trả lời:


13

Python, 827 777 ký tự

import re,random
N=1024
M=N*N
R=range
P=map(lambda x:eval(re.sub('i','+',x)+'j'if 'i'in x else x),raw_input().split())[::-1]
Q=[i*P[i]for i in R(len(P))][1:]
E=lambda p,x:sum(x**k*p[k]for k in R(len(p)))
def Z(x):
 for j in R(99):
  f=E(P,x);g=E(Q,x)
  if abs(f)<1e-9:return x,1
  if abs(x)>1e5or g==0:break
  x-=f/g
 return x,0
T=[]
a=9e9
b=-a
for i in R(999):
 x,f=Z((random.randrange(-9999,9999)+1j*random.randrange(-9999,9999))/99)
 if f:a=min(a,x.real,x.imag);b=max(b,x.real,x.imag);T+=[x]
s=b-a
a,b=a-s/2,b+s/2
s=b-a
C=[[255]*3]*M
H=lambda x,k:int(x.real*k)+87*int(x.imag*k)&255
for i in R(M):
 x,f=Z(a+i%N*s/N+(a+i/N*s/N)*1j)
 if f:C[i]=H(x,99),H(x,57),H(x,76)
for r in T:C[N*int(N*(r.imag-a)/s)+int(N*(r.real-a)/s)]=0,0,0
print'P3',N,N,255
for c in C:print'%d %d %d'%c

Tìm giới hạn hiển thị (và gốc) bằng cách tìm các điểm hội tụ cho một loạt các mẫu ngẫu nhiên. Sau đó, nó vẽ biểu đồ bằng cách tính các điểm hội tụ cho từng điểm bắt đầu và sử dụng hàm băm để có được màu sắc ngẫu nhiên cho mỗi điểm hội tụ. Nhìn rất kỹ và bạn có thể thấy rễ được đánh dấu.

Đây là kết quả cho đa thức ví dụ.

kết quả ví dụ đa thức


Tốt Tôi thích điều này.
FUZxxl

14

Java, 1093 1058 1099 1077 ký tự

public class F{double r,i,a,b;F(double R,double I){r=R;i=I;}F a(F c){return
new F(r+c.r,i+c.i);}F m(F c){return new F(r*c.r-i*c.i,r*c.i+i*c.r);}F
r(){a=r*r+i*i;return new F(-r/a,i/a);}double l(F c){a=r-c.r;b=i-c.i;return
Math.sqrt(a*a+b*b);}public static void main(String[]a){int
n=a.length,i=0,j,x,K=1024,r[]=new int[n];String o="P3\n"+K+" "+K+"\n255 ",s[];F z=new
F(0,0),P[]=new F[n],R[]=new F[n],c,d,e,p,q;for(;i<n;)P[i]=new
F((s=a[i++].split("i"))[0].isEmpty()?0:Float.parseFloat(s[0]),s.length==1?0:Float.parseFloat(s[1]));double
B=Math.pow(P[n-1].m(P[0].r()).l(z)/2,1./n),b,S;for(i=1;i<n;){b=Math.pow(P[i].m(P[i-1].r()).l(z),1./i++);B=b>B?b:B;}S=6*B/K;for(x=0;x<K*K;){e=d=c=new
F(x%K*S-3*B,x++/K*S-3*B);for(j=51;j-->1;){p=P[0];q=p.m(new
F(n-1,0));for(i=1;i<n;){if(i<n-1)q=q.m(c).a(P[i].m(new
F(n-1-i,0)));p=p.m(c).a(P[i++]);}c=c.a(d=q.r().m(p));if(d.l(z)<S/2)break;}i=j>0?0:n;for(;i<n;i++){if(R[i]==null)R[i]=c;if(R[i].l(c)<S)break;}i=java.awt.Color.HSBtoRGB(i*1f/n,j<1||e.l(c)<S&&r[i]++<1?0:1,j*.02f);for(j=0;j++<3;){o+=(i&255)+" ";i>>=8;}System.out.println(o);o="";}}}

Đầu vào là đối số dòng lệnh - ví dụ: chạy java F 1 0 0 -1. Đầu ra là thiết bị xuất chuẩn ở định dạng PPM (ASCII pixmap).

Thang đo được chọn sử dụng Fujiwara ràng buộc vào giá trị tuyệt đối của các gốc phức của một đa thức; Sau đó tôi nhân số đó bị ràng buộc bởi 1,5. Tôi điều chỉnh độ sáng bằng tốc độ hội tụ, vì vậy rễ sẽ ở trong các mảng sáng nhất. Do đó, thật hợp lý khi sử dụng màu trắng thay vì màu đen để đánh dấu các vị trí gần đúng của rễ cây (điều này làm tôi mất 41 ký tự cho một thứ thậm chí không thể được thực hiện "chính xác". Nếu tôi gắn nhãn tất cả các điểm hội tụ trong 0,5 pixel của chính chúng sau đó một số gốc xuất hiện không ghi nhãn, nếu tôi gắn nhãn tất cả các điểm hội tụ trong phạm vi 0,6 pixel thì một số gốc xuất hiện được gắn nhãn nhiều hơn một pixel, vì vậy, với mỗi gốc, tôi ghi nhãn điểm đầu tiên gặp phải để hội tụ trong vòng 1 pixel ).

Hình ảnh cho đa thức ví dụ (được chuyển đổi thành png với GIMP): Rễ của x ^ 5 + (- 2 + 7.5i) x ^ 4 + (23.0004-3.8i) x ^ 3 + 12i x ^ 2 + (5.1233 + 0.1i)


@FUZxxl, hình ảnh là từ phiên bản cũ. Tôi sẽ tải lên một cái với tốc độ hội tụ sau. Nhưng vấn đề với việc đánh dấu gốc là xác định pixel nào sẽ đánh dấu. Đây là vấn đề kinh điển mà với dấu phẩy động, bạn không thể sử dụng các bài kiểm tra đẳng thức chính xác, vì vậy bạn phải so sánh với một epsilon. Kết quả là, tôi không có giá trị "chuẩn" cho gốc. Tôi có thể đánh dấu các pixel hội tụ trong một bước, nhưng điều đó không đảm bảo đánh dấu bất cứ điều gì và có thể đánh dấu một khối 4 pixel cho một gốc.
Peter Taylor

@Peter Taylor: Như bạn thấy, Keith Randall cũng tìm ra giải pháp cho vấn đề đó. Tôi đã thêm yêu cầu này như là một khó khăn thêm. Một cách tiếp cận để làm điều đó là tính toán pixel gần nhất cho mỗi gốc và sau đó kiểm tra từng pixel xem có bằng với nó không.
FUZxxl

@FUZxxl, bạn chưa hiểu quan điểm của tôi. "Điểm ảnh gần nhất" của một gốc không được xác định rõ. Tuy nhiên, tôi có thể hack thứ gì đó có thể hoạt động cho tất cả các trường hợp thử nghiệm mà bạn ném vào nó và tôi có ấn tượng rằng điều đó sẽ khiến bạn hài lòng. Tôi sẽ tô màu trắng, không phải màu đen, vì điều đó hợp lý hơn.
Peter Taylor

@Peter Taylor: Được rồi.
FUZxxl

6
Ảnh hồ sơ của tôi sẽ sớm thay đổi x^6-9x^3+8, được thiết kế cẩn thận bằng cách chọn gốc và sau đó sử dụng Wolfram Alpha để đơn giản hóa đa thức. Ok, tôi đã gian lận bằng cách hoán đổi màu sắc xung quanh sau đó trong GIMP.
Peter Taylor

3

Python, 633 byte

import numpy as np
import matplotlib.pyplot as plt
from colorsys import hls_to_rgb
def f(z):
    return (z**4 - 1)
def df(z):
    return (4*z**3) 
def cz(z):
    r = np.abs(z)
    arg = np.angle(z)   
    h = (arg + np.pi)  / (3 * np.pi)
    l = 1.0 - 1.0/(1.0 + r**0.1)
    s = 0.8 
    c = np.vectorize(hls_to_rgb) (h,l,s)
    c = np.array(c)
    c = c.swapaxes(0,2) 
    return c    
x, y = np.ogrid[-1.5:1.5:2001j, -1.5:1.5:2001j]
z = x + 1j*y    
for i in range(10):
    z -= (f(z) / df(z))
zz = z
zz[np.isnan(zz)]=0
zz=cz(zz)
plt.figure()
plt.imshow(zz, interpolation='nearest')
plt.axis('off')
plt.savefig('plots/nf.svg')
plt.close()

Sau khi tăng tốc và làm đẹp (756 byte)

import numpy as np
from numba import jit
import matplotlib.pyplot as plt
from colorsys import hls_to_rgb 

@jit(nopython=True, parallel=True, nogil=True)
def f(z):
    return (z**4 - 1)   

@jit(nopython=True, parallel=True, nogil=True)
def df(z):
    return (4*z**3) 

def cz(z):
    r = np.abs(z)
    arg = np.angle(z)   

    h = (arg + np.pi)  / (3 * np.pi)
    l = 1.0 - 1.0/(1.0 + r**0.1)
    s = 0.8 

    c = np.vectorize(hls_to_rgb) (h,l,s)
    c = np.array(c)
    c = c.swapaxes(0,2) 
    return c    

x, y = np.ogrid[-1.5:1.5:2001j, -1.5:1.5:2001j]
z = x + 1j*y    

for i in range(10):
    z -= (f(z) / df(z))

zz = z
zz[np.isnan(zz)]=0
zz=cz(zz)
plt.figure()
plt.imshow(zz, interpolation='nearest')
plt.axis('off')
plt.savefig('plots/nf.svg')
plt.close()

Biểu đồ dưới đây dành cho hàm Newton Fractal of log (z).

Newton Fractal cho log (z)


Bạn có thể sử dụng tên ngắn hơn (1 char) và xóa khoảng trắng bằng cách kết hợp nhiều dòng bằng cách sử dụng ;. Ngoài ra, loại bỏ tất cả các không gian có thể.
mbomb007

Một số golf thông thường giảm điều này xuống chỉ còn 353 byte ! Không thử nghiệm nó (không có matplotlibở đây), vì vậy không đảm bảo rằng nó vẫn hoạt động.
Khuldraeseth na'Barya
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.