提出一个简单的想法的好算法

时间:2012-09-22 01:27:03

标签: javascript algorithm underscore.js

我需要提出一个执行以下操作的算法:

假设您有一系列正数(例如[1,3,7,0,0,9])并且事先知道它们的总和是20。

您希望从每个数字中抽取一些平均金额,以使新金额少于7。

为此,您必须遵循以下规则

  • 你只能减去整数
  • 生成的数组不得有任何负值
  • 您无法对存储桶的索引进行任何更改。

减法越均匀地分布在数组上越好。

这是我尝试使用JavaScript +下划线的算法(这可能会使它成为n ^ 2):

function distributeSubtraction(array, goal){
    var sum = _.reduce(arr, function(x, y) { return x + y; }, 0);
    if(goal < sum){
      while(goal < sum && goal > 0){
         var less = ~~(goal / _.filter(arr, _.identity).length); //length of array without 0s
         arr = _.map(arr, function(val){ 
            if(less > 0){
                return (less < val) ? val - less : val; //not ideal, im skipping some! 
            } else {
                if(goal > 0){ //again not ideal. giving preference to start of array
                    if(val > 0) {
                        goal--;
                        return val - 1;
                    }
                } else {
                    return val;
                }
            }
         });
         if(goal > 0){
             var newSum = _.reduce(arr, function(x, y) { return x + y; }, 0);
             goal -= sum - newSum;
             sum = newSum;
         } else {
            return arr;
         }
      }
    } else if(goal == sum) {
      return _.map(arr, function(){ return 0; });
    } else {
      return arr;
    }
}
var goal = 7;
var arr = [1,3,7,0,0,9];
var newArray = distributeSubtraction(arr, goal);
//returned: [0, 1, 5, 0, 0, 7];

嗯,这有效,但必须有更好的方法!我想,对于更大的数组和更大的数字,这个东西的运行时间会很糟糕。

编辑:我想澄清这个问题纯粹是学术性的。可以把它想象成一个面试问题,在这个问题上你可以使用白板,面试官会问你算法在不同类型的数据集上的行为。

2 个答案:

答案 0 :(得分:1)

听起来你想从每个数字中减去加权数量。 I.E您想从每个项目中减去X/sum * amount_to_subtract。你当然需要减去你减去的金额。问题是确保你减去了正确的总金额。此外,这取决于您的输入:您是否保证减去减去的金额?这是一个粗略的python实现,(我认为):

def uniform_array_reduction(inp, amount):
  total = sum(inp)
  if amount > total:
    raise RuntimeError('Can\'t remove more than there is')

  if amount == total: #special case
    return [0] * len(inp)

  removed = 0
  output = []
  for i in inp:
    if removed < amount:
      to_remove = int(round(float(i)/float(total)*float(amount)))
      output.append(i - to_remove)
      removed += to_remove
    else:
      output.append(i)
  # if we didn't remove enough, just remove 1 from
  # each element until we've hit our mark.
  # shouldn't require more than one pass
  while removed < amount:
    for i in range(len(output)):
      if output[i] > 0:
        output[i] -= 1
        removed += 1
        if removed == amount:
          break
  return output
编辑:我修复了代码中的一些错误。

答案 1 :(得分:1)

s = Sum(x) - required_sum
do:
    a = ceil( s/number_of_non_zeros(x) )
    For i=1 to length(x):
        v = min(a, x[i], s)
        x[i]-=v
        s-=v
while s>0

此版本无需排序。