Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 最小集差_Java_Algorithm - Fatal编程技术网

Java 最小集差

Java 最小集差,java,algorithm,Java,Algorithm,我在这个叫做Codibility的网站上遇到了这个问题,但我真的不知道如何解决它,如果能得到帮助,我将不胜感激 给定一个由n个整数组成的数组A,以及由n个元素组成的序列S 1或-1,我们定义该值: 假设零元素之和等于零。 写一个函数 int min_abs_sum(int[] A); 然后,给定一个由n个整数组成的数组,从[-100..100]范围内计算val(A,S)的最低可能值(对于元素为1或-1的任何序列S)。您可以假设n因此,目标是尽可能接近0 我的第一个想法是,我将按降序对数组进行

我在这个叫做Codibility的网站上遇到了这个问题,但我真的不知道如何解决它,如果能得到帮助,我将不胜感激

给定一个由n个整数组成的数组A,以及由n个元素组成的序列S 1或-1,我们定义该值:

假设零元素之和等于零。 写一个函数

int min_abs_sum(int[] A);

然后,给定一个由n个整数组成的数组,从[-100..100]范围内计算val(A,S)的最低可能值(对于元素为1或-1的任何序列S)。您可以假设n因此,目标是尽可能接近0

我的第一个想法是,我将按降序对数组进行排序,然后在列表上迭代执行以下操作:

int total = 0;
foreach(int i in a)
{
    total = Math.Min(Math.Abs(total - i), Math.Abs(total + i));
}
这将适用于
a={1,5,2,-2}
(总计为以下
5,4,2,0

但我不确定它是否在所有情况下都有效。我会仔细研究一下,然后看看是否有一个案例不适用

编辑:

好吧,我想蛮力会起作用吧

    public static int MinArray(int[] array)
    {
        int val = int.MaxValue;

        for (int i = 0; i < Math.Pow(2, array.Length); i++)
        {
            val = Math.Min(CalcMin(array, i), val);
        }

        return val;
    }

    private static int CalcMin(int[] array, int negs)
    {
        int ret = 0;

        for (int i = 0; i < array.Length; i++)
        {
            int neg = 1;

            if (negs != 0)
            {
                neg = negs % 2 == 1 ? -1 : 1;
                negs >>= 1;
            }
            ret += array[i] * neg;
        }

        return Math.Abs(ret);
    }
公共静态int MinArray(int[]数组)
{
int val=int.MaxValue;
for(int i=0;i>=1;
}
ret+=数组[i]*neg;
}
返回Math.Abs(ret);
}
所以,我所做的是取S的每一次迭代(通过取
MinArray
中I的二进制来计算)并以这种方式找到min


只需稍加修改,您还可以获得S的正确值(如果这是一个要求。如果不是,将其作为一个要求可能会在面试中给您打分?

这是分区问题的一个措辞拙劣的版本。您要将阵列A拆分为尽可能接近相等的两组。在S数组中,您将分配+1的和较大的一个组,另一个组将得到-1。选择一个分区问题的解决方案,并调整它以返回对此问题的答案。实际上,它是分区的一种变体,它寻找最好的可能值,而不是2个相等的集合

编辑以下是一些基于@Jerry Coffin链接的论文的python代码

def min_abs_sum(A):
vals = []
for x in A:
    for v in vals:
        n = v+x
        if (abs(n)<=1000000) and (n not in vals): vals.append(n)
        n = v-x
        if (abs(n)<=1000000) and (n not in vals): vals.append(n)
    if (x not in vals): vals.append(x)
    if (-x not in vals): vals.append(-x)
return (min([abs(x) for x in vals]))
def min_abs_sum(A):
VAL=[]
对于A中的x:
对于VAL中的v:
n=v+x

如果(abs(n),这基本上就是把
a
分成两部分,两部分的绝对值之和尽可能接近相等

然后将这些元素乘以1或-1,使一个分区全部为负,另一个分区全部为正

从算法的角度来看,我相信划分步骤几乎肯定是NP完全的(“子集求和”和“划分问题”之类的短语)。从编程的角度来看,它非常简单——彻底地测试各种可能性,直到得到最好的一个。只要元素的数量很小(最多十几个)[编辑:因为它是O(2N,你可能会将其增加到30-40范围内的某个地方),所以速度会相当快

不过我相信它应该与O(N!)成比例,因此如果数组变得很大,所花费的时间很快就会变得不合理。因为你只分为两个集合,集合内的顺序无关紧要,所以它是O(2N)而不是O(N!)。这个增长速度不如O(N!),但速度仍然足够快,无法处理大型集合

然而,我应该补充一点,Codibility似乎专门研究那些最初看起来可能是NP完全的问题,但实际上并不是——如果您在描述中遗漏了任何细节,那么问题可能会简单得多

编辑:重读它,问题可能是我忽略了一个关键的细节:限制范围。我不确定你如何立即使用它,但我非常确定它对于产生一个有效的解决方案是至关重要的。我的直接猜测是,它基于类似于将基于比较的排序更改为计数(也称为桶)的东西算是吧,不过我还没有仔细考虑过


编辑2:做了一点观察(并被@Moron提示),有限的范围很重要,我对如何将其转化为解决方案的思考通常是正确的。@白痴很好心地指出了关于子集和问题的Wikipedia条目,但我没有发现它写得特别好。稍微看一眼,我发现一个解释更清晰/更容易理解。

maxvalue = 100

def solve(data):
    def mark_sum(s):
        # wrap sum around maxvalue
        if s >= maxvalue:
            s -= maxvalue * 2
        elif sum < -maxvalue:
            s += maxvalue * 2
        # mark sum
        if s >= 0:
            s_new_pos[s] = True
        else:
            s_new_neg[s + maxvalue] = True

    s_old_pos = [False] * maxvalue  # marks for sums [0..99]
    s_old_neg = [False] * maxvalue  # marks for sums [-100..-1]
    s_old_pos[0] = True  # seed array with zero sum for zero elements
    for n in data:
        s_new_pos = [False] * maxvalue
        s_new_neg = [False] * maxvalue
        for i in range(maxvalue):   # mark new possible sums
            if s_old_pos[i]:
                mark_sum(i + n)
                mark_sum(i - n)
            if s_old_neg[i]:
                mark_sum(i - 100 + n)
                mark_sum(i - 100 - n)
        s_old_pos = s_new_pos
        s_old_neg = s_new_neg
    for i in range(maxvalue):
        if s_old_pos[i]:
            return i
        if s_old_neg[-1 - i]:
            return abs(-1 - i)
    raise AssertionError('my bad')
maxvalue=100
def解算(数据):
定义标记和:
#围绕maxvalue的求和
如果s>=最大值:
s-=最大值*2
elif总和<-最大值:
s+=最大值*2
#马克和
如果s>=0:
s_new_pos[s]=真
其他:
s_new_neg[s+maxvalue]=真
s_old_pos=[False]*maxvalue#总和的标记[0..99]
s_old_neg=[False]*maxvalue#总和标记[-100..-1]
s_old_pos[0]=零元素零和的真种子数组
对于数据中的n:
s_new_pos=[False]*maxvalue
s_new_neg=[False]*maxvalue
对于范围内的i(最大值):#标记新的可能总和
如果s_old_pos[i]:
马克和(i+n)
马克和(i-n)
如果s_old_neg[i]:
马克和(i-100+n)
马克和(i-100-n)
s_旧位置=s_新位置
s_old_neg=s_new_neg
对于范围内的i(最大值):
如果s_old_pos[i]:
返回i
如果s_old_neg[-1-i]:
返回abs(-1-i)
提出断言错误('m
def min_abs_sum(A):
    A[:] = sorted([ abs(i) for i in A if i != 0 ], reverse=True)
    s = sum(A)
    h = s / 2
    r = find_balance_iter(h, A)
    return abs(2*(h-r) - s)

def find_balance_iter(v, A):
    r = v
    n = len(A)
    for i in xrange(n):
        if i and A[i] == A[i-1]:
            continue
        for j in xrange(n-i-1):
            vv = v - A[i]
            rr = vv
            AA = A[i+j+1:]
            while True:
                if vv == 0 or vv in AA:
                    return 0
                if vv < 0 or not AA:
                    rr = vv
                    break
                if vv < AA[-1]:
                    rr = min(vv-AA[-1], rr, key=compare)
                    break
                vv -= AA[0]
                AA[:] = AA[1:]
            r = min(r, rr, key=compare)
    return r

def compare(a):
    return (abs(a), a)
import java.util.Arrays;

class Solution {

    public int solution ( int[] A ) {

        int n=A.length,r=0,c=1,sum=0,mid=0;

        // Add all numbers, replace them with their absolute value, and sort them
        for(int i=0;i<n;i++) {
          A[i]=Math.abs(A[i]);
          sum+=A[i];
        }
        Arrays.sort(A); // This minimizes the speed of growth of r in the loop below and allows us to count duplicates while scanning the array
        mid=sum/2; // If the number is odd the result is rounded down (the best possible solution is 1 instead of 0).

        // Find the subset of numbers whose sum is closest to half the total sum    
        boolean[] bs=new boolean[mid+101]; // We only need to check sets that are equal or less than half the sum of the numbers (to avoid having to check the sum in the inner loop I sacrifice 100 booleans since 100 is the maximum value allowed)
        bs[0]=true; // The set with zero elements always adds to 0
        for(int i=0;i<n;i++){
          if( A[i]==0 ) continue;
          // Count duplicate entries
          if(i<n-1 && A[i]==A[i+1] ){
            c++;
            continue;
          } 
          // Scan the subset sum result array from right to left and add A[i] c times to existing subset sums
          for (int j = r; j >= 0; j--)
            if(bs[j] ){
              int m= Math.min(mid, j+A[i]*c );
              for(int k= j+A[i];k<=m && !bs[k];k+=A[i] ) bs[k]=true; // To avoid duplicate work when dealing with multiples of previous numbers the loop stops if we find an entry has already been set.
            }
          r=Math.min(mid, r+A[i]*c); // New rightmost subset sum can be no more than the mid point
          while(!bs[r]) r--; // Scan back to rightmost subset sum
          if(r==mid) break; // Found an optimal solution; no need to continue
          c=1;
    }
    return sum-2*r; // The rightmost subset sum that does not go over half the sum is the best solution, compute the difference of the complementary subsets (r and sum-r).
  }

}