0

THE PROBLEM

Imagine the following scenario:

There is a numeric value representing a wattage:

var w = 1000; // 1000 watts

Then there is an array containing a variety of transformers, covering certain wattages:

var transformers = [240, 200, 100, 50, 20];

The goal is to create an array that contains any number of values from transformers needed to cover for the wattage needs as defined per w.

However, the resulting array must contain as few elements as possible, thus large numbers come first. However, there must also be minimal loss, so whatever is left over from division, should be covered by the smallest amount (from transformers) as possible.

The goal is simply to compute the transformers needed to cover for the supplied wattage, with minimal loss (use a smaller transformer when possible, but use the fewest amount of transformers altogether).

EXAMPLE 1

w = 1000;
transformers = [260, 200, 100, 50];
results = [260, 260, 260, 260];

EXAMPLE 2

w = 1042;
transformers = [260, 200, 100, 50];
results = [260, 260, 260, 260, 50];

EXAMPLE 3

w = 502;
transformers = [220, 180, 60, 30];
results = [220, 220, 180];

MY APPROACH

Of course i tried to solve this myself, but due to a lack of mathematical computing capabilities of my brain, i failed miserably.

My approach was this:

  1. Create an array that contains the available wattages, sorted in descending order
  2. Loop over a total wattage requirement (a copy of w) until the total wattage is 0 or lower
  3. Divide the total wattage requirement by the largest transformer available
    1. If division result is larger than 1, push the current transformer to the results array and subtract the current wattage from the total wattage
    2. If division result is smaller than 1, shift the array of transformers to remove the largest transformer in the array
  4. If array of transformers has only one element left, push that to the results array and set the total wattage to 0

The results came close to correct, but in some cases turned out to be wrong as more optimal choices for covering the wattage could have been made. I also had cases when there was uncovered for wattage left.

My Code

// test values:
var wattages = [264, 100, 60, 35, 18];
var _wattageTotal = 1000; // 1000 watts

var _trafos=new Array(); // for the results

console.log('-----------------------');
console.log('THESE ARE THE AVAILABLE WATTAGES:');
console.log(wattages);

while(_wattageTotal){

    console.log('WATTAGE TOTAL: '+_wattageTotal);
    console.log('TESTING: '+wattages[0]+' against total wattage => '+_wattageTotal/wattages[0]);

    if( _wattageTotal/wattages[0] >= 1 ) {

        console.log('==> FOUND FIT: '+wattages[0]);
        _trafos.push( byWattage[ wattages[0] ].key );
        _wattageTotal-=wattages[0];

    } else {

        console.log(wattages[0]+' DOES NOT FIT');
        wattages.shift();

        if(wattages.length==1){
            _trafos.push( byWattage[ wattages[0] ].key );
            _wattageTotal=0;
        }

    }

}
SquareCat
  • 5,699
  • 9
  • 41
  • 75
  • could you post your code? – Nicolas Straub Nov 14 '14 at 00:26
  • It's fairly specific so i wanted to avoid that, plus i don't feel i have a very smart approach there and i am sure there are better ways to do it, but sure. Will do in a minute. – SquareCat Nov 14 '14 at 00:30
  • it seems correct... could you elaborate on the cases where it turned out to be wrong? – Nicolas Straub Nov 14 '14 at 00:36
  • Sounds like the [subset sum problem](https://en.wikipedia.org/wiki/Subset_sum_problem) with additional requirements, needing to find the smallest subset that solves it optimally. I think you should try to simply brute force it unless your number of transformers is substantially high. If that's the case, have fun! – Bergi Nov 14 '14 at 00:36
  • Observation: the solution where you only use the largest transformer always results in the minimal number of transformers used -- which is the primary criterion. Do this first, then swap one of these out with the smallest one which can still cover the total wattage requirement. Then try swapping out the next one. Do this until you can't swap any out. Should guarantee the right solution. – Saran Tunyasuvunakool Nov 14 '14 at 00:41
  • Sounds similar to the knapsack problem... maybe you should check an algoritm solving it. – chris-l Nov 14 '14 at 00:44
  • Instance A: i get [60, 18] when i feed with 83.2 (which means there's an uncovered leftover. Instance B: i get [100, 18] when i feed with 124.8 although using one 264 unit would have been the more optimum solution. Please note: i edited the available wattages in my example code to match my live values. – SquareCat Nov 14 '14 at 00:44
  • @chris-l Thanks, problem is i am not built to do this. My brain simply denies computing such stuff. I am good with programming but mathematics is absolutely not my thing. I tried real hard to solve this but i can't look at the problem anymore, it's killing me :) – SquareCat Nov 14 '14 at 00:45
  • @Bergi Bruteforce? Not sure what you mean by that in this case. The number of transformers is not too large. Right now the maximum is 5. – SquareCat Nov 14 '14 at 00:52
  • @SquareCat: Just create [all possible subsets](http://stackoverflow.com/a/15649645/1048572), iterate over them and find the best (which has enough power, and has minimal amount of used tranformers). – Bergi Nov 14 '14 at 02:15
  • Hey, that's a pretty funky approach, ... actually, that's what i thought. On a second note though, i realize this is not an option, because there can be cases with multiple trafos of the same wattage being used/required (such as when you need to cover for 2000 watts). So this approach won't work :( – SquareCat Nov 14 '14 at 02:18
  • Downvoter: if you provided some information on why you chose to downvote, it would help me AND the community. IMO this was a totally pointless downvote. – SquareCat Nov 14 '14 at 03:13

2 Answers2

0

don't set _wattageTotal to 0. When you do it exits the while loop, and any leftover not covered by your current _trafos will be ignored

you also need to shift the current wattage after performing the check for length =1, and check for _wattageTotal > 0 in your while loop:

var wattages = [264, 100, 60, 35, 18];
var _wattageTotal = 82; // 1000 watts

var _trafos=new Array(); // for the results

console.log('-----------------------');
console.log('THESE ARE THE AVAILABLE WATTAGES:');
console.log(wattages);

while(_wattageTotal > 0){

    console.log('WATTAGE TOTAL: '+_wattageTotal);
    console.log('TESTING: '+wattages[0]+' against total wattage => '+_wattageTotal/wattages[0]);

    if( _wattageTotal/wattages[0] >= 1 ) {

        console.log('==> FOUND FIT: '+wattages[0]);
        _trafos.push( wattages[0] );
        _wattageTotal-=wattages[0];

    } else {

        console.log(wattages[0]+' DOES NOT FIT');

        if(wattages.length==1){
            _trafos.push(  wattages[0]  );
            _wattageTotal -= wattages[0];
        }

        wattages.shift();
    }

}

jsfiddle

Nicolas Straub
  • 3,381
  • 6
  • 21
  • 42
  • If you look at my code, i am only setting `_wattageTotal` to `0` if there is only one available trafo left in the array. While you are correct that this is actually a problem and shouldn't be done, it is not the solution to the problem per sé (whish it was). In my tests the results come out just the same if i remove that line, actually. – SquareCat Nov 14 '14 at 00:49
  • seems I jumped the gun a bit... updating answer. – Nicolas Straub Nov 14 '14 at 01:07
  • Gives me an infinite loop. – SquareCat Nov 14 '14 at 01:58
  • In your fiddle, the result is incorrect. It says [60,18,18] but the correct pick would be [100]. As you can see it's a very tricky thing to compute this and cannot simply be solved by hit and miss (been there, done that). Many thanks though for your trying to help! – SquareCat Nov 14 '14 at 02:11
  • yeah, I figured that much out. the solution is rather complex and involves checking the result against the original array to see if you can replace more than one trafo with another trafo, and doing that recursively until there are no more candidates available to replace. Maybe you could build on that thought. I'm off to bed now and would be happy to do it tomorrow morning... – Nicolas Straub Nov 14 '14 at 02:33
0

After more in-depth research, testing, some failed approaches and a number of coffees, here's the final result.

// test values

var wattage=600;
var trafos=[264, 100, 60, 35, 18];

var g, i, results=[];

// since wattage can also end up below zero,
// we loop until it is no longer greater than zero

while(wattage>0){

    // each run, we want an empty array g

    g=[];

    // we iterate over all available trafos

    for (i in trafos){

        // g will contain our division factors, but since
        // we want to be able to sort this array, we must
        // use an object literal

        g.push({

            // g.k = the factor of the division (total wattage / trafo wattage)

            k: wattage/trafos[i],

            // g.v = the trafo wattage, as reference, so we know which 
            // division factor belongs to which trafo

            v: trafos[i]

        });

    }

    // now, g will contain the division factors for all trafo wattages
    // against the current wattage that needs to be covered
    // naturally these will range from 0 to X
    // we then use a sort function to sort this array
    // by which values are closest to 1 (= which trafos fit best for the current
    // wattage) but with the values SMALLER than 1 having more priority than
    // the ones being greater

    g.sort(function(a, b){
        var dis=Math.abs(1-a.k) - Math.abs(1-b.k);
        if(a.k<1 && b.k>=1){ return -1; }
        if(a.k>=1 && b<1){ return 1; }
        return dis;
    });

    // naturally, the first element of g will now contain the trafo
    // which covers best for the wattage that needs to be covered for,
    // so we push its wattage (using g.v) into the results array

    results.push( g[0].v );

    // finally we can now subtract the wattage from the total wattage
    // that still needs to be covered for

    wattage -= g[0].v;

}

ADVANCED EXPLANATION

Instead of using a shrinking array. we take a different approach: for each value we check and match we create an array that contains the factors of the corresponding division.

We then sort that array using a pretty magic sort function which does two things:

  • It sorts values by how close the values are to 1
  • with a preference to values that are smaller than one, rather than those that are greater

(Credits for this go to user BatScream who provided this function in response to my question Sorting an array by which value is closest to 1)

I initially came up with this solution using the sort function, but only sorting the values by which ones were closest to 1. This worked, but still resulted in non-optimal computations such as this:

wattage = 600;
trafos=[264, 100, 60, 35, 18];

result = [264, 264, 60, 18];

Where as the best result would be:

result = [264, 264, 100];

Therefore the goal was to adapt the sort function accordingly, so that it would still pick the largest trafo available for the still to-cover-for wattage.

Now that my brain is fried, i can finally rest in peace.

Community
  • 1
  • 1
SquareCat
  • 5,699
  • 9
  • 41
  • 75