Как выразить функцию тетратации, для комплексных чисел - PullRequest
3 голосов
/ 23 июня 2019

Существует так называемая последовательность гиперопераций .Он работает так, как будто вы строите умножение a*b=a+a+a+a...+a со многими сложениями a, повторяющимися b раз.Затем идет возведение в степень a^b = a*a*a*a*...*a со многими умножениями a, повторенными b раз.Затем идет тетрация , выраженная как башня возведений в степень, такая же, как a^^b == a^a^a^...^a, повторяется b раз.

Мне интересно, как написать эту функцию для плавающей запятой икомплексные числа?

Я уже написал функции умножения и возведения в степень в glsl:

// complex multiplication:
vec2 cmul(in vec2 a, in vec2 b) {
    return vec2(a.x*b.x-a.y*b.y, a.x*b.y+a.y*b.x);
}

// complex exponent e^a
vec2 cexp(in vec2 a) {
    float ea = exp(a.x);
    float vl = a.y;
    return ea * vec2( cos(vl), sin(vl) );
}

// complex natural logarithm ln(a)
vec2 cln(in vec2 a) {
    float ql = length(a);
    return vec2( log(ql), atan(a.y, a.x));
}

// complex power function a^b
vec2 cpow(in vec2 a, in vec2 b) {
    return cexp(cmul(cln(a), b));   
}

Но тогда я застрял!Как мы можем написать ctet(in vec2 a, in vec2 b) функцию тетратации не только для чисел с плавающей точкой, но и для всей комплексной плоскости?

1 Ответ

4 голосов
/ 24 июня 2019

хорошо, давайте начнем с Реальный домен и только целое число b:

a^^b = a^a^a^a^a...^a  // a is there b times

это можно оценить следующим образом: C ++ :

double tetration(double a,int b)    // a^^b = a^a^a^a... b times
    {
    double c;
    if (b<=0) return 0;
    for (c=a;b>1;b--) c=pow(a,c);
    return c;
    }

, поскольку у вас уже есть pow для сложного домена, вы можете сделать то же самое и там ... Чтобы не усложнять это, я пока не буду касаться этого ...

Вот некоторые результаты:

a\b| 1|   2|            3|    4
-------------------------------
 1 | 1|   1|            1|    1
 2 | 2|   4|           16|65536
 3 | 3|  27|7625597484987|
 4 | 4| 256|             |
 5 | 5|3125|             |

btw. все эти гипероперации связаны с функцией Аккермана , вы можете найти итеративную реализациюмой в C ++ здесь:

Однако из-за чрезвычайно быстрого роста дажеdouble скоро выйдет за пределы диапазона (отсюда и пропущенные значения) ...

Теперь, как переместить b в Реальный домен ?Не имею понятия об алгебраическом подходе для этого, но возможен геометрический.

Просто "построите" a^^b как функцию переменной b и константу a для целочисленных значений b вокруг вашеготребуется вещественное b, а затем интерполировать действительное пространство b с использованием целочисленного домена b в качестве контрольных точек.Это похоже на получение нецелого порядка вывода функции.

Так что (X,Y) будет вашим (a^^b,b).Теперь используйте любую интерполяцию для построения функции Real domain.

Линейная интерполяция будет выглядеть следующим образом:

y0 = a^^(int(b)) 
y1 = a^^(int(b)+1)
a^^b = y0 + (b-int(b))*(y1-y0)

Однако необходима интерполяция более высокого порядка, а также параметр интерполяции должен быть масштабирован до нелинейных метрик.,Для получения дополнительной информации см .:

После некоторой детализации (t^3) и log^2 Масштаб оказался достаточным (пример C ++, использующий мой 128-битный класс с плавающей точкой f128, просто переименуйте его в double):

f128 tetration_fi(f128 a,int b)     // a^^b = a^a^a^a... b times
    {
    f128 c;
    if (b==-1) return 0.0;          // first singularity
    if (b== 0) return 1.0;          // second singularity
    if (b< -1) return 0.0;          // uncomputed
    for (c=a;b>1;b--) c=pow(a,c);
    return c;
    }
//---------------------------------------------------------------------------
f128 tetration_ff(f128 a,f128 b)    // a^^b = a^a^a^a... b times
    {
    int bi;
    f128 z0,z1,z2,z3,a0,a1,a2,a3,t,tt,ttt,o=2.0;
    if (b==-1) return 0.0;          // first singularity
    if (b== 0) return 1.0;          // second singularity
    if (b< -1) return 0.0;          // uncomputed
    bi=b.ToInt(); b-=bi;
    if (b.iszero()) return tetration_fi(a,bi);

    z0=tetration_fi(a,bi-1);        // known points around a^^b
    z1=pow(a,z0);
    z2=pow(a,z1);
    z3=pow(a,z2);

    z0=log2(log2(z0+o)+o);          // log^2 scale
    z1=log2(log2(z1+o)+o);
    z2=log2(log2(z2+o)+o);
    z3=log2(log2(z2+o)+o);

    t =0.5*(z2-z0);                 // cubic interpolation coeff.
    tt=0.5*(z3-z1);
    a0=z1;
    a1=t;
    a2=(3.0*(z2-z1))-(2.0*t)-tt;
    a3=t+tt+(2.0*(z1-z2));

    t=b-bi;                         // cubic interpolation
    tt=t*t;
    ttt=tt*t;
    z0=a0+(a1*t)+(a2*t*t)+(a3*t*t*t);

    z0=exp2(exp2(z0)-o)-o;          // linear scale
    return z0;
    }
//---------------------------------------------------------------------------

final real preview

Это то, с чем я сравнивал:

Я выбираю те же основы графов a из a^^b и какВы можете видеть, что это очень хорошее совпадение, только диапазон ниже 1,0 немного выключен.

Давайте перейдем к фракталу Сложного домена

Теперь, когда вы хотите перейти к сложный домен вы не можете сделать то же самое, что и в реальном, потому что результаты слишком хаотичны для интерполяции.Таким образом, мы можем использовать только целое число b или использовать алгоритм Кнезера для вычисления.

К счастью, для нас есть больше способов показать фрактал ... Например, мы можем оценить целое число b из a^^b, где только a является сложным и использовать результат для раскраски вывода.Вот пример GLSL (на основе моего шейдера Мандельброта и вашей сложной математики):

Фрагмент:

// Fragment
#version 450 core
uniform dvec2 p0=dvec2(0.0,0.0);        // mouse position <-1,+1>
uniform double zoom=1.000;          // zoom [-]
in smooth vec2 p32;
out vec4 col;
//---------------------------------------------------------------------------
// All components are in the range [0…1], including hue.
vec3 rgb2hsv(vec3 c)
    {
    vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
    vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
    vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
    float d = q.x - min(q.w, q.y);
    float e = 1.0e-10;
    return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
    }
//---------------------------------------------------------------------------
// All components are in the range [0…1], including hue.
vec3 hsv2rgb(vec3 c)
    {
    vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
    vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
    return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
    }
//---------------------------------------------------------------------------
vec3 spectral_color(float l)        // RGB <0,1> <- lambda l <400,700> [nm]
    {
    float t;  vec3 c=vec3(0.0,0.0,0.0);
         if ((l>=400.0)&&(l<410.0)) { t=(l-400.0)/(410.0-400.0); c.r=    +(0.33*t)-(0.20*t*t); }
    else if ((l>=410.0)&&(l<475.0)) { t=(l-410.0)/(475.0-410.0); c.r=0.14         -(0.13*t*t); }
    else if ((l>=545.0)&&(l<595.0)) { t=(l-545.0)/(595.0-545.0); c.r=    +(1.98*t)-(     t*t); }
    else if ((l>=595.0)&&(l<650.0)) { t=(l-595.0)/(650.0-595.0); c.r=0.98+(0.06*t)-(0.40*t*t); }
    else if ((l>=650.0)&&(l<700.0)) { t=(l-650.0)/(700.0-650.0); c.r=0.65-(0.84*t)+(0.20*t*t); }
         if ((l>=415.0)&&(l<475.0)) { t=(l-415.0)/(475.0-415.0); c.g=             +(0.80*t*t); }
    else if ((l>=475.0)&&(l<590.0)) { t=(l-475.0)/(590.0-475.0); c.g=0.8 +(0.76*t)-(0.80*t*t); }
    else if ((l>=585.0)&&(l<639.0)) { t=(l-585.0)/(639.0-585.0); c.g=0.84-(0.84*t)           ; }
         if ((l>=400.0)&&(l<475.0)) { t=(l-400.0)/(475.0-400.0); c.b=    +(2.20*t)-(1.50*t*t); }
    else if ((l>=475.0)&&(l<560.0)) { t=(l-475.0)/(560.0-475.0); c.b=0.7 -(     t)+(0.30*t*t); }
    return c;
    }
//---------------------------------------------------------------------------
// complex domain math
vec3 color_wheel(vec2 a)    // complex -> polar -> HSV -> RGB
    {
    float an=(atan(-a.y,-a.x)*0.15915494309189533576888376337251)+0.5;
    float  r=length(a); r-=floor(r); r*=0.75; r+=0.25;
    return hsv2rgb(vec3(an,1.0,r));
    }
vec3 color_spectral(vec2 a) // complex -> wavelength -> RGB
    {
    float  r=length(a); r-=floor(r);
    return spectral_color(400.0+(300.0*r));
    }
vec2 cadd(vec2 a,vec2 b)    // a+b
    {
    return a+b;
    }
vec2 csub(vec2 a,vec2 b)    // a-b
    {
    return a-b;
    }
vec2 cmul(vec2 a,vec2 b)    // a*b
    {
    return vec2((a.x*b.x)-(a.y*b.y),(a.x*b.y)+(a.y*b.x));
    }
vec2 cdiv(vec2 a,vec2 b)    // a/b
    {
    float an=atan(-a.y,-a.x)-atan(-b.y,-b.x);
    float  r=length(a)/length(b);
    return r*vec2(cos(an),sin(an));
    }
vec2 csqr(vec2 a)           // a^2
    {
    return cmul(a,a);
    }
vec2 cexp(vec2 a)           // e^a
    {
//  e^(x+y*i)= e^x * e^(y*i) = e^x * ( cos(y) + i*sin(y) )
    return exp(a.x)*vec2(cos(a.y),sin(a.y));
    }
vec2 cln(vec2 a)            // ln(a)
    {
    return vec2(log(length(a)),atan(-a.y,-a.x));
    }
vec2 cpow(vec2 a,vec2 b)    // a^b
    {
    return cexp(cmul(cln(a),b));
    }
vec2 ctet(vec2 a,int b)     // a^^b
    {
    vec2 c=vec2(1.0,0.0);
    for (;b>0;b--) c=cpow(a,c);
    return c;
    }
//---------------------------------------------------------------------------
void main()
    {
    // poistion (double)
    dvec2 p=dvec2(p32);
    p=(p/zoom)-p0;          // x,y (-1.0, 1.0)
    // position (float)
    vec2 pp=vec2(p);

    // [chose function]

    // complex domain test function 1 (color wheel)
//  vec2 a=cdiv(cmul(csub(cmul(pp,pp),vec2(1.0,0.0)),csqr(csub(pp,vec2(2.0,1.0)))),cadd(csqr(pp),vec2(2.0,2.0)));
    // complex domain test function 2 (color wheel)
//  vec2 a=pp; a=cln(a);
    // exponentiation escape fractal 1 (color wheel)
//  vec2 a=cpow(pp,vec2(100,0));
    // exponentiation escape fractal 2 (color wheel)
//  vec2 a=vec2(1.0,1.0); for (int i=0;i<100;i++) a=cpow(a,pp);
    // exponentiation escape fractal 3 (color wheel)
//  vec2 a=vec2(0.0,0.0),b=vec2(1.0,0.0); float r=0.5,rr=1.0,wt=0.1; for (int i=0;i<20;i++){ a+=rr*cexp(vec2(-b.y,b.x)*wt); b=cmul(b,pp); rr*=r; } a*=(1.0-r);
    // tetration escape fractal (grayscale)
//  vec2 a=ctet(pp,100);
    // pentation escape fractal (grayscale)
    vec2 a=pp; for (int i=0;i<20;i++) a=ctet(a,20); a*=100.0;

    // [chose coloring method]

    // grayscale based on escape
    float r=0.2*length(a); r-=floor(r); r=0.25+0.75*r; col=vec4(r,r,r,1.0);
    // RGB based on result
//  col=vec4(a,a.x+a.y,1.0);
    // result -> wavelength+intensity
//  col=vec4(color_wheel(a),1.0);
    // result -> spectral color
//  col=vec4(color_spectral(a),1.0);
    }

И предварительный просмотр тетратации:

complex preview

Это то, с чем я сравнил:

, и это соответствует моему результату, просто отражается в обоих x,y

Так что я делал вычисления a^^100, где aявляется сложной позицией домена фрагмента на экране <-1,+1> с некоторыми panning и zooming и отображает цвет, построенный из результата ...

Я оставляю там также тестовую функцию (не фрактальную), которую я использовалчтобы проверить методы раскраски и сложную математику, взятые из здесь первое из Вики, второе - результат шейдера (цветовое колесо):

test function Wiki test function mine

Вы можете сделать аварийное тестирование, например, для Мандельброта или любого другого алгоритма, чтобы вместо этого показать фрактал.

Здесь варианты окраски scrEenshots тетрации (мне нравится оттенки серого) zoom=500.0 pos=-0.188418+0.234466i

enter image description here enter image description here enter image description here enter image description here

И, наконец, пенсия:

enter image description here enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...