1
$\begingroup$

I have a list of numbers whose length increases by 1 for every iteration of a loop. I need to take the sum of the last 5 elements of the list, and if the list's length is smaller than 1 I need to have the total sum of the existing elements (for length of 1 to 4). I have found the following crude solution, where the "ted" step is an If function.

bill := {}
Do[{bill = Append[bill, 1],
  ted = If[Length[bill] < 5, Total[bill], Total[Take[bill, -5]]]},
 {i, 1, 10}]

The numbers in the list don't matter, feel free to use distribution drawings etc.

I was looking for a faster, more elegant way to perform the same procedure. Any ideas? And thanks to everyone for the help.

$\endgroup$
1
$\begingroup$
ListCorrelate[ConstantArray[1, 5], Range[10], {5}, 0]

{1, 3, 6, 10, 15, 20, 25, 30, 35, 40}

This moves the "kernel" {1,1,1,1,1} along the list Join[ConstantArray[0, 4], Range[10]]] and forms Dot products. It should be equivalent to

ListCorrelate[ConstantArray[1, 5], Join[ConstantArray[0, 4], Range[10]]]

which might be a bit easier to understand.

If you need only the last value, then

Total[list[[Max[1, Length[list] + 1 - 5] ;;]]]

should do.

If you need that for many lists, you can try to use Compile:

data = Range /@ RandomInteger[{1, 20}, 100000];
a = Compile[{{list, _Integer, 1}},
      Total[list[[Max[1, Length[list] + 1 - 5] ;; -1]]],
      RuntimeAttributes -> {Listable}, Parallelization -> True
      ][data]; // RepeatedTiming // First

0.041

But beware that CompiledFunctions don't like empty lists. So you have to assure that data does not contain any lists of length 0. If you want to sum not only integers but real numbers, you should change {list, _Integer, 1} within Compile to {list, _Real, 1}.

$\endgroup$
  • $\begingroup$ Thank you very much, that does indeed what I had in mind with one crucial difference. In each iteration of my loop, I will need only the last result, e.g. when the list has a length of 10 I would need only 40 as output, not the other 9 elements or the entire list. I can use Part or something else, of course, but for efficiency reasons is there a way to avoid calculating the previous sums every time (i.e. in every i of the loop in my question?) $\endgroup$ – Titus May 5 '18 at 10:29
  • $\begingroup$ I had used that Total function and it is as efficient as the If function I had used (both take 7 seconds for 50k repetitions). Still, it is a compact solution. Thank you for your time! $\endgroup$ – Titus May 5 '18 at 12:45
  • $\begingroup$ You're welcome! $\endgroup$ – Henrik Schumacher May 5 '18 at 12:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.