玩混沌游戏


28

混沌游戏是产生分形的简单方法。给定起点,长度比r和一组2D点,请重复执行以下操作:

  • 从一组要点中,随机(均匀)选择一个。
  • 使用r1-r作为权重对该点和最后绘制的点(或起点)进行平均(即r = 0表示获得起点,r = 1表示获得随机点,r = 0.5表示您在一半之间得到点。)
  • 绘制结果点。

例如,如果您选取了一个等边三角形的顶点且r = 0.5,则绘制的点将绘制出一个Sierpinski三角形:

在此处输入图片说明

在Wikipedia上找到的图片

您要编写一个“玩”混沌游戏以创建分形的程序或函数。

输入项

您可以编写程序或函数,并通过ARGV,STDIN或函数参数接受以下输入:

  • 要绘制的点数。
  • 起始坐标(也必须绘制!)。
  • 区间[0,1]中的平均权重r
  • 点列表供您选择。

输出量

您可以在屏幕上渲染或写入图像文件。如果对结果进行栅格化,则每侧至少需要600像素,所有点都必须在画布上,并且点的图像水平和垂直范围的至少75%必须用于点(这是为了避免用一个黑色像素回答“它实际上已经缩小了”)。的Xÿ轴必须在相同的比例(即从(0,0行)到(1,1)必须处于45度角),并在混乱游戏绘制各点必须表示为单个像素(如果您的绘图方法使该点抗锯齿,则该点可能会分布在2x2像素上)。

颜色是您的选择,但是您至少需要两种可区分的颜色:一种用于背景,另一种用于在混乱游戏中绘制的点。您可能但不必绘制输入点。

请在答案中包括三个有趣的示例输出。

计分

这是代码高尔夫球,因此最短的答案(以字节为单位)获胜。

编辑:您不再需要绘制输入点,因为它们无论如何都不会作为单个像素真正可见。


绘制的每个点...必须表示为单个像素 ”是什么意思?是否a)不应使用抗锯齿;或b)第二种颜色的点数必须等于输入中的第一项?请注意,除非迭代过程对“该像素是否与先前绘制的像素重合?”进行测试,否则b)是无法保证的,因为如果随机数选择器连续选择相同的点足够多的时间,则位置将收敛到那一点。
彼得·泰勒

@PeterTaylor这样做是为了避免人们在点上绘制大点(默认情况下像Mathematica一样),但是我已经注意到,抗锯齿会导致在确保Soham答案中的单个像素时遇到一些麻烦。我认为我会将其放宽为“不得大于2x2像素”,这应涵盖所有抗锯齿问题。
Martin Ender 2014年

我想我误会了一些东西:您总是将绘制的最后一个点的“均值”和当前列表的一个随机点作为“平均值”。然后将新点添加到列表中。那是对的吗?看来,如果您在一个“角”处有很多点,那么在那里您会得到更多,但不太可能走出云端-至少我的代码总是非常快地“收敛”到彼此之间太近的点,以至于真正增强了画面。
瑕疵的

1
@flawr不,您不将新点添加到列表中。列表是固定的-算法只关心绘制的最后一点,而不关心它之前的那个点。
纳撒尼尔(Nathaniel)2014年

谢谢,这解释了很多,也许应该在问题中加以澄清。
瑕疵

Answers:


8

Mathematica,89岁

f[n_,s_,r_,p_]:=Graphics@{AbsolutePointSize@1,Point@NestList[#-r#+r RandomChoice@p&,s,n]}

f[10000, {0, 0}, .5, {{-(1/2), Sqrt[3]/2}, {-(1/2), -(Sqrt[3]/2)}, {1, 0}}]

Mathematica图形

怎么运行的

在Mathematica中,该Graphics[]函数可生成可缩放的图形,您只需拖动图片角即可将其渲染为所需大小。实际上,所有显示图形的初始大小都是一个“ .ini”设置,您可以将其设置为600或您希望的任何其他值。因此,不需要为600x600的要求做任何特别的事情。

AbsolutePointSize[]事指定点大小不会被放大图像尺寸进行修改。

核心构造是

 NestList[#-r#+r RandomChoice@p&,s,n]

或使用非高尔夫伪代码:

 NestList[(previous point)*(1-r) + (random vertex point)*(r), (start point), (iterations)]

它递归地构建一个列表,该列表从(start point)第一个参数中的(矢量)函数开始并将其应用到每个连续点,最后返回所有要绘制的计算点的列表,其中Point[]

一些自我复制的例子:

Grid@Partition[Table[
   pts = N@{Re@#, Im@#} & /@ Table[E^(2 I Pi r/n), {r, 0, n - 1}];
   Framed@f[10000, {0, 0}, 1/n^(1/n), pts], {n, 3, 11}], 3]

Mathematica图形


@MartinBüttner1 Instructions for testing this answer without Mathematica installed:)从pastebin 下载此文件并将其另存为* .CDF 2)从Wolfram Research 下载并安装免费的CDF环境,网址为(不是小文件)。请享用。告诉我是否可行!
belisarius博士2014年

打高尔夫球的版本效果不佳(至少在V10上如此):您需要切换#r到,r#以在没有空格或没有空格的情况下逃脱*
Martin Ender 2014年

@MartinBüttner很好奇!它在v9上像魅惑一样工作(我还没有v10)。无论如何,我(盲目地)交换了#r
belisarius博士2014年

啊,这是一个新功能。现在,您可以将函数应用于关联,在这种情况下,您将获得可以通过访问的命名参数#key。我相信这会派上用场。:)
Martin Ender 2014年

8

Java的:246 253 447

作为功​​能m()

void m(float[]a){new java.awt.Frame(){public void paint(java.awt.Graphics g){int i=0,x=i,y=i,v;for(setSize(832,864),x+=a[1],y+=a[2];i++<=a[0];v=a.length/2-2,v*=Math.random(),x+=(a[v+=v+4]-x)*a[3],y+=(a[v+1]-y)*a[3])g.drawLine(x,y,x,y);}}.show();}

换行符(在程序中以显示用法):

class P{
    public static void main(String[]a){
        new P().m(new float[]{1000000,            // iterations
                              416,432,            // start
                              0.6f,               // r
                              416,32,16,432,      // point list...
                              416,832,816,432,
                              366,382,366,482,
                              466,382,466,482});
    }

    void m(float[]a){
        new java.awt.Frame(){
            public void paint(java.awt.Graphics g){
                int i=0,x=i,y=i,v;
                for(setSize(832,864),x+=a[1],y+=a[2];
                    i++<=a[0];
                    v=a.length/2-2,v*=Math.random(),
                    x+=(a[v+=v+4]-x)*a[3],
                    y+=(a[v+1]-y)*a[3])
                    g.drawLine(x,y,x,y);
            }
        }.show();
    }
}

图纸输入点已从要求中删除(是80个字节!)。它们仍显示在下面的旧屏幕截图中,但是如果运行它们将不会显示。如果有兴趣,请参阅修订历史记录。

输入以浮点数数组形式给出。首先是迭代,接下来的两个是start x y。第四个是r,最后是x1 y1 x2 y2 ...时尚的坐标列表。

忍者之星

1000000 400 400 0.6 400 0 0 400 400 800 800 400 350 350 350 450 450 350 450 450

在此处输入图片说明

交叉

1000000 400 400 0.8 300 0 500 0 500 300 800 300 800 500 500 500 500 800 300 800 300 500 0 500 0 300 300 300

在此处输入图片说明

八链

1000000 400 400 0.75 200 0 600 0 800 200 800 600 600 800 200 800 0 600 0 200

在此处输入图片说明


这在我的计算机上不起作用,并且Java抱怨show已弃用
骄傲的haskeller 2014年

@proudhaskeller show() 过时,但仍然可以使用。当您说“无效”时,这是什么意思?如果您没有安装Java 8中,您将需要一个添加finalString[]a主至少。
Geobits

将您的答案移植到“处理中”并削减了100个字符。
2014年

1
@ace尼斯。您几乎可以使用任何Java golf来实现图形输出,但是我喜欢它恰好是 100个字符:D
Geobits,2014年

7

JavaScript(E6)+ HTML 173176193

编辑:大幅削减,感谢威廉·巴博萨

编辑:少了3个字节,这要归功于DocMax

173个字节,用于计算显示输出所需的功能和canvas元素。

测试另存为html文件并在FireFox中打开。

JSFiddle

好啊


面具


雪


地毯


<canvas id=C>
<script>
F=(n,x,y,r,p)=>{
  for(t=C.getContext("2d"),C.width=C.height=600;n--;x-=(x-p[i])*r,y-=(y-p[i+1])*r)
    i=Math.random(t.fillRect(x,y,1,1))*p.length&~1      
}
F(100000, 300, 300, 0.66, [100,500, 500,100, 500,500, 100,100, 300,150, 150,300, 300,450, 450,300]) // Function call, not counted
</script>

1
<canvas id=C><script>F=(n,x,y,r,p)=>{t=C.getContext("2d"),C.width=C.height=600;for(;n--;)t.fillRect(x,y,1,1),i=Math.random()*p.length&~1,x-=(x-p[i])*r,y-=(y-p[i+1])*r}</script>是176个字节长,我不明白您的计数
William Barbosa 2014年

@WilliamBarbosa根据我的回答,我的计数是正确的。有了您的提示,情况会越来越好-谢谢!
edc65 2014年

1
如果将大小初始化和y更新移入for通话,则可以再减少两个:for(C.width=C.height=600;n--;y-=(y-p[i+1])*r)
DocMax 2014年

6

蟒蛇- 200 189

import os,random as v
def a(n,s,r,z):
    p=[255]*360000
    for i in[1]*(n+1):
        p[600*s[0]+s[1]]=0;k=v.choice(z);s=[int(k[i]*r+s[i]*(1-r))for i in(0,1)]
    os.write(1,b'P5 600 600 255 '+bytes(p))

将输入作为a的函数参数,将结果作为pgm文件写入stdout。 n是迭代,s是起点,r是r和z输入点列表。

编辑:不再以灰色绘制输入点。

有趣的输出:

在此处输入图片说明

Iterations: 100000
Starting Point: (200, 200)
r: 0.8
Points: [(0, 0), (0, 599), (599, 0), (599, 599), (300, 300)]

在此处输入图片说明

Iterations: 100000
Starting Point: (100, 300)
r: 0.6
Points: [(0, 0), (0, 599), (599, 0), (300, 0), (300, 300), (0, 300)]

在此处输入图片说明

Iterations: 100000
Starting Point: (450, 599)
r: 0.75
Points: [(0, 0), (0, 300), (0, 599), (300, 0), (599, 300), (150, 450)]

一些常见的Python字符保存:初始值(例如)p=[255]*360000可以用作函数的可选参数;如果没有控制流,则for循环的主体可以全部在同一行上;你可以从刮胡子括号[1]*(n+1)[1]*-~n; 由于您不在i外部for循环中使用代码,因此将代码运行n时间缩短为exec"code;"*n)会更短;我认为for i in(0,1)可以删除其中的部分。
xnor 2014年

6

超级对撞机-106

SuperCollider是一种用于生成音乐的语言,但是它可以在关键时刻进行图形处理。

f={|n,p,r,l|Window().front.drawHook_({{Pen.addRect(Rect(x(p=l.choose*(1-r)+(p*r)),p.y,1,1))}!n;Pen.fill})}

我使用了一些晦涩的语法快捷方式来节省一些字节-更具可读性和内存效率的版本是

f={|n,p,r,l|Window().front.drawHook_({n.do{Pen.addRect(Rect(p.x,p.y,1,1));p=l.choose*(1-r)+(p*r)};Pen.fill})}

109个字符。

与Mathematica示例一样,您必须手动调整窗口大小以获取600x600像素。执行此操作时,您必须等待它重新绘制。

这将生成一个基本的Sierpinsky三角形(未显示,因为您之前已经看过)

f.(20000,100@100,0.5,[0@600,600@600,300@0])

这使一种Sierpinsky五边形类型的东西:

f.(100000,100@100,1-(2/(1+sqrt(5))),{|i| (sin(i*2pi/5)+1*300)@(1-cos(i*2pi/5)*300)}!5)

在此处输入图片说明

6点相同的事物在中间留下倒置的科赫雪花:

f.(100000,100@100,1/3,{|i| (sin(i*2pi/6)+1*300)@(1-cos(i*2pi/6)*300)}!6)

在此处输入图片说明

最后,这是从ace的回答中得出的关于3D金字塔的即兴之处。(请注意,我使用了其中两个点之一来获得阴影效果。)

f.(150000,100@100,0.49,[300@180, 0@500,0@500,350@400,600@500,250@600])

在此处输入图片说明


6

Python中,189 183 175

编辑:固定反比r,并切换到黑白图像以节省一些字节。

将点数为n,第一点为p,比率为r,点列表为l。需要模块枕头。

import random,PIL.Image as I
s=850
def c(n,p,r,l):
    i=I.new('L',(s,s));x,y=p;
    for j in range(n):w,z=random.choice(l);w*=r;z*=r;x,y=x-x*r+w,y-y*r+z;i.load()[x,s-y]=s
    i.show()

例子:

我在图像中心周围画圆

points = [(425+s*cos(a)/2, 425+s*sin(a)/2) for a in frange(.0, 2*pi, pi/2)]
c(1000000, (425, 425), 0.4, points)

在此处输入图片说明

XOXO重复,将比例从0.4更改为0.6

在此处输入图片说明

某种雪花

stars = [(425+s*cos(a)/2,425+s*sin(a)/2) for a in frange(.0,2*pi, pi/4)]
c(1000000, (425, 425), 0.6, stars)

在此处输入图片说明


说不上关于固定向后[R的事情,但你可以通过使用这个程序节省相当多的字符n,p,r,l=input()。您也可以从*=操作中删除括号并使用import random as R
FryAmTheEggman 2014年

@FryAmTheEggman不幸的是,纠正我的答案无效信号的优化*=。:(的input事情会好是非常不愉快地工作着,而进口是目前最简洁的形式可能(或有我错过了什么?)。
德互联网络是由catz

我很确定这条线可以过去import random as R,PIL.Image as I,然后random.choice可以R.choice。是的,使用输入是la脚的,但是您可以使用功能版本进行测试并将其发布input()以获得更好的分数!! 1!:P
FryAmTheEggman 2014年

哦,我刚刚注意到定义随机实际上节省了0个字符。糟糕:S无论如何,我也意识到数学是您的朋友:y=x*(1-r)+w== y=x-x*r-w
FryAmTheEggman 2014年

@FryAmTheEggman这就是我的观点:p。但是感谢数学。
互联网由catz制作,2014年

4

JavaScript (407)(190)

我很高兴收到关于自己的脚本和高尔夫的任何反馈,因为我对JS =不满意(请随意使用此值/更改该值以进行自己的提交!)

读取输入 (与edc65的输入相当,我不计算输入。):

p=prompt;n=p();[x,y]=p().split(',');r=p();l=p().split(';').map(e=>e.split(','));

画布设置和计算

d=document;d.body.appendChild(c=d.createElement('canvas'));c.width=c.height=1000;c=c.getContext('2d');
for(;n--;c.fillRect(x,y,2,2),[e,f]= l[Math.random()*l.length|0],x-=x*r-e*r,y-=y*r-f*r);

有点不符合实际(包括一个示例输入,其中实际输入的promt仅被注释掉,因此可以使用):

p=prompt;
n=p('n','4000');
[x,y]=p('start','1,1').split(',');
r=p('r','0.5');
l=p('list','1,300;300,1;300,600;600,300').split(';').map(e=>e.split(','));d=document;
d.body.appendChild(c=d.createElement('canvas'));
c.width=c.height=1000;c=c.getContext('2d');
for(;n--;c.fillRect(x,y,2,2),[e,f]= l[Math.random()*l.length|0],x-=x*r-e*r,y-=y*r-f*r);

例子

for(k = 0; k<50; k++){
rad = 10;
l.push([350+rad*k*Math.cos(6.28*k/10),350+rad*k*Math.sin(6.28*k/10)]);
}
r = 1.13;

在此处输入图片说明

r = 0.5;list = [[1,1],[300,522],[600,1],[300,177]];

在此处输入图片说明

r = 0.5
list = [[350+350*Math.sin(6.28*1/5),350+350*Math.cos(6.28*1/5)],
[350+350*Math.sin(6.28*2/5),350+350*Math.cos(6.28*2/5)],
[350+350*Math.sin(6.28*3/5),350+350*Math.cos(6.28*3/5)],
[350+350*Math.sin(6.28*4/5),350+350*Math.cos(6.28*4/5)],
[350+350*Math.sin(6.28*5/5),350+350*Math.cos(6.28*5/5)],


[350+90*Math.sin(6.28*1.5/5),350+90*Math.cos(6.28*1.5/5)],
[350+90*Math.sin(6.28*2.5/5),350+90*Math.cos(6.28*2.5/5)],
[350+90*Math.sin(6.28*3.5/5),350+90*Math.cos(6.28*3.5/5)],
[350+90*Math.sin(6.28*4.5/5),350+90*Math.cos(6.28*4.5/5)],
[350+90*Math.sin(6.28*5.5/5),350+90*Math.cos(6.28*5.5/5)]];

在此处输入图片说明


你是什​​么意思
瑕疵的

谢谢您告诉我,我将尽快更新提交内容!
瑕疵

您链接了我的答案,但我确实计算了画布的设置。我只是不计算调用该函数的一行。无论如何,还是不错的图像,尤其是第一个。
edc65 2014年

啊,我没注意到,我只是想使其变得“可比”,但是当我尝试仅依靠JS时这很困难=)@MartinBüttner更新,现在我以正确的方式理解了它,能够删除大部分垃圾=)
骗子

3

加工153

将@Geobits的Java答案移植到Processing,并进行了更多的打高尔夫球,从而减少了100个字符。我本来打算为该过程制作动画,但是对此的输入约束太苛刻了(Processing没有stdin或argv,这意味着我必须编写自己的函数而不是使用Processing的本机draw()循环)。

void d(float[]a){int v;size(600,600);for(float i=0,x=a[1],y=a[2];i++<a[0];v=(int)random(a.length/2-2),point(x+=(a[v*2+4]-x)*a[3],y+=(a[v*2+5]-y)*a[3]));}

带有换行符的完整程序:

void setup() {
  d(new float[]{100000,300,300,.7,0,600,600,0,600,600,0,0,400,400,200,200,400,200,200,400}); 
}
void d(float[]a){
  int v;
  size(600,600);
  for(float i=0,x=a[1],y=a[2];
      i++<a[0];
      v=(int)random(a.length/2-2),point(x+=(a[v*2+4]-x)*a[3],y+=(a[v*2+5]-y)*a[3]));
}

上面的程序给出了十字架: 在此处输入图片说明

d(new float[]{100000,300,300,.65,142,257,112,358,256,512,216,36,547,234,180,360}); 

这给出了金字塔: 在此处输入图片说明

d(new float[]{100000,100,500,.5,100,300,500,100,500,500});

这给出了Sierpinski三角形: 在此处输入图片说明


4
我喜欢金字塔的3D效果。:)
Martin Ender 2014年

1

Python的非引用式“参考实现”

更新:快得多(按数量级)

查看交互式外壳!

编辑文件并将其设置interactiveTrue,然后执行以下操作之一:

polygon numberOfPoints numeratorOfWeight denominatorOfWeight startX startY numberOfSides 生成,保存并显示多边形。

points numberOfPoints numeratorOfWeight denominatorOfWeight startX startY point1X point1Y point2X point2Y ... 满足规格要求。

在此处输入图片说明

import matplotlib.pyplot as plt
import numpy as np
from fractions import Fraction as F
import random
from matplotlib.colors import ColorConverter
from time import sleep
import math
import sys
import cmd
import time

def plot_saved(n, r, start, points, filetype='png', barsize=30, dpi=100, poly=True, show=False):
    printed_len = 0

    plt.figure(figsize=(6,6))
    plt.axis('off')

    start_time = time.clock()
    f = F.from_float(r).limit_denominator()

    spts = []
    for i in range(len(points)):
        spts.append(tuple([round(points[i].real,1), round(points[i].imag,1)]))

    if poly:
        s = "{}-gon ({}, r = {}|{})".format(len(points), n, f.numerator, f.denominator)
    else:
        s = "{} ({}, r = {}|{})".format(spts, n, f.numerator, f.denominator) 

    step = math.floor(n / 50)

    for i in range(len(points)):
        plt.scatter(points[i].real, points[i].imag, color='#ff2222', s=50, alpha=0.7)

    point = start
    t = time.clock()

    xs = []
    ys = []

    for i in range(n+1):
        elapsed = time.clock() - t
        #Extrapolation
        eta = (n+1-i)*(elapsed/(i+1))
        printed_len = rewrite("{:>29}: {} of {} ({:.3f}%) ETA: {:.3f}s".format(
                s, i, n, i*100/n, eta), printed_len)
        xs.append(point.real)
        ys.append(point.imag)
        point = point * r + random.choice(points) * (1 - r)

    printed_len = rewrite("{:>29}: plotting...".format(s), printed_len)
    plt.scatter(xs, ys, s=0.5, marker=',', alpha=0.3)

    presave = time.clock()
    printed_len = rewrite("{:>29}: saving...".format(s), printed_len)
    plt.savefig(s + "." + filetype, bbox_inches='tight', dpi=dpi)

    postsave = time.clock()
    printed_len = rewrite("{:>29}: done in {:.3f}s (save took {:.3f}s)".format(
                            s, postsave - start_time, postsave - presave),
                            printed_len)

    if show:
        plt.show()
    print()
    plt.clf()

def rewrite(s, prev):
    spaces = prev - len(s)
    sys.stdout.write('\r')
    sys.stdout.write(s + ' '*(0 if spaces < 0 else spaces))
    sys.stdout.flush()
    return len(s)

class InteractiveChaosGame(cmd.Cmd):
    def do_polygon(self, args):
        (n, num, den, sx, sy, deg) = map(int, args.split())
        plot_saved(n, (num + 0.0)/den, np.complex(sx, sy), list(np.roots([1] + [0]*(deg - 1) + [-1])), show=True)

    def do_points(self, args):
        l = list(map(int, args.split()))
        (n, num, den, sx, sy) = tuple(l[:5])
        l = l[5:]
        points = []
        for i in range(len(l)//2):
            points.append(complex(*tuple([l[2*i], l[2*i + 1]])))
        plot_saved(n, (num + 0.0)/den, np.complex(sx, sy), points, poly=False, show=True)

    def do_pointsdpi(self, args):
        l = list(map(int, args.split()))
        (dpi, n, num, den, sx, sy) = tuple(l[:6])
        l = l[6:]
        points = []
        for i in range(len(l)//2):
            points.append(complex(*tuple([l[2*i], l[2*i + 1]])))
        plot_saved(n, (num + 0.0)/den, np.complex(sx, sy), points, poly=False, show=True, dpi=dpi)

    def do_default(self, args):
        do_generate(self, args)

    def do_EOF(self):
        return True

if __name__ == '__main__':
    interactive = False
    if interactive:
        i = InteractiveChaosGame()
        i.prompt = ": "
        i.completekey='tab'
        i.cmdloop()
    else:
        rs = [1/2, 1/3, 2/3, 3/8, 5/8, 5/6, 9/10]
        for i in range(3, 15):
            for r in rs:
                plot_saved(20000, r, np.complex(0,0), 
                            list(np.roots([1] + [0] * (i - 1) + [-1])), 
                            filetype='png', dpi=300)

如果不运行这个,我不知道是什么真棒手段。也许您可以解释一下,或者显示一些图片使其与较短的图片有所不同?
Geobits 2014年

@Geobits编辑为包含免责声明和图片:)
Soham Chowdhury

4
我希望您在其他答案中将其包含在单独的标题下(例如Ungolfed参考实现),因为从技术上讲,发布ungolfed代码是“不是答案”。
Martin Ender 2014年

-2

Python(202个字符)

取点数为n,平均权重为r,起点为a tuple s,点列表为XY列表,tuple称为l

import random as v,matplotlib.pyplot as p
def t(n,r,s,l):
 q=complex;s=q(*s);l=[q(*i)for i in l];p.figure(figsize=(6,6))
 for i in range(n):p.scatter(s.real,s.imag,s=1,marker=',');s=s*r+v.choice(l)*(1-r)
 p.show()

@MartinBüttner我正在使用特定类型的输入的事实是否符合规范?
Soham Chowdhury 2014年

1
另外,在我的机器上,结果不是600x600像素,x和y的长度比例不同,并且这些点覆盖的像素超过1个像素。
Martin Ender 2014年
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.