5

This is a follow-up question from Sum of Multinomial Coefficients

I have thought about the meaning of the formula I mentioned and, with help, I implemented the following code:

supp[vec_] := Module[{support = {}, i},
  Do[If[vec[[i]] != 0, AppendTo[support, i]], {i, 1, Length[vec]}];
  support
];

calctrafo[n_, func_] := Module[{vecs, trafo = 0, i},
  vecs = Tuples[Range[0, (n - 1)], n];
  vecs = Select[vecs, Total[#] == (n - 1) &];
  Do[trafo += (Multinomial @@ vecs[[i]])*func[supp[vecs[[i]]]], {i, 1, Length[vecs]}];
  trafo
];

calctrafo[7, func]

The function supp gives me the support of the lists and func is a arbitrary function. This code works well for me, but I need the code to work for large n, n >= 100. The problem lies in the function Tuples, which crashes for n > 6. Is there a way to make this work for large n?

rainer
  • 401
  • 4
  • 12
  • As I understand you the problem is that your very large result list from tuples will not fit in memory. If that is the case, you could perhaps use an iterative approach and something like http://mathematica.stackexchange.com/questions/9554/lazy-lists-of-tuples-and-subsets – jVincent Mar 01 '13 at 08:43
  • @jVincent Thanks for the Link. But do I miss something, because I tried the first code in your answer but that it did not work. – rainer Mar 02 '13 at 09:29
  • There was a syntax change in the length test. You should be able to run it now. – jVincent Mar 04 '13 at 08:48
  • Thanks! I edited my code and tried to create the tuples "on the fly" but for let's say for Tuples[Range[0,20],19] I loop through all possible Tuples. But there are approx. 19^20 Tuples and mathematica can't handle such a large number. Is there any other way? – rainer Mar 05 '13 at 08:06
  • Yes. If you go from the code in that question you could simply do allmyTuples=lazyTuples[Range@20,19]; which will not actually calculate them all yet, then if you call for instance allmyTuples[[21312312841789283727]] it will return only that one tuple, without having calculated all the others. If you want to iteraet over the tuples, the length can be found by calling Length[allmyTuples]. – jVincent Mar 05 '13 at 09:12
  • Yes, I did that, but i need to apply a function to the every tuple, and if I loop through all tuples eg Do[func[lazyTuples[Range@20,19][[i]]],{i,1,19^20}]. Mathematica tells me it can't evaluate it. – rainer Mar 05 '13 at 10:22
  • You misspelled the function. It's LazyTuple not LazyTuples, and while such an evaluation will take quite a while to finish due to the 37,589,973,457,545,958,193,355,601 iterations it needs to go through, it should be possible as long as you are not using up to much memory in your func calls. I would suggest you start out trying to calculate how long it should approximately take before just blindly running the code and waiting though. – jVincent Mar 05 '13 at 11:13
  • I think my function is really using to much memory because I get the following error Do::iterb: "Iterator {i,1,(n-1)^(n+1)} does not have appropriate bounds". I looking for all tuples Tuples`[Range[0,n],(n-1)] where the sum is equal to (n-1), calculate with these the Multinomial Coefficient and apply a function. Since I don't have any cs background I thought I seek help from you guys. – rainer Mar 05 '13 at 20:56
  • That error message seems to suggest that you haven't defined n meaning that the iterator doesn't have appropriate bounds since it doesn't know what (n-1)^(n+1) is. You should make sure you have set n to a value when you do this loop. – jVincent Mar 06 '13 at 09:24
  • No, actually n has a value: n = 20; Do[tmp = lazyTuple[Range[0, n], (n - 1)][[i]]; If[Total[tmp] == (n - 1), Print[(Multinomial @@ tmp)*func[supp[tmp]]]], {i, 1, (n + 1)^(n - 1)}] (*20 Do::iterb...*) where supp is the function provided by Simon Woods – rainer Mar 06 '13 at 16:20
  • Ahh, seems that by "inappropriate" it means that the bound is to large. It works up to n=16 so it's properly just to large for it to work with. Try running a while loop instead, and again I would advice trying to estimate total run time by running a smaller number of iterations. – jVincent Mar 06 '13 at 16:44

1 Answers1

10

Instead of creating a list of all tuples and then selecting those whose total is n-1, you could start with the IntegerPartitions of n-1, pad them to length n with zeroes, and create all the permutations:

getvecs[n_] := Flatten[Permutations[PadRight[#, n]] & /@ IntegerPartitions[n - 1], 1]

I would also suggest using Position for the support function:

supp2[vec_] := Flatten @ Position[Unitize @ vec, 1]

and replacing the Do loop with Dot:

calctrafo2[n_, func_] := With[{vecs = getvecs[n]},
  (Multinomial @@@ vecs).(func[supp2[#]] & /@ vecs)]

This is considerably faster:

Timing[calctrafo[7, Total]]
(*  {4.375, 1987804}  *)

Timing[calctrafo2[7, Total]]
(*  {0.031, 1987804}  *)
Simon Woods
  • 84,945
  • 8
  • 175
  • 324