int数组之间更好的总和

时间:2013-03-18 10:39:56

标签: arrays algorithm sum max

我有这个数组

int arr = new int[] { 978, 978, 978, 978, 978, 978, 978, 978, 978, 978, 978, 978, 978, 978, 
978, 978, 978, 978, 978, 978, 696, 696, 696, 696, 696, 696, 696, 696, 696, 696, 696, 696, 
696, 696, 696, 696, 696, 696, 696, 696, 678, 678, 678, 678, 678, 678, 678, 678, 678, 678, 
446, 446, 446, 446, 446, 446, 446, 446, 446, 446 };

20 elements 978
20 elements 696
10 elements 678
10 elements 446

我需要找到最好的总和直到6000。

Fox示例:

978 + 978 + 978 + 978 + 696 + 696 + 696 = 6000

这是一个最好的组合。

找到最佳组合后,我需要删除相加的元素以再次找到最佳总和。

在这种情况下,我的数组将是:

16 elements 978
17 elements 696
10 elements 678
10 elements 446

然后,下一个更好的总和将是:

 978 + 978 + 978 + 978 + 696 + 696 + 696 = 6000

然后:

978 + 978 + 978 + 978 + 696 + 696 + 696 = 6000

然后:

978 + 978 + 978 + 978 + 696 + 696 + 696 = 6000

此时我的阵列是:

4 elements 978
8 elements 696
10 elements 678
10 elements 446

现在,我最好的总和是:

696 + 696 + 696 + 696 + 696 + 696 + 696 + 678 + 446 = 5996

我的阵列:

4 elements 978
1 elements 696
9 elements 678
9 elements 446

嗯......我需要做总和,直到我的数组为空。

任何消化?

可以是js,c#或vb。

3 个答案:

答案 0 :(得分:0)

这是我对您的任务的单次迭代的解决方案。我将在时间O(m * n)中找到“最佳匹配”组合,其中m是目标数(在您的情况下为6000),n是数组中剩余的元素总数(初始迭代中为53) )。我的解决方案的内存复杂性为O(m)

我将使用一个辅助符号:

  • parent[m + 1] - 存储用于形成特定总和的最后一个数字。如果总和尚未实现,则值为-1
  • maxm - arr
  • 中的最大数量
  • list<int> chosen - 为特定迭代选择的数字
  • best根据您的定义最佳总和

这是我的解决方案的伪代码

parents[i] = { -1 if i != 0; -2 otherwise} // distinguish the empty sum
for (i = 0; i < n; i++)
  for (j = m + maxm; j > 0; j--)
    if parents[j] == -1 && j - arr[i] >= 0 && arr[i] != -1
       parents[j] = i

// choose the best; choose only numbers with parents[i] != -1
for i = 0; i < m + maxm; i++
   if parents[m + i] != -1
      best = m + i
      break
   else if parents[m - i] != -1
      best = m - i
      break
while best != 0 
   chosen.add(best - parents[best])
   best = parents[best]

编辑根据您的定义添加best的计算。

答案 1 :(得分:0)

..这是我在C#中的代码:

Dictionary ex = new Dictionary<Integer,Short>();

ex.Add(978, 20); // 20 elements 978
ex.Add(696, 20); // 20 elements 696
ex.Add(678, 10); // 10 elements 678
ex.Add(446, 10); // 10 elements 446

使用时间效率更高,而不是所有元素的数组,而不是每次计算总和,我已经预先计算了总和:

class Ex
{
    Dictionary<Integer,Short> ex;
    long sum;

    public Ex ()
    {
        ex = new Dictionary<Integer,Short>();
        sum = 0;
    }

    public void add (int number, short frequency)
    {
        ex.Add(number, frequency);
        sum += (number * frequency);
    }

    public void remove (int number)
    {
        sum -= (number * ex.Get(number));
        ex.Remove(number);
    }

    public void increment (int number, short frequency)
    {
        ex.Set(number, ex.Get(number)+frequency);
        sum += (number * frequency);
    }

    public void subtract (int number, short frequency)
    {
        ex.Set(number, ex.Get(number)-frequency);
        sum -= (number * frequency);
    }

    public long getSum ()
    {
        return sum;
    }
}

希望这会有所帮助..

答案 2 :(得分:0)

好的我不记得我是如何偶然发现这个话题的,但我想我已经设法找到一个快速的算法来解决这个问题。至少用给定的数据。我的解决方案是JS,我觉得更舒服。它可以在不到7毫秒的时间内解决,但我相信它可以简单地转换成Haskell或C,如果需要更快的话。

好的,先看看解决方案......

function getSum(arr,sum){
  function getCombinations(arr){
    var len = arr.length,
       subs = Array(Math.pow(2,len)).fill();
    return subs.map((_,i) => { var j = -1,
    	                           k = i,
                                 res = [];
                               while (++j < len ) {
                                 k & 1 && res.push(arr[j]);
                                 k = k >> 1;
                               }
                               return res;
                             }).slice(1);
  }
  function getPossibles(a,t){
    var fo = a[0],
    maxTry = Math.min(Math.floor(t/fo.n),fo.c);

    return a.length > 1 ? Array(maxTry).fill()
                                       .map((_,i) => ({n:fo.n, c:maxTry-i}))
                                       .reduce((p,c) => (p.push(...getPossibles(a.slice(1),t-c.n*c.c).map(e => a.length > 2 ? [c,...e]
                                                                                                                            : [c,e])),p),[])
                        : [{n:fo.n, c: maxTry}];
  }
  var  hash = arr.reduce((p,c) => (p[c] ? p[c]++ : p[c] = 1,p),{}),
   condense = Object.keys(hash)
                    .reverse()
                    .map(k => ({n: k, c:hash[k]}));
     combos = getCombinations(condense);
  possibles = combos.reduce((p,c) => (c.length > 1 ? p.push(...getPossibles(c,sum))
                                                   : p.push(getPossibles(c,sum)),p),[]);
  return possibles.filter(p => p.reduce((s,o) => s += o.n*o.c,0) === sum)
                  .map(e => e.reduce((p,c) => p.concat(Array(c.c).fill(c.n)),[]));
}

var arr = [978, 978, 978, 978, 978, 978, 978, 978, 978, 978, 978, 978, 978, 978, 978, 978, 978, 978, 978, 978, 696, 696, 696, 696, 696, 696, 696, 696 ,696, 696, 696, 696, 696, 696, 696, 696, 696, 696, 696, 696, 678, 678, 678, 678, 678, 678, 678, 678, 678, 678, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446],
 result = [],
     ps = 0,
     pe = 0;
ps = performance.now();
result = getSum(arr,6000);
pe = performance.now();
console.log(result);
console.log("Done in:", pe-ps);

我试图让代码尽可能明确。我将尝试在这里逐步解释。

我们有一个函数来解决这个名为getSum()的问题,其中我们有两个实用函数getCombinations()getPossibles()

一旦我们收到我们的值数组(arr)和目标(sum),我们首先在hash下建立一个哈希表,结果就像是;

{ '446': 10, '678': 10, '696': 20, '978': 20 }

显然,通过告诉哪个数字存在多少次来给我们提供数组的映射。

然后我将数组重新映射为更简单的对象形式,并将其存储在condense

[ { n: '978', c: 20 },
  { n: '696', c: 20 },
  { n: '678', c: 10 },
  { n: '446', c: 10 } ]

此时我们需要获得这些对象的组合,以便我们以后可以解决所有可能性。对于此任务,我们使用getCombinations()实用程序功能。结果如下;

[ [ { n: '978', c: 20 } ],
  [ { n: '696', c: 20 } ],
  [ { n: '978', c: 20 }, { n: '696', c: 20 } ],
  [ { n: '678', c: 10 } ],
  [ { n: '978', c: 20 }, { n: '678', c: 10 } ],
  [ { n: '696', c: 20 }, { n: '678', c: 10 } ],
  [ { n: '978', c: 20 }, { n: '696', c: 20 }, { n: '678', c: 10 } ],
  [ { n: '446', c: 10 } ],
  [ { n: '978', c: 20 }, { n: '446', c: 10 } ],
  [ { n: '696', c: 20 }, { n: '446', c: 10 } ],
  [ { n: '978', c: 20 }, { n: '696', c: 20 }, { n: '446', c: 10 } ],
  [ { n: '678', c: 10 }, { n: '446', c: 10 } ],
  [ { n: '978', c: 20 }, { n: '678', c: 10 }, { n: '446', c: 10 } ],
  [ { n: '696', c: 20 }, { n: '678', c: 10 }, { n: '446', c: 10 } ],
  [ { n: '978', c: 20 },
    { n: '696', c: 20 },
    { n: '678', c: 10 },
    { n: '446', c: 10 } ] ]

所以现在我们知道我们会尝试什么。但我们必须巧妙地尝试。我们知道目标总和为6000,我们不应该允许超额计算。这是棘手的部分。我们有getPossibles()实用程序函数,它只返回具有count(c属性)值的对象,这些值可以总计或小于6000.好的我已经影响了我的Array.prototype.cartesian()工具工作正常。

Ok getPossibles()将获取一系列对象(精简数据)并将为我们提供总和小于6000的集合的可能组合。例如,对于[ { n: '978', c: 20 }, { n: '696', c: 20 }, { n: '678', c: 10 } ]的组合,我们将收到; < / p>

[ [ { n: '978', c: 5 }, { n: '696', c: 1 }, { n: '678', c: 0 } ],
  [ { n: '978', c: 4 }, { n: '696', c: 3 }, { n: '678', c: 0 } ],
  [ { n: '978', c: 4 }, { n: '696', c: 2 }, { n: '678', c: 1 } ],
  [ { n: '978', c: 4 }, { n: '696', c: 1 }, { n: '678', c: 2 } ],
  [ { n: '978', c: 3 }, { n: '696', c: 4 }, { n: '678', c: 0 } ],
  [ { n: '978', c: 3 }, { n: '696', c: 3 }, { n: '678', c: 1 } ],
  [ { n: '978', c: 3 }, { n: '696', c: 2 }, { n: '678', c: 2 } ],
  [ { n: '978', c: 3 }, { n: '696', c: 1 }, { n: '678', c: 3 } ],
  [ { n: '978', c: 2 }, { n: '696', c: 5 }, { n: '678', c: 0 } ],
  [ { n: '978', c: 2 }, { n: '696', c: 4 }, { n: '678', c: 1 } ],
  [ { n: '978', c: 2 }, { n: '696', c: 3 }, { n: '678', c: 2 } ],
  [ { n: '978', c: 2 }, { n: '696', c: 2 }, { n: '678', c: 3 } ],
  [ { n: '978', c: 2 }, { n: '696', c: 1 }, { n: '678', c: 4 } ],
  [ { n: '978', c: 1 }, { n: '696', c: 7 }, { n: '678', c: 0 } ],
  [ { n: '978', c: 1 }, { n: '696', c: 6 }, { n: '678', c: 1 } ],
  [ { n: '978', c: 1 }, { n: '696', c: 5 }, { n: '678', c: 2 } ],
  [ { n: '978', c: 1 }, { n: '696', c: 4 }, { n: '678', c: 3 } ],
  [ { n: '978', c: 1 }, { n: '696', c: 3 }, { n: '678', c: 4 } ],
  [ { n: '978', c: 1 }, { n: '696', c: 2 }, { n: '678', c: 5 } ],
  [ { n: '978', c: 1 }, { n: '696', c: 1 }, { n: '678', c: 6 } ] ]

嗯......一旦掌握了这些数据,就可以减少你拥有的数据了。所以只有最后一行;

possibles = combos.reduce((p,c) => (c.length > 1 ? p.push(...getPossibles(c,sum))
                                                 : p.push(getPossibles(c,sum)),p),[]);
                  .filter(p => p.reduce((s,o) => s += o.n*o.c,0) === sum)
                  .map(e => e.reduce((p,c) => p.concat(Array(c.c).fill(c.n)),[]));

是我们如何在JS中以不到7毫秒的速度获得解决方案。