产生牛顿分形


24

你们都知道牛顿法可以近似函数的根,不是吗?我在此任务中的目标是向您介绍该算法的一个有趣方面。

牛顿算法仅针对某些但所有复杂的输入值进行收敛。如果您描绘了复杂平面上所有输入值的方法的收敛性,通常会得到一个漂亮的分形,如下所示:

f(x)= x ^ 3-1的牛顿分形 图片来自维基共享资源

技术指标

此任务的目标是生成此类分形。这意味着,您将获得多项式作为输入,并且必须以您选择的格式将相应的分形作为图像打印输出。

输入项

输入是用空格分隔的复数列表。它们在风格写下来<Real part><iImaginary part>,这样的数字:5.32i3.05。您可能会假设输入数字的位数不超过4个且小于1000。它们中的第一个不能为零。例如,这可能是您程序的输入:

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

数字被解释为从最高幂开始的多项式系数。在本说明书中的其余部分,输入多项式称为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

输入可能来自标准输入,传递给程序的参数或显示给程序的提示。您可以假定输入不包含任何前导或尾随空格字符。

渲染图

您必须通过以下方式渲染分形:

  • 选择尽可能多的颜色作为P的根,再加上额外的颜色以发散
  • 对于可见平面中的每个数字,确定方法是否收敛,如果是,则收敛到哪个根。根据结果​​为点着色。
  • 不要打印标尺或其他花哨的东西
  • 在这些点上打印一个黑点,这些点是定向的多项式根。每个根周围最多可以打印四个像素。
  • 找到一种方式来选择可见平面,所有根都是可区分的,并且在可能的情况下在整个平面上广泛分布。尽管不需要完美放置输出框架,但我保留拒绝接受以不可接受的方式选择框架的答案的权利。总是在同一坐标上,所有根都在一个点上,依此类推。
  • 输出图像的大小应为1024 * 1024像素。
  • 渲染时间最长为10分钟
  • 使用单精度浮点值就足够了

输出量

输出应该是您选择的文件格式的光栅图形图像,该图像可由Brand X操作系统的标准软件读取。如果您想使用一种罕见的格式,请考虑添加一个指向网站的链接,从中可以下载该网站的查看器。

将文件输出到stdout。如果您的语言不支持将某些内容添加到stdout,或者您发现此选项不太方便,请尝试另一种方法。无论如何,必须有可能保存生成的图像。

限制条件

  • 没有图像处理库
  • 没有分形生成库
  • 最短的代码胜出

扩展名

如果您喜欢此任务,则可以尝试根据收敛速度或其他标准为点着色。我希望看到一些有趣的结果。


6
我不确定这是否适合打高尔夫。在我看来,任务太复杂了。我可能被证明是错的。
乔伊,

5
@乔伊:的确。我希望自己成为代码挑战者
乔伊·亚当斯

2
...或PPM。
乔伊,

1
@Joey:我的意图是创建一个相当困难的任务,因为许多人不喜欢非常简单的任务。
FUZxxl 2011年

1
它很容易分解为单独的任务,如果您的语言本机支持复杂的浮点数,那么您可以节省一大块。我有一个版本不完整的版本,以1600个字符运行,其中340是复数类。它尚不能确定其根源并使用颜色,但是我试图追踪我认为是NR代码中的错误的地方。(找到从-0.5 + 0.866i开始的x ^ 3-1的根肯定不会发散!)
彼得·泰勒

Answers:


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

通过查找一堆随机样本的收敛点来找到显示边界(和根)。然后,通过计算每个起点的会聚点并使用哈希函数为每个会聚点获得随机颜色来绘制图形。仔细观察,您会看到标记的根。

这是示例多项式的结果。

结果,例如多项式


好!我喜欢这个。
2011年

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

输入是命令行参数-例如run java F 1 0 0 -1。输出以PPM格式(ASCII像素图)输出到stdout。

使用在多项式复数根的绝对值上的藤原界来选择比例。然后,将其乘以1.5。我确实通过会聚率来调整亮度,因此其根部将位于最亮的补丁中。因此,使用白色而不是黑色来标记根的大概位置是合乎逻辑的(这花了我41个字符,这甚至无法“正确地”完成。如果我标记所有会聚到自身0.5像素内的点,那么一些根会被标记为未标记;如果我标记所有收敛到其自身0.6像素以内的点,那么一些根会被标记为一个以上像素;因此对于每个根,我标记所遇到的第一个点会收敛到其自身的1像素以内)。

示例多项式的图像(使用GIMP转换为png): x ^ 5 +(-2 + 7.5i)x ^ 4 +(23.0004-3.8i)x ^ 3 + 12i x ^ 2 +(5.1233 + 0.1i)的根


@FUZxxl,该图像来自旧版本。稍后,我将以收敛速度上传一个。但是标记根的问题是确定要标记的像素。这是一个经典的问题,对于浮点数,您不能使用精确的相等性测试,因此必须与epsilon进行比较。结果,我的根没有“规范”值。我可以标记一步收敛的像素,但这不能保证标记任何东西,并且可以为单个根标记一个4像素的块。
彼得·泰勒

@Peter Taylor:如您所见,Keith Randall也找到了解决该问题的方法。我将此要求添加为额外的困难。一种实现方法是为每个根计算最近的像素,然后检查每个像素是否相等。
FUZxxl 2011年

@FUZxxl,您不明白我的意思。根的“最近像素”定义不明确。但是,我可以破解一些可能适用于您扔给它的所有测试用例的东西,并且给人的印象是,这会让您感到高兴。我将其着色为白色,而不是黑色,因为这更合逻辑。
彼得·泰勒

@Peter Taylor:好的。
FUZxxl 2011年

6
我的个人资料图片应尽快更改为x^6-9x^3+8,经过精心设计,方法是选择根,然后使用Wolfram Alpha简化多项式。好的,我后来在GIMP中交换了色相来作弊。
彼得·泰勒

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

加速和美化之后(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()

下图是log(z)函数的牛顿分形图。

牛顿分形对数(z)


您可以使用较短的名称(1个字符),并使用组合多行来删除空格;。另外,删除所有可能的空间。
mbomb007'4

一些常规的高尔夫球将其减少到353个字节!还没有测试(matplotlib这里没有),所以不能保证它仍然可以工作。
Khuldraeseth na'Barya
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.