# Better way to do such iteration

Consider such an iteration function

SeedRandom[1];
n = 2.5;
func[{A_, B_, C_}, t_] := With[{M = (A + B)/2, P = A + (B - A) 0.3, Q = A + (B - A) 0.7},
{{C, A, M + t (P - M)}, {B, C, M + t (Q - M)}}];
init = RandomReal[1, {3, 2}];

iter = Join @@ Table[func[p, If[Length@# <= 2^(n - 1), 1, Mod[n, 1]]], {p, #}] &;
ans1 = Nest[iter, {init}, Ceiling@n]


The above code can work normally, but it's not flexible enough, because it depends on the length of the previous iteration list. In this case, it's simple, the general term formula is $2^{n-1}$. When func is changed, it's not necessarily a simple exponential function. I came up with this

list = NestList[Join @@ Table[func[i, 1], {i, #}] &, {init}, Ceiling@n];
iter2 = Join @@ Table[func[i, If[Length@# <= Length@list[[Floor@n]], 1, Mod[n, 1]]], {i, #}] &;
ans2 = Nest[iter2, {init}, Ceiling@n]
ans2 == ans1


I feel like this requires extra calculation, is there a better way?

My goal is to implement a fractal, when the number of iterations is not an integer, there is a transition effect. Change {n,0,5} to {n,0,5,1} to see the difference.

Manipulate[
Module[{func,init,iter,ans1},
func[{A_,B_,C_},t_]:=With[{M=(A+B)/2,P=A+(B-A) 0.3,Q=A+(B-A) 0.7},{{C,A,M+t (P-M)},{B,C,M+t (Q-M)}}];
SeedRandom[5];
init=RandomReal[1,{3,2}];
iter=Join@@Table[func[p,If[Length@#<=2^(n-1),1,Mod[n,1]]],{p,#}]&;
ans1=Nest[iter,{init},Ceiling@n];
Graphics[{Polygon/@ans1}]
],{n,0,5}]

• It's not really clear what you are asking or what you code is supposed to do. Please reduce the code to a minimal working example, and describe in detail what you want it to do, and where you are stuck. As it stands, you simply show a big block of code that does something, and for some reason, you change a condition from <= 2^(n-1) to <=Length@list[[Floor@n]] (without explaining why). Is the goal simply to predict the length of the elements of list? Jan 14, 2023 at 15:03
• Best to avoid single-capital-letters as variables (or beginning with capitals at all). In this case C is a protected system symbol that serves a particular and familiar purpose. Using it for other purposes can lead to confusion. Jan 14, 2023 at 15:43
• it's not flexible enough, because it depends on the length of the previous iteration list Yes, it very explicitly depends on the length of the previous result. That's an inherent part of the semantics of what you've given us. How could you possibly remove that and get the same result? Jan 14, 2023 at 17:50
• I completely agree with @LukasLang that it is not clear what the code is supposed to do. It would help to see your equations in LaTeX form. Jan 14, 2023 at 18:10
• I am here just to say that I too agree with @LukasLang and yarchik. It would be more useful to explain what you are trying to do with equations and words rather than providing the code only
– bmf
Jan 15, 2023 at 4:00

Based on your update with the fractal demo, I think I may have a solution. The basic idea is that a triangle is split into two smaller triangles based on a parameter that determines how long the new sidelengths are as a factor of the length of the original base. In your demo you slide outward from the center of the base, so we'll also take this parameter to a factor of .5. While you slid outward from the center, one could also slide inward from the vertices. I'll provide both solutions.

Here is the basic function (out-to-in):

TriangleSplitByEdgeLength[p_][{b1 : {_, _}, a : {_, _}, b2 : {_, _}}] :=
With[
{a1 = b1 + .5 p (b2 - b1), a2 = b2 + .5 p (b1 - b2)},
{{b1, a1, a}, {a, a2, b2}}]


In your forumulation, the apex of the triangle is the third point, but I'll take the apex to be the second, just because it was easier for me to keep track.

Now we need a way to slide from whatever "zero" is to the "max" of our parameter p. At the same time, we'll deal with the nesting. We'll add a second parameter, and our implementation will split that into integer and fracional parts and use each appropriately:

TriangleSplitByEdgeLength[p_, iter_] :=
TriangleSplitByEdgeLength[
Rescale[FractionalPart[iter], {0, 1}, {0, p}]]@*
(Nest[TriangleSplitByEdgeLength[p], #, IntegerPart[iter]] &)


The nesting won't work as is, because our previous definition worked on a single triple, so we need to add a definition for working with lists of triples:

TriangleSplitByEdgeLength[args___][tris : {{{_, _}, {_, _}, {_, _}} ...}] :=
Splice@*TriangleSplitByEdgeLength[args] /@ tris


These are all of the "out-to-middle" functions. Now we can add the "middle-to-out" functions, and we'll leverage what we've already built:

TriangleSplitByVoidLength[p_] := TriangleSplitByEdgeLength[1 - p];
TriangleSplitByVoidLength[p_, iter_] :=
TriangleSplitByVoidLength[Rescale[FractionalPart[iter], {0, 1}, {0, p}]]@*
(Nest[TriangleSplitByVoidLength[p], #, IntegerPart[iter]] &)


SeedRandom[5];
init = RandomReal[1, {3, 2}]


Since I'm taking the apex as the second point, I'll alter this a bit:

init2 = {init[[1]], init[[3]], init[[2]]};

Manipulate[
Graphics[Polygon[TriangleSplitByEdgeLength[.666, s][init2]]],
{s, .01, 5.1}]


Manipulate[
Graphics[Polygon[TriangleSplitByVoidLength[.4, s][init2]]],
{s, .01, 6.1}]


UPDATE

Might as well make the parameter dynamic as well:

Manipulate[
Graphics[Polygon[TriangleSplitByEdgeLength[p, s][init2]]],
{{s, 3.1}, 0.01, 10.01},
{{p, .75}, .5, .98}]


and

Manipulate[
Graphics[Polygon[TriangleSplitByVoidLength[p, s][init2]]],
{{s, 3.1}, 0.01, 10.01},
{{p, .25}, .03, .5}]


It's kind of mesmerizing.

• It's a very nice answer and very well presented. Just one point to be made: this It's kind of mesmerizing is the understatement of the year. The appropriate description is to make an animation and start causing seizures :-)
– bmf
Jan 18, 2023 at 14:12
• @bmf Thank you! Jan 18, 2023 at 19:02
• Using Map and Flatten can avoid the 2^(n - 1).
• We seperate the times t to IntegerPart@t+ FractionalPart@t.For the IntegerPart@t we do the normal Nest of the transformation f[t]/.t->1, and for the FractionalPart@t we using the transformation f[t]
{a, b, c} = {{3, 2}, {0, 0}, {6, 0}};
split[{left_, center_, right_}][t_][{a_, b_,
c_}] := {{{1 - t, t} . {b + center (c - b), b + left (c - b)}, b,
a}, {{1 - t, t} . {b + center (c - b), b + right (c - b)}, c,
a}};
f[t_] = Flatten[#, 1] &@*Map[split[{.2, .6, .7}]@t];
Manipulate[
Graphics[
f[FractionalPart@t]@Nest[f[1], {{a, b, c}}, IntegerPart@t] //
Triangle], {t, 0, 9.5}]


• Another effect : All of the steps of spliting are random.
Clear[a, b, c, split, f, ani];
{a, b, c} = {{3, 2}, {0, 0}, {6, 0}};
split[{left_, center_, right_}][t_][{a_, b_,
c_}] := {{{1 - t, t} . {b + center (c - b), b + left (c - b)}, b,
a}, {{1 - t, t} . {b + center (c - b), b + right (c - b)}, c,
a}};
ratios[n_] :=
ratios[n] = {RandomReal[{.2, .45}], RandomReal[{.45, .55}],
RandomReal[{.55, .8}]};
f[n_][t_] := Flatten[#, 1] &@*Map[split[ratios[n]]@t];
ani = Manipulate[
Graphics[{ColorData[97][1 + Floor@t], Triangle[#]} &@
Apply[RightComposition,
Join[Table[
f[k][1], {k, 1, Floor@t}], {f[1 + Floor@t][
t - Floor@t]}]]@{{a, b, c}}], {t, 0, 9.5},
SaveDefinitions -> True]


• Very clever to use the Map + Flatten like that!!!
– bmf
Jan 21, 2023 at 5:11
• @bmf Thanks! I also upvote the answer by lericr, I think I need to take more effort to understand his skill, his's skill are more advanced then me. Jan 21, 2023 at 5:18
• lericr did an excellent work on this one, that's true. You are both much more skilled than me. I tried and failed (blushing)
– bmf
Jan 21, 2023 at 5:19
• Yours is very nice @cvgmt! I tend to overgeneralize. Jan 21, 2023 at 14:09
• Knopp's Osgood Curve Jun 11, 2023 at 10:28

This isn't complete, but I'm at a point where I need feedback. As best as I can tell, your func is doing a rescale operation, and that rescaling happens to rescale t in the range {-2.5, 2.5}. Since you've set n = 2.5, I'm wondering if you want your func to be parameterized based on n. And maybe that answers the question of how the list-length criteria changes based on func.

So, maybe this is how to write your func (just renaming so I can keep the pieces clear):

TripletShiftSplit[range_?NumberQ][t_][{a_, b_, c_}] :=
TripletShiftSplit[{-range, range}][t][{a, b, c}];
TripletShiftSplit[range : {_, _}][t_][{a_, b_, c_}] :=
{{c, a, Rescale[t, range, {b, a}]}, {b, c, Rescale[t, range, {a, b}]}}


(Maybe I went too general, but I didn't know what the rescaling range options might be. This could be simplified if we knew the constraints.)

I've used SubValues so that we can curry this function later to simplify the Nest expression. To translate,

func[{a, b, c}, 1]


{{c,a,0.7 a+0.3 b},{b,c,0.3 a+0.7 b}}

TripletShiftSplit[2.5][1][{a, b, c}]


{{c,a,0.7 a+0.3 b},{b,c,0.3 a+0.7 b}}

(after some simplifying).