在直方图上分配值的快速算法?

时间:2018-03-01 17:40:16

标签: algorithm performance time-complexity

我正在寻找一个快速(在复杂性方面(问题的大小可能接近2 ^ 32)和在常数方面),这不一定要计算最优解(因此,如果一种启发式方法产生的结果“接近”最优并且在计算特定问题的计算时间方面具有“相当大的”优势,那么启发式是可接受的。

我有一个整数直方图A:|A| = n, A[i]>0;和值R:0<R<=A[0]+...+A[n-1]。我必须尽可能均匀地在直方图上分配-R。形式上这意味着这样的事情(在正式表示法中也有一些额外的信息):我需要找到B,这样|B| = |A| && B[i] = A[i] - C[i],其中0<=C[i]<=A[i] && C[0]+...+C[n-1] = R和C必须最小化表达式:{{1} }和L_2 = C[0]^2 + ... + C[n-1]^2。只需从公式中可以看出问题不一定具有唯一解(考虑A [0] = 1,A [1] = 1且R = 1,则B [0] = 0,B [1] ] = 1且B'[0] = 1,B'[1] = 0是最优解),可以添加一个附加约束,例如L_infinity = max(C[0], ..., C[n-1]),但在我的情况下它并不重要。天真的人可以迭代C [i]的所有可能性(R-重复组合)并找到最优解,但显然对于较大的n并不是非常快。

另一种可能的解决方案是找到if A[i]<A[j] then C[i]<C[j]q = R/n,然后迭代所有元素并存储r=R%ndiff[i] = A[i]-q,然后继续所有未删除的A [i] ,将它们设置为if diff[i]<=0 then r-=diff[i] && B[i] = 0 && remove A[i]A[i] = diff[i]R = r。如果迭代这个过程,那么在每个步骤我们将删除至少一个元素,直到我们到达n=n-removedElementsCount或我们只有1个元素的点,然后我们只需要{R}这样q == 0来自A的元素,因为在A[i]-=1情况下是R<n,或者只有q==0,如果我们只有1个元素剩余(我们有0个元素的情况是不重要的)。由于我们每步删除至少一个元素,并且我们需要在最坏的情况下迭代A[i]-=R个元素,然后我们的复杂度为O((1 + ... + n))= O(n ^ 2)。

我希望有人已经熟悉一个更好的算法,或者如果你有任何想法我会很高兴听到它们(我知道这也可以被认为是一个优化问题)。

编辑:使R为正,因此更容易阅读。

编辑2:我意识到我搞砸了优化标准。

2 个答案:

答案 0 :(得分:1)

将直方图转换为(value, index)对数组,然后将其转换为min heap。此操作为O(n)

现在,您的C会将一些值设置为0,将最大值减少一些,其余值减去最大值1。您喜欢减少所有内容的最大金额很容易计算,它被R/n四舍五入。

现在通过堆。只要堆底部的值为< ceil(R/size of heap),该索引处的该值将设置为零,并在时间O(log(n))中从堆中删除该值。一旦该循环结束,您可以将最大值和小于最大值的1分配给其余部分。

这将在O(n log(n))最糟糕的时间运行。当必须将O(n)元素清零时,您将遇到最糟糕的情况。

答案 1 :(得分:0)

我在O(n * log(n))时间内想出了一个非常简单的贪婪算法(如果有人设法在O(n)中解决它,尽管我很乐意听到)。

算法:

  1. 给定:整数数组:A[0],...,A[|A|-1]: A[i]>=0;整数:R0: 0<=R0<=A[0]+...+A[|A|-1]

  2. 基地:

  3. 按升序排序 - 取O(n * log(n)时间。

    设置i = 0; R = R0; n = |A|; q = floor(R/n); r = R - q*n; d = q;

    1. if(i==|A| or R==0) goto 6.;

    2. if(i>=|A|-r) d = q + 1;

    3. 4

      if(A[i]>=d) 
      { 
          R-=d; 
          A[i]-=d; 
      }
      else 
      { 
          R-=A[i]; 
          A[i] = 0; 
          n = |A|-(i+1); 
          q = floor(R/n);
          d = q;
          r = R - q*n; 
      }
      
      1. i=i+1; goto 2.;

      2. if(R>0) A[|A|-1] -= R; return A;

      3. 非正式解决方案最优性证明:

        n = |A|

        案例0:n==1 -> C[0] = R

        案例1:n>1 && A[i]>=q && A[j]>=q+1 for j>=max(0,n-r)

        最佳解决方案由C[i] = q for i<n-r && C[j] = q+1 for i>=n-r给出。 假设C'[i] = C[i] + E[i]给出了另一个最优解,其中E的约束为:E[0]+...+E[m-1]=0(否则C'会违反C'[0] + ... + C'[n-1] = R),{{1} (否则C[i]>=-E[i]会违反非负面约束),C'[i](来自E[i] <= A[i] - C[i])和C'[i]<=A[i](来自E[i]<=E[j] for i<=j),然后: C[i]<=C[j] for A[i]<=A[j] && A[i]<=A[j] for i<=j 最后一个不等式是正确的,因为对于每个术语L_2' - L_2 = 2*q*(E[0]+...+E[n-r-1]) + 2*(q+1)*(E[n-r]+...+E[n-1]) + (E[0]^2 + ... + E[n-1]^2) = 2*q*0 + (E[0]^2 + ... + E[n-1]^2) + 2*(E[n-r] + ... + E[n-1]) >= 0,如果它至少对2*E[n-i], 1<=i<=r是负的,则会有相应的术语E[n-i]^2, 1<=i<=r取消它。让我们分析E[n-i]<-1,显然2*E[n-i] = -2不足以在这种情况下取​​消它的情况。但是,由于E[n-i]^2 = 1的所有元素总和为E,因此存在0:这样j!=n-i会对其进行补偿,因为我们有E[j]这个词。对于每个可能的解E[j]^2,在L_2&lt; = L_2'之后的最后一个不等式中,这意味着C'最小化C。看到L_2最小化也得到满足是微不足道的:L_inf,如果我们有L_inf = q + (r>0) <= L_inf' = max(q+E[0], ... , q+E[n-r-1], q+1+E[n-r], ... , q+1+E[n-1]),我们得到更高的最大值,我们也可以永远不会减少最大值,因为E[i]>1 for i<n-r, or E[j]>0 for j>=n-r总和为E

        案例2:0

        在这种情况下,最佳解决方案要求所有n>1 && there exists k: A[k]<qC[k] = A[k]。我们假设存在一个最优解k: A[k]<qC'。存在C'[k]<A[k]<q -> C'[k]<q-1i>=k。假设没有这样的C'[i]<q-1 && C'[i+1]>=q-1,然后是iC'[k] == C[n-1] < q-1,这是一个矛盾,这意味着这样的C'[0]+...+C'[n-1]<n*q-n<R确实存在。还存在i j>k(如果我们认为这是不真实的,我们再次与C[j]>q && C[j-1]<C[j]求和C相矛盾。我们需要这些证据才能满足R。让我们考虑修改后的解决方案C[t]<=C[l] for t<=lC''[t] = C'[t] for t!=i,j; and C''[i] = C'[i]+1, and C''[j] = C'[j]-1。最后一个不等式来自L_2' - L_2'' = C'[i]^2 - (C'[i]+1)^2 + C'[j]^2 - (C'[j]-1)^2 = -2*C'[i] + 2*C'[j] - 2 = 2*((C'[j]-C'[i])-1) > 2*(1-1) = 0。如果我们增加(C'[i]<q-1 && C'[j]>q) -> C'[j] - C'[i] > 1,我们证明L_2'>L_2''。通过归纳,最优解应该具有C[i]: C[i]<A[i]<q。完成此操作后,可以使用简化问题C[l]=A[l] for all l: A[l]<q归纳地继续进行。

        案例3:n' = n-(i+1), R' = R - (C[0]+...+C[i]), q' = floor(R'/n'), r' = R' - q'*n', D[0] = A[i+1], ..., D[n'-1] = A[n-1]

        n>1 && A[i]>=q && A[j]<q+1 for j==max(0,n-r)以来,这意味着A[k]>=A[i] for k>=i。但由于我们还A[i]<q+1 for i<=j这意味着q<=A[i],因此我们无法在任何A[i]==q中添加任何余数。 C[i] : i<=j的最优性来自案例1中的证明(证明有C[i]=A[i]=q for i<j项更普遍的证明)。由于问题对于q+1来说是最佳的,我们可以开始解决减少的问题:0<=i<j

        案例0,1,2,3都是可能的情况。除了明确给出解决方案的案例0和案例1之外,2和3中的解决方案将问题减少到较小的问题,其再次落入其中一个案例中。由于每个步骤都会减少问题,因此我们可以通过有限的步骤获得最终解决方案。我们也不会多次引用一个元素,这意味着D[0] = A[j],...,D[n-j] = A[n-1]时间,但我们需要O(n)进行排序,因此最终我们的算法时间复杂度为O(n*log(n))。我不确定这个问题是否可以在O(n*log(n))时间内解决,但我觉得没有排序就无法逃脱,因为案例2和3严重依赖它,所以也许O(n)是可以实现的最佳复杂性。