สร้าง fractals นิวตัน


24

คุณทุกคนรู้วิธีการของนิวตันเพื่อประมาณค่ารากของฟังก์ชันใช่มั้ย เป้าหมายของฉันในงานนี้คือการแนะนำคุณในแง่มุมที่น่าสนใจของอัลกอริทึมนี้

อัลกอริธึมของนิวตันมาบรรจบกันสำหรับค่าที่แน่นอนเท่านั้น แต่ส่วนใหญ่ของค่าอินพุตที่ซับซ้อนทั้งหมด หากคุณเห็นการรวมกันของวิธีการสำหรับค่าอินพุตทั้งหมดบนระนาบเชิงซ้อนคุณมักจะได้เศษส่วนที่สวยงามดังนี้:

เศษส่วนนิวตันสำหรับ f (x) = x ^ 3-1 ภาพจาก Wikimedia Commons

ข้อมูลจำเพาะ

เป้าหมายของภารกิจนี้คือเพื่อสร้างเศษส่วนดังกล่าว ซึ่งหมายความว่าคุณได้รับพหุนามเป็นอินพุตและต้องพิมพ์เศษส่วนที่สอดคล้องกันเป็นรูปภาพในรูปแบบที่คุณเลือกเป็นเอาต์พุต

อินพุต

อินพุตเป็นรายการของจำนวนเชิงซ้อนที่คั่นด้วยช่องว่าง พวกเขาเขียนลงในรูปแบบ<Real part><iImaginary part>เช่นหมายเลขนี้:5.32i3.05เช่นจำนวนนี้:คุณอาจสันนิษฐานได้ว่าหมายเลขอินพุตนั้นมีทศนิยมไม่เกิน 4 ตำแหน่งและมีขนาดเล็กกว่า 1,000 ตัวแรกคือต้องไม่เป็นศูนย์ ตัวอย่างเช่นนี่อาจเป็นอินพุตของโปรแกรมของคุณ:

1 -2i7.5 23.0004i-3.8 i12 0 5.1233i0.1

ตัวเลขถูกตีความว่าเป็นสัมประสิทธิ์ของพหุนามเริ่มต้นด้วยพลังสูงสุด ส่วนที่เหลือของข้อกำหนดนี้ตลอดพหุนามเรียกว่าP Pข้อมูลข้างต้นมีค่าเท่ากับพหุนามนี้:

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

อินพุตอาจมาถึงคุณจาก stdin จากอาร์กิวเมนต์ที่ส่งผ่านไปยังโปรแกรมหรือจากพรอมต์ที่ปรากฏไปยังโปรแกรมของคุณ คุณอาจจะสมมติว่าอินพุตนั้นไม่มีอักขระช่องว่างนำหน้าหรือต่อท้าย

การกระทำ

คุณต้องแสดงเศษส่วนด้วยวิธีต่อไปนี้:

  • เลือกสีได้มากเท่ากับรากของPบวกสีพิเศษสำหรับความแตกต่าง
  • สำหรับแต่ละหมายเลขในระนาบที่มองเห็นได้ให้กำหนดว่าวิธีการรวมเข้าด้วยกันหรือไม่และถ้าใช่กับรูตใด แต้มสีตามผลลัพธ์
  • อย่าพิมพ์ไม้บรรทัดหรือสิ่งแฟนซีอื่น ๆ
  • พิมพ์จุดดำที่จุดนั่นคือรากของพหุนามเพื่อการวางแนว คุณสามารถพิมพ์แต่ละพิกเซลได้มากถึงสี่พิกเซล
  • ค้นหาวิธีการเลือกระนาบที่มองเห็นได้ในแบบที่รากทั้งหมดนั้นสามารถแยกได้และกระจายออกไปในวงกว้างถ้าเป็นไปได้ แม้ว่าจะไม่ต้องการตำแหน่งที่สมบูรณ์ของเฟรมเอาต์พุต แต่ฉันขอสงวนสิทธิ์ในการปฏิเสธที่จะยอมรับคำตอบที่เลือกเฟรมในวิธีที่ไม่สามารถยอมรับได้เช่น ที่พิกัดเดียวกันเสมอรากทั้งหมดอยู่ในจุดเดียว ฯลฯ
  • ภาพที่ส่งออกควรมีขนาด 1024 * 1024 พิกเซล
  • เวลาเรนเดอร์คือ 10 นาทีสูงสุด
  • การใช้ค่าทศนิยมเพียงจุดเดียวก็เพียงพอแล้ว

เอาท์พุต

ผลลัพธ์ควรเป็นภาพกราฟิกแรสเตอร์ในรูปแบบไฟล์ที่คุณเลือกซึ่งสามารถอ่านได้โดยซอฟต์แวร์มาตรฐานสำหรับระบบปฏิบัติการแบรนด์ X หากคุณต้องการใช้รูปแบบที่หายากลองพิจารณาเพิ่มลิงค์ไปยังเว็บไซต์ที่สามารถดาวน์โหลดโปรแกรมดูได้

ส่งออกไฟล์ไปยัง stdout หากภาษาของคุณไม่รองรับการวางบางอย่างลงใน stdout หรือหากคุณพบว่าตัวเลือกนี้สะดวกน้อยกว่าให้หาวิธีอื่น ไม่ว่าจะด้วยวิธีใดก็ตามจะต้องสามารถบันทึกภาพที่สร้างขึ้นได้

ข้อ จำกัด

  • ไม่มีไลบรารีการประมวลผลรูปภาพ
  • ไม่มีไลบรารีการสร้างเศษส่วน
  • รหัสที่สั้นที่สุดชนะ

ส่วนขยาย

ถ้าคุณชอบภารกิจนี้คุณสามารถลองแต้มสีตามความเร็วของคอนเวอร์เจนซ์หรือเกณฑ์อื่น ๆ ฉันต้องการเห็นผลลัพธ์ที่น่าสนใจ


6
ฉันไม่แน่ใจว่านี่เหมาะกับการเล่นกอล์ฟหรือไม่ ในสายตาของฉันงานมีความซับซ้อนเกินไป ฉันอาจได้รับการพิสูจน์ว่าผิด
Joey

5
@ Joey: แน่นอน ฉันต้องการให้สิ่งนี้เป็นรหัสที่ท้าทายตัวเอง
Joey Adams

2
... หรือ PPM สำหรับเรื่องนั้น
Joey

1
@ โจอี้: ความตั้งใจของฉันคือการสร้างงานที่ค่อนข้างยากเนื่องจากหลายคนไม่ชอบงานที่ง่ายมาก
FUZxxl

1
แยกย่อยงานได้ง่าย ๆ และหากภาษาของคุณรองรับหมายเลขทศนิยมที่ซับซ้อนโดยกำเนิดคุณสามารถบันทึกก้อนขนาดใหญ่ได้ ฉันมีเวอร์ชันที่ไม่ได้ตีกอล์ฟอย่างเต็มที่ทำงานที่ 1600 chars ซึ่ง 340 เป็นคลาสหมายเลขที่ซับซ้อน ยังไม่ได้ระบุรากและใช้สี แต่ฉันพยายามติดตามสิ่งที่ฉันคิดว่าเป็นข้อบกพร่องในรหัส NR (หารากของ x ^ 3-1 เริ่มต้นที่ -0.5 + 0.866i ก็ไม่ควรแตกต่างเป็นคนแรก!)
ปีเตอร์เทย์เลอร์

คำตอบ:


13

Python 827 777 ตัวอักษร

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

ค้นหาขอบเขตการแสดงผล (และราก) โดยการหาจุดบรรจบกันสำหรับกลุ่มตัวอย่างสุ่ม จากนั้นจะวาดกราฟโดยคำนวณจุดบรรจบสำหรับจุดเริ่มต้นแต่ละจุดและใช้ฟังก์ชันแฮชเพื่อรับสีแบบสุ่มสำหรับจุดบรรจบแต่ละจุด มองอย่างใกล้ชิดและคุณจะเห็นรากที่ทำเครื่องหมายไว้

นี่คือผลลัพธ์ของพหุนามตัวอย่าง

ผลลัพธ์ตัวอย่างพหุนาม


ดี! ฉันชอบสิ่งนี้.
FUZxxl

14

Java, 1093 1058 1099 1077 ตัวอักษร

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="";}}}

อินพุตเป็นอาร์กิวเมนต์บรรทัดคำสั่ง - เช่นเรียกใช้ java F 1 0 0 -1เช่นวิ่ง เอาต์พุตคือ stdout ในรูปแบบ PPM (ASCII pixmap)

สเกลนั้นถูกเลือกโดยใช้ฟูจิวาระที่ยึดตามค่าสัมบูรณ์ของรากที่ซับซ้อนของพหุนาม จากนั้นฉันก็คูณมันด้วย 1.5 ฉันจะปรับความสว่างด้วยอัตราการลู่เข้าดังนั้นรากจะอยู่ในส่วนที่สว่างที่สุด ดังนั้นมันจึงสมเหตุสมผลที่จะใช้สีขาวแทนที่จะเป็นสีดำเพื่อทำเครื่องหมายตำแหน่งโดยประมาณของราก (ซึ่งมีค่าใช้จ่ายฉัน 41 ตัวอักษรสำหรับสิ่งที่ไม่สามารถทำได้ "ถูกต้อง" ถ้าฉันติดป้ายทุกจุดที่บรรจบกันภายใน 0.5 พิกเซลของตัวเอง ถ้าฉันติดป้ายทุกจุดที่มาบรรจบกันภายใน 0.6 พิกเซลของตัวเองรากบางตัวจะมีป้ายกำกับมากกว่าหนึ่งพิกเซลดังนั้นสำหรับแต่ละรูตฉันจะติดป้ายจุดแรกที่พบว่ามาบรรจบกันภายใน 1 พิกเซลของตัวเอง )

อิมเมจสำหรับตัวอย่างพหุนาม (แปลงเป็น png ด้วย GIMP): รากของ x ^ 5 + (- 2 + 7.5i) x ^ 4 + (23.0004-3.8i) x ^ 3 + 12i x ^ 2 + (5.1233 + 0.1i)


@FUZxxl รูปภาพมาจากเวอร์ชั่นเก่า ฉันจะอัปโหลดด้วยอัตราการบรรจบกันในภายหลัง แต่ปัญหาเกี่ยวกับการทำเครื่องหมายรูทคือการกำหนดพิกเซลที่จะทำเครื่องหมาย มันเป็นปัญหาคลาสสิกที่มีจุดลอยตัวคุณไม่สามารถใช้การทดสอบความเท่าเทียมกันที่แน่นอนดังนั้นคุณต้องเปรียบเทียบกับเอปไซลอน เป็นผลให้ฉันไม่มีค่า "บัญญัติ" สำหรับราก ฉันสามารถทำเครื่องหมายพิกเซลที่รวมกันในขั้นตอนเดียว แต่นั่นไม่รับประกันว่าจะทำเครื่องหมายอะไรและสามารถทำเครื่องหมายบล็อก 4 พิกเซลสำหรับรูทเดียว
Peter Taylor

@ ปีเตอร์เทย์เลอร์: อย่างที่คุณเห็น Keith Randall ก็พบวิธีแก้ปัญหาเช่นกัน ฉันเพิ่มข้อกำหนดนี้เป็นความยากพิเศษ วิธีหนึ่งในการทำเช่นนั้นก็คือการคำนวณพิกเซลที่ใกล้ที่สุดสำหรับแต่ละรูตแล้วตรวจสอบแต่ละพิกเซลว่าเท่ากันหรือไม่
FUZxxl

@FZZxxl คุณไม่เข้าใจประเด็นของฉัน "พิกเซลที่ใกล้ที่สุด" ของรูทไม่ได้รับการกำหนดอย่างชัดเจน อย่างไรก็ตามฉันสามารถแฮ็คบางสิ่งที่อาจใช้ได้กับกรณีทดสอบทั้งหมดที่คุณส่งมาและฉันก็ได้รับความประทับใจที่จะทำให้คุณมีความสุข ฉันจะให้สีมันเป็นสีขาวไม่ใช่สีดำเพราะนั่นเป็นเหตุผลมากกว่า
Peter Taylor

@ Peter Taylor: โอเค
FUZxxl

6
รูปโปรไฟล์ของฉันควรเปลี่ยนเป็นx^6-9x^3+8ออกแบบโดยใช้รากแล้วใช้ Wolfram Alpha เพื่อทำให้พหุนามง่ายขึ้น โอเคฉันโกงด้วยการสลับเฉดสีหลังจากนั้นใน GIMP
Peter Taylor

3

Python ขนาด 633 ไบต์

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

After Speed ​​Ups and Beautification (756 ไบต์)

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

เนื้อเรื่องด้านล่างสำหรับฟังก์ชัน Fractal ของ log (z) ของ Newton

เศษส่วนนิวตันสำหรับบันทึก (z)


คุณสามารถใช้สั้น (1 ถ่าน) ;ชื่อและลบช่องว่างโดยการรวมหลายบรรทัดใช้ ยังลบช่องว่างทั้งหมดที่เป็นไปได้
mbomb007

สนามกอล์ฟทั่วไปบางแห่งลดขนาดลงเหลือเพียง353 ไบต์ ! ยังไม่ได้ทดสอบ (ไม่มีmatplotlibที่นี่) ดังนั้นจึงไม่รับประกันว่าจะยังใช้งานได้
Khuldraeseth na'Barya
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.