Helecho de Barnsley

De Wikipedia, la enciclopedia libre
Helecho de Barnsley

El helecho de Barnsley es un fractal que lleva el nombre del matemático británico Michael Barnsley, quien lo describió por primera vez en su libro de 1993 "Fractals Everywhere" (Fractales en todas partes).[1]​ Lo ideó para que su forma se pareciera a las hojas del adianto negro, un helecho común en zonas lluviosas de todo el mundo.

Historia[editar]

El helecho es uno de los ejemplos básicos de conjuntos autosemejantes, es decir, es un patrón generado matemáticamente que puede reproducirse con cualquier aumento o reducción. Al igual que el triángulo de Sierpinski, el helecho de Barnsley muestra cómo se pueden construir estructuras gráficamente hermosas a partir de usos repetitivos de fórmulas matemáticas con computadoras. El libro de 1988 de Barnsley "Fractals Everywhere" se basa en el curso que impartió para estudiantes de pregrado y posgrado en la Escuela de Matemáticas del Instituto de Tecnología de Georgia, llamado "Fractal Geometry". Después de la publicación del libro, se desarrolló un segundo curso, llamado Teoría de la medida fractal.[1]​ El trabajo de Barnsley ha sido una fuente de inspiración para que otros artistas gráficos intentaran imitar la naturaleza con modelos matemáticos.

Imagen procesada del helecho de Barnsley

El código del helecho desarrollado por Barnsley es un ejemplo de un sistema iterativo de funciones (SIF) para crear un fractal a partir del teorema del collage. Ha utilizado fractales para modelar una amplia gama de fenómenos en ciencia y tecnología, pero más específicamente en estructuras vegetales.

Los SIF proporcionan modelos para ciertas plantas, hojas y helechos, en virtud de la auto-similitud que a menudo presentan las estructuras ramificadas en la naturaleza. Pero la naturaleza también exhibe aleatoriedad y variación de un nivel a otro; no hay dos helechos exactamente iguales, y las frondas ramificadas se convierten en hojas a menor escala. Los fractales V-variables permiten tal aleatoriedad y variabilidad a través de escalas, mientras que al mismo tiempo admiten una dependencia continua de parámetros que facilitan el modelado geométrico. Estos factores nos permiten hacer los modelos biológicos híbridos ... ... especulamos que cuando se encuentra un modelo fractal geométrico variable en V que se ajusta bien a la geometría de una planta dada, entonces existe una relación específica entre estos árboles de código y la información almacenada en los genes de la planta.

— Michael Barnsley et al. [2]

Construcción[editar]

Fotografía de las hojas de un helecho verdadero

El helecho de Barnsley usa cuatro transformaciones afines. La fórmula de una transformación es la siguiente:

Barnsley muestra el código "IFS" para su fractal de helecho "Black Spleenwort" como una matriz de valores que se muestran en una tabla.[3]​ En la tabla, las columnas de la "a" a la "f" son los coeficientes de la ecuación y "p" representa el factor de probabilidad.

w a b c d e f p Porción generada
ƒ1 0 0 0 0.16 0 0 0.01 Tallo
ƒ2 0.85 0.04 −0.04 0.85 0 1.60 0.85 Foliolos sucesivamente más pequeños
ƒ3 0.20 −0.26 0.23 0.22 0 1.60 0.07 Foliolo más grande a la izquierda
ƒ4 −0.15 0.28 0.26 0.24 0 0.44 0.07 Foliolo más grande a la derecha

Estos corresponden a las siguientes transformaciones:

Generación con computadoras[editar]

Helecho fractal en cuatro estados de construcción. Los triángulos resaltados muestran cómo la mitad de un "foliolo" se transforma en la mitad de una "hoja" o "fronda" entera

Aunque, en teoría, el helecho de Barnsley podría trazarse a mano con un bolígrafo y papel cuadriculado, el número de iteraciones necesarias asciende a decenas de miles, lo que hace que el uso de una computadora sea prácticamente obligatorio. Muchos modelos informáticos diferentes del helecho de Barnsley son populares entre los matemáticos contemporáneos. Siempre que las matemáticas se programen correctamente utilizando la matriz de constantes de Barnsley, se producirá la misma forma de helecho.

El primer punto dibujado está en el origen (x0 = 0, y0 = 0) y a continuación los nuevos puntos se calculan iterativamente aplicando aleatoriamente una de las siguientes cuatro transformaciones de coordenadas:[4][5]

ƒ1

xn + 1 = 0
yn + 1 = 0.16 yn.

Esta transformación de coordenadas se elige el 1% del tiempo y simplemente asigna cualquier punto a un punto en el primer segmento de línea en la base del tallo. Esta parte de la figura es la primera que se completa durante el transcurso de las iteraciones.


ƒ2

xn + 1 = 0.85 xn + 0.04 yn
yn + 1 = −0.04 xn + 0.85 yn + 1.6.

Esta transformación de coordenadas se elige el 85% de las veces y calcula cualquier punto dentro del foliolo representado por el triángulo rojo a un punto dentro del folliolo opuesto, más pequeño, representado por el triángulo azul en la figura.

ƒ3

xn + 1 = 0.2 xn - 0.26 yn
yn + 1 = 0.23 xn + 0.22 yn + 1.6.

Esta transformación de coordenadas se elige el 7% del tiempo y calcula cualquier punto dentro del foliolo representado por el triángulo azul a un punto dentro del triángulo correspondiente alterno a través del tallo (lo voltea).

ƒ4

xn + 1 = −0.15 xn + 0.28 yn
yn + 1 = 0.26 xn + 0.24 yn + 0.44.

Esta transformación de coordenadas se elige el 7% de las veces y calcula cualquier punto dentro del foliolo representado por el triángulo azul a un punto dentro del triángulo correspondiente alterno a través del tallo (sin voltearlo).

La primera transformación de coordenadas dibuja el tallo. La segunda genera copias sucesivas del tallo y las frondas inferiores para formar el helecho completo. La tercera dibuja la fronda inferior a la izquierda. El cuarto dibuja la fronda inferior a la derecha. La naturaleza recursiva del SIF garantiza que el conjunto sea una réplica más grande de cada fronda. Debe tenerse en cuenta que el helecho completo está dentro del rango −2,1820 < x < 2.6558 y 0 ≤ y < 9.9983.

Variedades mutantes[editar]

Helecho de Barnsley transformado en un helecho telipteridáceo
Helecho de Barnsley transformado en un polipodio

Jugando con los coeficientes, es posible crear variedades de helechos mutantes. En su artículo sobre fractales V-variables, Barnsley llama a este rasgo un "superfractal".[2]

Sin embargo, un experimentador ha elaborado una tabla de coeficientes para producir otro helecho de aspecto notablemente natural, parecido a los helechos Cyclosorus o Thelypteridaceae. Estos valores paramétricos son:[6][7]

w a b c d e f p
ƒ1 0 0 0 0.25 0 −0.4 0.02
ƒ2 0.95 0.005 −0.005 0.93 −0.002 0.5 0.84
ƒ3 0.035 −0.2 0.16 0.04 −0.09 0.02 0.07
ƒ4 −0.04 0.2 0.16 0.04 0.083 0.12 0.07


Código informático[editar]

Puede usarse el código siguiente para dibujar el helecho con un ordenador:

Python[editar]

import turtle
import random

pen = turtle.Turtle()
pen.speed(0)
pen.color("green")
pen.penup()

x = 0
y = 0
for n in range(11000):
    pen.goto(65 * x, 37 * y - 252)  # scale the fern to fit nicely inside the window
    pen.pendown()
    pen.dot(3)
    pen.penup()
    r = random.random()
    if r < 0.01:
        x, y =  0.00 * x + 0.00 * y,  0.00 * x + 0.16 * y + 0.00
    elif r < 0.86:
        x, y =  0.85 * x + 0.04 * y, -0.04 * x + 0.85 * y + 1.60
    elif r < 0.93:
        x, y =  0.20 * x - 0.26 * y,  0.23 * x + 0.22 * y + 1.60
    else:
        x, y = -0.15 * x + 0.28 * y,  0.26 * x + 0.24 * y + 0.44

R[editar]

# Barnsley's Fern

# create function of the probability and the current point
fractal_fern2 <- function(x, p){
  if (p <= 0.01) {
    m <- matrix(c(0, 0, 0, .16), 2, 2)
    f <- c(0, 0)
  } else if (p <= 0.86) {
    m <- matrix(c(.85, -.04, .04, .85), 2, 2)
    f <- c(0, 1.6)
  } else if (p <= 0.93) {
    m <- matrix(c(.2, .23, -.26, .22), 2, 2)
    f <- c(0, 1.6)
  } else {
    m <- matrix(c(-.15, .26, .28, .24), 2, 2)
    f <- c(0, .44)
  }
  m %*% x + f
}

# how many reps determines how detailed the fern will be
reps <- 10000

# create a vector with probability values, and a matrix to store coordinates
p <- runif(reps)

# initialise a point at the origin
coords <- c(0, 0)

# compute Fractal Coordinates
m <- Reduce(fractal_fern2, p, accumulate = T, init = coords)
m <- t(do.call(cbind, m))

# Create plot
plot(m, type = "p", cex = 0.1, col = "darkgreen",
     xlim = c(-3, 3), ylim = c(0, 10), 
     xlab = NA, ylab = NA, axes = FALSE)

Processing[editar]

/*
    Barnsley Fern for Processing 3.4
*/

// declaring variables x and y
float x, y;

// creating canvas
void setup() {
  size(600, 600);
  background(255);
}

/* setting stroke,  mapping canvas and then
   plotting the points */
void drawPoint() {
  stroke(34, 139, 34);
  strokeWeight(1);
  float px = map(x, -2.1820, 2.6558, 0, width);
  float py = map(y, 0, 9.9983, height, 0);
  point(px, py);
}

/* algorithm for calculating value of (n+1)th
   term of x and y based on the transformation
   matrices */
void nextPoint() {
  float nextX, nextY;
  float r = random(1);
  if (r < 0.01) {
    nextX =  0;
    nextY =  0.16 * y;
  } else if (r < 0.86) {
    nextX =  0.85 * x + 0.04 * y;
    nextY = -0.04 * x + 0.85 * y + 1.6;
  } else if (r < 0.93) {
    nextX =  0.20 * x - 0.26 * y;
    nextY =  0.23 * x + 0.22 * y + 1.6;
  } else {
    nextX = -0.15 * x + 0.28 * y;
    nextY =  0.26 * x + 0.24 * y + 0.44;
  }
  x = nextX;
  y = nextY;
}

/* iterate the plotting and calculation
   functions over a loop */
void draw() {
  for (int i = 0; i < 100; i++) {
    drawPoint();
    nextPoint();
  }
}

P5.JS[editar]

let x = 0;
let y = 0;

function setup() {
  createCanvas(600, 600);
  background(0);
}

//range −2.1820 < x < 2.6558 and 0 ≤ y < 9.9983.
function drawPoint() {
  stroke(255);
  strokeWeight(1);
  let px = map(x, -2.1820, 2.6558, 0, width);
  let py = map(y, 0, 9.9983, height, 0);
  point(px, py);
}

function nextPoint() {
  let nextX;
  let nextY;

  let r = random(1);

  if (r < 0.01) {
    //1
    nextX = 0;
    nextY = 0.16 * y;
  } else if (r < 0.86) {
    //2
    nextX = 0.85 * x + 0.04 * y;
    nextY = -0.04 * x + 0.85 * y + 1.60;
  } else if (r < 0.93) {
    //3
    nextX = 0.20 * x + -0.26 * y;
    nextY = 0.23 * x + 0.22 * y + 1.60;
  } else {
    //4
    nextX = -0.15 * x + 0.28 * y;
    nextY = 0.26 * x + 0.24 * y + 0.44;
  }

  x = nextX;
  y = nextY;
}

function draw() {
  for (let i = 0; i < 1000; i++) {
    drawPoint();
    nextPoint();
  }
}

JavaScript (HTML5)[editar]

<canvas id="canvas" height="700" width="700">
</canvas>

<script>
    let canvas;
    let canvasContext;

    let x = 0, y = 0;

    window.onload = function () {
        canvas = document.getElementById("canvas");
        canvasContext = canvas.getContext('2d');

        canvasContext.fillStyle = "black";
        canvasContext.fillRect(0, 0, canvas.width, canvas.height);

        setInterval(() => {
            // Update 20 times every frame
            for (let i = 0; i < 20; i++)
                update();
                
        }, 1000/250); // 250 frames per second
    };

    function update() {

        let nextX, nextY;
        let r = Math.random();
        if (r < 0.01) {
            nextX =  0;
            nextY =  0.16 * y;
        } else if (r < 0.86) {
            nextX =  0.85 * x + 0.04 * y;
            nextY = -0.04 * x + 0.85 * y + 1.6;
        } else if (r < 0.93) {
            nextX =  0.20 * x - 0.26 * y;
            nextY =  0.23 * x + 0.22 * y + 1.6;
        } else {
            nextX = -0.15 * x + 0.28 * y;
            nextY =  0.26 * x + 0.24 * y + 0.44;
        }

        // Scaling and positioning
        let plotX = canvas.width * (x + 3) / 6;
        let plotY = canvas.height - canvas.height * ((y + 2) / 14);

        drawFilledCircle(plotX, plotY, 1, "green");

        x = nextX;
        y = nextY;

    }
    const drawFilledCircle = (centerX, centerY, radius, color) => {
        canvasContext.beginPath();
        canvasContext.fillStyle = color;
        canvasContext.arc(centerX, centerY, radius, 0, 2 * Math.PI, true);
        canvasContext.fill();
    };
</script>

QBasic[editar]

SCREEN 12
WINDOW (-5, 0)-(5, 10)
RANDOMIZE TIMER
COLOR 10
DO
    SELECT CASE RND
        CASE IS < .01
            nextX = 0
            nextY = .16 * y
        CASE .01 TO .08
            nextX = .2 * x - .26 * y
            nextY = .23 * x + .22 * y + 1.6
        CASE .08 TO .15
            nextX = -.15 * x + .28 * y
            nextY = .26 * x + .24 * y + .44
        CASE ELSE
            nextX = .85 * x + .04 * y
            nextY = -.04 * x + .85 * y + 1.6
    END SELECT
    x = nextX
    y = nextY
    PSET (x, y)
LOOP UNTIL INKEY$ = CHR$(27)

Amola[editar]

addpackage("Forms.dll")

set("x", 0)
set("y", 0)
set("width", 600)
set("height", 600)

method setup()
	createCanvas(width, height)
	rect(0, 0, 600, 600, color(0, 0, 0))
end

method drawPoint()
    set("curX", div(mult(width, add(x, 3)), 6))
    set("curY", sub(height, mult(height, div(add(y, 2), 14))))
    set("size", 1)
	//log(curX)
	//log(curY)
	rect(round(curX - size / 2), round(curY - size / 2), round(curX + size / 2), round(curY + size / 2), color(34, 139, 34))
end

method nextPoint()
	set("nextX", 0)
	set("nextY", 0)
	set("random", random(0, 100))
	if(random < 1)
		set("nextX", 0)
		set("nextY", 0.16 * y)
	end
	else
		if(random < 86)
			set("nextX", 0.85 * x + 0.04 * y)
			set("nextY", -0.04 * x + 0.85 * y + 1.6)
		end
		else
			if(random < 93)
				set("nextX", 0.2 * x - 0.26 * y)
				set("nextY", 0.23 * x + 0.22 * y + 1.6)
			end
			else
				set("nextX", -0.15 * x + 0.28 * y)
				set("nextY", 0.26 * x + 0.24 * y + 0.44)
			end
		end
	end

	set("x", nextX)
	set("y", nextY)
end

setup()
while(true)
	drawPoint()
	nextPoint()
end

TSQL[editar]

/* results table */
declare @fern table (Fun int, X float, Y float, Seq int identity(1,1) primary key, DateAdded datetime default getdate())

declare @i int = 1	/* iterations */
declare @fun int	/* random function */

declare @x float = 0	/* initialise X = 0 */
declare @y float = 0	/* initialise Y = 0 */
declare @rand float
insert into @fern (Fun, X, Y) values (0,0,0)	/* set starting point */

while @i < 5000 /* how many points? */
begin

	set @rand = rand()

	select @Fun = case	/* get random function to use -- @fun = f1 = 1%, f2 = 85%, f3 = 7%, f4 = 7% */
		when @rand <= 0.01 then 1
		when @rand <= 0.86 then 2
		when @rand <= 0.93 then 3
		when @rand <= 1 then 4
	end

	select top 1 @X = X, @Y = Y from @fern order by Seq desc /* get previous point */

	insert into @fern(Fun, X, Y)	/* transform using four different function expressions  */
	select @fun,
		case @fun
			when 1 then 0
			when 2 then 0.85*@x+0.04*@y
			when 3 then 0.2*@x-0.26*@y
			when 4 then -0.15*@x + 0.28*@y
		end X,
		case @fun
			when 1 then 0.16*@y
			when 2 then -0.04*@x + 0.85*@y + 1.6
			when 3 then 0.23*@x + 0.22*@y + 1.6
			when 4 then 0.26*@x + 0.24*@y + 0.44
		end Y

	set @i=@i+1
end

select top 5000 *,geography::Point(Y, X, 4326) from @fern 
order by newid()

MATLAB[editar]

N = 1000000;

xy = [0; 0];
fern = zeros(N, 2);

f_1 = [0 0; 0 0.16];
f_2 = [0.85 0.04; -0.04 0.85];
f_3 = [0.2 -0.26; 0.23 0.22];
f_4 = [-0.15 0.28; 0.26 0.24];

P = randsample(1:4, N, true, [0.01 0.85 0.07 0.07]);
for i = 2:N
    p = P(i - 1);
    if p == 1 % Stem
        xy = f_1 * xy;
    elseif p == 2 % Sub-leaflets
        xy = f_2 * xy + [0; 1.6];
    elseif p == 3 % Left leaflet
        xy = f_3 * xy + [0; 1.6];
    else % Right leaflet
        xy = f_4 * xy + [0; 0.44];
    end
    
    fern(i, 1) = xy(1);
    fern(i, 2) = xy(2);
end
clearvars -except N fern % R2008a+

% Plotting the fern

%{
% Better detail, slower performance
c = linspace(0, 0.35, N - 1); c(end + 1) = 1;
colormap(summer(N));
set(gcf, 'Color', 'k', 'position', [10, 50, 800, 600]);
scatter(fern(:, 1), fern(:, 2), 0.1, c, 'o');
set(gca, 'Color', 'k');
%}

%
% Less detail, better performance
c = linspace(1, 0.2, N - 1); c(end + 1) = 0;
colormap(summer(N));
set(gcf, 'Color', 'k', 'position', [10, 50, 800, 600]);
scatter(fern(:, 1), fern(:, 2), 0.1, c, '.');
set(gca, 'Color', 'k');
%}

Wolfram Mathematica[editar]

BarnsleyFern[iterations_] := (
  it = iterations; 
  noElement = 1;
  BarnsleyfernTransformation(*obtained from the affine transformation matrix for simpler
coding*)= {
    {0, +0.16 y},
    {0.85 x + 0.04 y, 1.6 - 0.04 x + 0.85 y},
    {0.2 x - 0.26 y, 1.6 + 0.23 x + 0.22 y},
    {-0.15 x + 0.28 y, 0.44 + 0.26 x + 0.24 y}};
   (*list of coordinates*)
  point = {{0, 0}};
  While[it > 0 && noElement <= iterations,
   AppendTo[point,
    BarnsleyfernTransformation[[(*determines which transformation \
applies to the previous point*)
       RandomInteger[{1, 4}]]] /. {x -> Part[point, noElement, 1], 
      y -> Part[point, noElement, 2] }];
   (*update var for the while loop*)
   it -= 1; noElement += 1];
  ListPlot[point, AspectRatio -> Automatic, PlotRange -> Full])

Referencias[editar]

  1. a b Fractals Everywhere, Boston, MA: Academic Press, 1993, ISBN 0-12-079062-9
  2. a b Michael Barnsley, et al.,«"V-variable fractals and superfractals"». Archivado desde el original el 22 de febrero de 2012. Consultado el 6 de enero de 2022.  (2.22 MB)
  3. Fractals Everywhere, table III.3, IFS code for a fern.
  4. Barnsley, Michael (2000). Fractals everywhere. Morgan Kaufmann. p. 86. ISBN 0-12-079069-6. Consultado el 7 de enero de 2010. 
  5. Weisstein, Eric. «Barnsley's Fern». Consultado el 7 de enero de 2010. 
  6. Otras variedades de helechos con coeficientes proporcionados
  7. A Barnsley fern generator