I have implemented something I want to do in two different ways, because I'm not sure which of the two approaches give best speed/memory efficiency. Comparing the speed of the two approaches is trivial: either Timing
or AbsoluteTiming
.
Now, if I want to compare how memory efficient the two approaches are, what is the best/most telling way of doing this? Is MaxMemoryUsed[approach]
the best bet? If no, what alternatives are there?
EDIT: The reason I'm asking is that I have a recursive piece of code that permutes elements of a list (with some repeated elements), and since the elements don't commute, I need to take care of this. In the first implementation I pass the list of unused elements to the function as an argument on every call, and I thought that this should be memory-wasteful because then every list is copied. In the second implementation I just modify a global list of unused elements and do not pass it to the function. But MaxMemoryUsed[func1]
reports almost exactly the same number as MaxMemoryUsed[func2]
in all cases I have tested. Am I wrongly interpreting the results?
EDIT 2: The lists I am permuting are sorted lists of strings, where a string may contain any of the characters P,Z,W or H. Switching the order of two strings should result in a sign change according to
commutator[s1_String,s2_String]:=If[EvenQ[
StringCount[s1,"Z"|"P"]StringCount[s2,"W"|"P"]+
StringCount[s1,"W"|"P"]StringCount[s2,"Z"|"P"]
],1,-1]
Now I want to find all permutations recursively. In the first implementation:
permutationRulesRecursive[num_,sl_List,pos_]:=
If[pos==copylen,
copy[[-1]]=sl[[1]];Sow[{num,copy}];Return[],
Scan[
(copy[[pos]]=sl[[#]];
permutationRulesRecursive[
num*commutator[StringJoin@sl[[;;#-1]],sl[[#]]],Drop[sl,{#}],pos+1]
)&,
Join[{1},Accumulate[Length/@Most[Split[sl]]]+1]
]
The part Join[{1},Accumulate[Length/@Most[Split[sl]]]+1]
are the positions of the unique elements that have not yet been permuted. This function is called from:
func1[stringlist_List]:=
Block[{copy=stringlist,copylen,res},
copylen=Length[copy];
res=Reap[permutationRulesRecursive[1,copy,1]][[2,1]];
DeleteDuplicates[(StringJoin@#2->#1*(StringJoin@stringlist))&@@@res][[2;;]]
]
In this implementation, the list of elements not yet permuted, namely sl
, gets passed as an argument to each call to permutationRulesRecursive
. I think this should waste memory.
In the second implementation, I instead have:
permutationRulesRecursive[num_,pos_]:=
If[pos==copylen,
copy[[-1]]=Extract[dist,First@Position[count,1,{1},1]];
Sow[{num,copy}];Return[],
Scan[
If[count[[#]]>0,
copy[[pos]]=dist[[#]];
count[[#]]--;
permutationRulesRecursive[
num*Times@@(commutator[dist[[;;#-1]],copy[[pos]]]^count[[;;#-1]]),
pos+1
];
count[[#]]++;
]&,range]
]
and I call it from
func2[stringlist_List]:=
Block[{copy=stringlist,copylen,dist,count,range,res},
copylen=Length[copy];
{dist,count}=Transpose[Tally[copy]];
range=Range[Length[count]];
res=Reap[permutationRulesRecursive[1,1]][[2,1]];
DeleteDuplicates[(StringJoin@#2->#1*(StringJoin@stringlist))&@@@res][[2;;]]
]
In func2
I only update how many of each distinc element I have left to permute (count
) and never pass lists to the recursive function. Still, as stated above, MaxMemoryUsed[func1]
gives almost exactly the same number as MaxMemoryUsed[func]
, for instance with the stringlist
{"PH", "PH", "W", "W", "W", "W", "W", "Z", "Z"}