【问题标题】:Sum of numbers with approximation and no repetition具有近似值且不重复的数字总和
【发布时间】:2016-02-19 09:49:54
【问题描述】:

对于我正在开发的应用程序,我需要处理一个数字数组并返回一个新数组,以使元素的总和尽可能接近目标总和。这与coin-counting problem 类似,但有两点不同:

  1. 新数组的每个元素都必须来自输入数组(即没有重复/重复)
  2. 当发现一个数组的总和在目标数的 X 范围内时,算法应该停止(例如,给定 [10, 12, 15, 23, 26],目标为 35,sigma 为 5,结果[10, 12, 15] 的结果(总和 37)是可以的,但 [15, 26] 的结果(总和 41)不是。

我正在考虑以下算法(在伪代码中),但我怀疑这是最好的方法。

function (array, goal, sigma)
    var A = []
    for each element E in array
        if (E + (sum of rest of A) < goal +/- sigma)
            A.push(E)
    return A

不管怎样,我使用的语言是 Javascript。非常感谢任何建议!

【问题讨论】:

  • 您是否有任何关于输入数组的先验信息(数字范围、长度等)?
  • @shapiroyaacov 这些数字通常在 100 到 600 之间(它们是歌曲长度,以秒为单位),输入数组的长度可以是大约 5 到 50 之间的任何地方(尽管这个数字不是不一定有界)。
  • 还有进球数?对此有何意见?
  • @shapiroyaacov 目标数可以是 100 到 7200 之间的任何值。
  • 这听起来像是en.wikipedia.org/wiki/Subset_sum_problem 的变体,因为输入是有限的,我认为蛮力,也许有一些调整应该能够做到这一点

标签: algorithm language-agnostic heuristics


【解决方案1】:

这并不是最好的答案,只是可能会足够好。欢迎所有评论/输入。
此外,这是考虑到 cmets 的答案,即输入是歌曲的长度(通常为 100 - 600),输入数组的长度在 5 到 50 之间,目标在 100 到 7200 之间。

想法:

  1. 从找到输入的平均值开始,然后猜测您需要的输入值的数量。可以说出来x
  2. 订购您的输入。
  3. 取第一个x-1 值并将最小的值替换为任何其他值以达到您的目标(范围内的某个位置)。如果不存在,请找到一个数字,以便您仍然低于目标。
  4. 使用回溯或类似的方法重复第 3 步。也许限制你在那里度过的试验次数。
  5. x++ 并返回到第 3 步。

【讨论】:

    【解决方案2】:

    我会使用某种分而治之的方法和递归实现。这是 Smalltalk 中的原型

    SequenceableCollection>>subsetOfSum: s plusOrMinus: d
        "check if a singleton matches"
        self do: [:v | (v between: s - d and: s + d) ifTrue: [^{v}]].
    
        "nope, engage recursion with a smaller collection"
        self keysAndValuesDo: [:i :v |
            | sub |
            sub := (self copyWithoutIndex: i) subsetOfSum: s-v plusOrMinus: d.
            sub isNil ifFalse: [^sub copyWith: v]].
    
        "none found"
        ^nil
    

    这样使用:

    #(10 12 15 23 26) subsetOfSum: 62 plusOrMinus: 3.
    

    给予:

    #(23 15 12 10)
    

    【讨论】:

      【解决方案3】:

      这是 JavaScript 中的一种注释算法:

      var arr = [9, 12, 20, 23, 26];
      var target = 35;
      var sigma = 5;
      var n = arr.length;
      
      // sort the numbers in ascending order
      arr.sort(function(a,b){return a-b;});
      
      // initialize the recursion
      var stack = [[0,0,[]]];
      
      while (stack[0] !== undefined){
        var params = stack.pop();
        var i = params[0]; // index
        var s = params[1]; // sum so far
        var r = params[2]; // accumulating list of numbers
      
        // if the sum is within range, output sum
        if (s >= target - sigma && s <= target + sigma){
          console.log(r);
          break;
      
        // since the numbers are sorted, if the current
        // number makes the sum too large, abandon this thread
        } else if (s + arr[i] > target + sigma){
          continue;
        }
      
        // there are still enough numbers left to skip this one
        if (i < n - 1){ 
          stack.push([i + 1,s,r]);
        }
      
        // there are still enough numbers left to add this one
        if (i < n){
          _r = r.slice();
          _r.push(arr[i]);
          stack.push([i + 1,s + arr[i],_r]);
        }
      }
      
      /* [9,23] */
      

      【讨论】:

        【解决方案4】:

        在输入有限的情况下,这个问题非常适合具有时间复杂度的动态规划O((Sum + Sigma) * ArrayLength)

        德尔福代码:

          function FindCombination(const A: array of Integer; Sum, Sigma: Integer): string;
          var
            Sums: array of Integer;
            Value, idx: Integer;
          begin
            Result := '';
            SetLength(Sums, Sum + Sigma + 1); //zero-initialized array
            Sums[0] := 1; //just non-zero
        
            for Value in A do begin
              idx := Sum + Sigma;
              while idx >= Value do begin
                if Sums[idx - Value] <> 0 then begin //(idx-Value) sum can be formed from array]
        
                  Sums[idx] := Value;    //value is included in this sum
        
                  if idx >= Sum - Sigma then begin  //bingo!
                    while idx > 0 do begin  //unwind and extract all values for this sum
                      Result := Result + IntToStr(Sums[idx]) + ' ';
                      idx := idx - Sums[idx];
                    end;
                    Exit;
                  end;
        
                end;
                Dec(idx); //idx--
              end;
            end;
          end;
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-04-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-12-06
          • 2021-09-25
          • 2021-12-27
          相关资源
          最近更新 更多