3
$\begingroup$

I have two set of lists:
first list (let us call it A) {121, 30, 70, 50} and
a second, list of list (call it B) {{90,10,30},{40,20,1},{10,70,20},{10,15,20}}
Following operations need to be performed to get the output list:

  1. take the first element of list A and take first list of list B, in this case 121 and {90,10,30}

  2. 121 can be divided or stacked (or filled) as 90+10+21=121 based on the list {90,10,30}, here full of 90 is used then full of 10 is used then only 21 of 30 is used. So the output for first value is {90,10,21} (Note: Elements are stacked from Left to right, ie., 10 starts filling only after 90 is fully filled and 30 starts filling only after 10 is fully filled)

    • similarly for the second items 30 and {40,20,1} only 30 out of 40 is enough and then nothing of the others so, 0 of 20 and 0 of 1, so the output is {30,0,0} such that 30+0+0=30
    • For the third items 70 and {10,70,20} full 10 out of 10 is used and then 60 of 70 and 0 of 20, so the output is {10,60,0} such that 10+60+0=70
    • For the fourth items 50 and {10,15,20} full 10 out of 10 is used and then full 15 of 15 and full 20 of 20, so the output is {10,15,20} such that 10+15+20=45. NOTE: 45 is less than 50 but that is not a problem. The aim is to fully use the list to the maximum possible amounts from Left to Right and try to come close or satisfy the number.
  3. Some Notes:
    Numbers can be float. Integers are given here for only example and explanation purpose.
    The size of the lists inside list B will all be the same, in this case 3 elements.
    The size of the list A and corresponding B is much larger (around 8670 items) than shown in the example.
    Summary:
    Inputs: {121, 30, 70, 50} ; {{90,10,30},{40,20,1},{10,70,20},{10,15,20}}
    Output: {{90,10,21},{30,0,0},{10,60,0},{10,15,20}}

Fun Fact: This logic is used in real life to dispatch power plants to supply electricity demand and it is called stack dispatch model. List A is the demand at a given hour, and list B are power plants available at that hour. Output is how much of each power plant is operated to satisfy the demand.

I am looking for a simple and efficient code for this operation. Any help is appreciated.
Thank you in advance.

$\endgroup$

5 Answers 5

4
$\begingroup$
g = Differences @ Prepend[0] @ Clip[Accumulate @ #2, {0, #}] &;

MapThread[g] @ {a, b}
{{90, 10, 21}, {30, 0, 0}, {10, 60, 0}, {10, 15, 20}} 
$\endgroup$
3
$\begingroup$

Here is a first attempt:

a = {121, 30, 70, 
  50}; 
b = {{90, 10, 30}, {40, 20, 1}, {10, 70, 20}, {10, 15, 20}}

Define function:

f[x_, y_] := Module[{w},
  w = SplitBy[FoldList[Subtract, x, y], Sign][[1]];
  ArrayReshape[-Differences@Append[w, 0], 3, 0]
  ]

Apply to data:

MapThread[f, {a, b}]

yields:

{{90, 10, 21}, {30, 0, 0}, {10, 60, 0}, {10, 15, 20}}

$\endgroup$
3
$\begingroup$

Finally, got the chance to use FoldPairList :D - Even though the solution below is simpler, it's a little slower than @ubpdqn's answer:

Define the function

ClearAll[temp];

temp[n_, values_] := FoldPairList[{Min[##], #1 - Min[##]} &, n, values];

Apply to data

MapThread[temp, {a, b}]

(* Out: {{90, 10, 21}, {30, 0, 0}, {10, 60, 0}, {10, 15, 20}} *)
$\endgroup$
2
$\begingroup$
a = {121, 30, 70, 50};
b = {{90, 10, 30}, {40, 20, 1}, {10, 70, 20}, {10, 15, 20}};

accumulateWhile[{a_Integer, b_List}] :=
 Differences@FoldList[If[#1 + #2 <= a, #1 + #2, a] &, 0, b]

accumulateWhile /@ Transpose[{a, b}]

{{90, 10, 21}, {30, 0, 0}, {10, 60, 0}, {10, 15, 20}}

$\endgroup$
2
$\begingroup$
 a = {121, 30, 70, 50};
b = {{90, 10, 30}, {40, 20, 1}, {10, 70, 20}, {10, 15, 20}};
Transpose@Ramp[# - Ramp[Accumulate@# - Threaded@a]] &@Transpose@b
{{90, 10, 21}, {30, 0, 0}, {10, 60, 0}, {10, 15, 20}}
n = 10^6;
a = RandomReal[{1, 100}, n];
b = RandomReal[{100, 200}, {n, 3}];

f = Transpose@Ramp[#2 - Ramp[Accumulate@#2 - Threaded@#1]] &;
g = Differences@Prepend[0]@Clip[Accumulate@#2, {0, #}] &;

r1 = MapThread[g]@{a, b}; // RepeatedTiming
r2 = f[a, Transpose@b]; // RepeatedTiming
r1 == r2
{2.84142, Null}
{0.047036, Null}
True
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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