简体   繁体   中英

How can I scale an array of values while maintaining a minimum value?

So I have an array of values that I need to scale down while maintaining a minimum value for the scaled value.

For example, let's say I have an array of values [1, 1, 3, 5] with a minimum scale factor of .2 .

By normalizing the array, we get the array [.1, .1, .3, .5] . However, keeping in mind the minimum scale factor, we'd have values [.2, .2, .3, .5] , which adds up to 1.2 , not 1 .

My thinking was to iterate over the array and first set all values that would be under the minimum to the minimum, keeping a carry variable to determine how much still needs to be redistributed to the other elements in the array that were over the minimum.

With all of the values that were over the minimum, scale their values with respect to the carry variable and then subtract that from their values.

So with the example above, we'd subtract 3/8 * .2 from .3 , and 5/8 * .2 from .5 to get [.2, .2, .225, .375] .

Is there any other way to do this more efficiently? Or an alternative way to scale the remaining values?


Edit: Sorry, scaling might be the incorrect term, but in the end the values of the array are to be divided in such a way that their values are changed with respect to the total value.

I'll explain the specific implementation so that the question might be more clear:

I have a number of posts, and each of the posts is to be shown for a certain amount of time before fading out, after which the next post is to be shown. I want the delay between posts to be dependent on the number of words within each post, but also constrained to be at least some minimum value.

There is a total amount of time for all of the posts to be shown, and the time is supposed to be split up between all of the posts.

I want the delay between posts to be dependent on the number of words within each post, but also constrained to be at least some minimum value.

There is a total amount of time for all of the posts to be shown, and the time is supposed to be split up between all of the posts.

You cannot guarantee that you'll meet both requirements. If you have 30 posts, each of which must be displayed for at least one second, and only 20 seconds in which to display them, then it's impossible meet both requirements. You'll have to:

  1. Extend the total time; OR
  2. Reduce the minimum time

We have a sample set s = [ 1 1 3 5 ] , and we are looking for a function f(x) which takes a single sample and returns the display time.

Requiring Sum(Map(s, f)) = 1.0 , (that is, the sum of f(s) for all s[i]) and also that s[i] >= minVal for all s[i] , consider first the linear function

f(x) = ax + b

For the minimum

a.xmin + b = minVal
b = minVal - a.xmin

Sum:

total = Sum(f(x) for x in s)
      = Sum((a*x + b) for x in s)
      = b*len(s) + Sum(a*x for x in s)
      = b*len(s) + a * Sum(s)
1 = b*len(s) + a * Sum(s)
a = (b * len(s) - 1.0) / Sum(s)

Substit

1 = b*len(s) + a * Sum(s)
1 = (minVal - a.xmin) * len(s) + a * Sum(s)
1 = minVal * len(s) - xmin * len(s) * a + Sum(s) * a
1 - (minVal * len(s)) = (Sum(s) - xmin*len(s)) * a
a = (1 - (minVal * len(s))) / (Sum(s) - xmin*len(s))

Given a,

b = minVal - a.xmin    

Thus in javascript we can have a function factory to give you a scaling function f, given a sample set s:

function makeScalingFun(s, minVal) {
   var total = s.reduce(function(a, b) { return a + b; });
   var xmin = s.reduce(function(a, b) { return Math.min(a,b); });
   // f(x) = ax + b
   var a = (1.0 - (minVal * s.length)) / (total - xmin * s.length)
   var b = minVal - a * xmin
   var f = function(x) {
      return a * x + b;
   };
   return f;
}

And in use:

var scaler = makeScalingFun(s, 0.2);
console.log("Index, Value: Calced Delay");
for(var i = 0; i < s.length; ++i) {
    console.log(i + ", " + s[i] + ": " + scaler(s[i]));
}

Result:

Index, Value: Calced Delay
0, 1: 0.2
1, 1: 0.2
2, 3: 0.26666666666666666
3, 5: 0.3333333333333333 

If you have further requirements, you could use them to construct a quadratic target function instead of a linear one, etc.

Note that the smallest value always gets given the minVal delay, which isn't perhaps very realistic. Consider modifying to use a constant value for xmin, eg 0, so that if you have 3 posts of 450, 451 and 452, you don't just get a comparatively tiny delay for the first just because it's the shortest.

I would take the following approach:

Assume you have the values as in your example: [1 1 3 5] . The sum of the values is 10 .

So, divide all values by 10 and correct the values under the minimum. Keep track of the number of corrected values. In this case that's 2 . Multiply 2 by .2 . That is .4 .

Now, 1 - .4 = .6 should be divided over the values 3 and 5 . The sum of 3 and 5 is 8 . So, divide each uncorrected original value by 8 and multiply it by .6 : eg 3 / 8 * .6 .

That will give you your normalized list of values: [.2 .2 .225 .375] .

Obviously you can't scale the terms in the usual sense of the word, as you may end up with times less than your minimum time. Also you will have trouble if the number of posts * minimum time exceeds your total time available.

But assuming not...I would suggest giving each post the minimum time plus extra time proportional to the number of extra words in the longer documents. So, given the values [1 1 3 5] and the requirement that the minimum is 0.2...

1) Subtract the minimum value from each value to give [0 0 2 4]
2) Normalize this to give [0 0 0.333 0.667]
3) Scale by (1 - 4*0.2) (that is 1 - number_of_values*min_time) to give [0 0 0.0667 0.133333]
4) Finally add the minimum to each value to give [0.2 0.2 0.267 0.333]

Now each post gets a base time plus an extra time proportional to its extra length.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM