如何编写Java程序将一组数字分成两组,使得各个数字之和的差异最小。
例如,我有一个包含整数的数组 - [5,4,8,2]。我可以把它分成两个阵列 - [8,2]和[5,4]。假设给定的一组数字,可以像上面的例子一样有一个独特的解决方案,如何编写一个Java程序来实现解决方案。即使我能够找出最小可能的差异,也没关系。 假设我的方法接收一个数组作为参数。该方法必须首先将接收的数组分成两个数组,然后添加其中包含的整数。此后,它必须返回它们之间的差异,以便差异可能最小。
P.S.-我在这里看了看,但找不到任何具体的解决方案。最可能的解决方案似乎在这里给出 - divide an array into two sets with minimal difference。但我无法从该线程中收集如何编写Java程序以获得问题的明确解决方案。
修改
在查看@Alexandru Severin的评论之后,我尝试了一个java程序。它适用于一组数字[1,3,5,9],但不适用于另一组[4,3,5,9,11]。以下是该计划。请建议更改: -
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class FindMinimumDifference {
public static void main(String[] args) {
int[] arr= new int[]{4,3,5,9, 11};
FindMinimumDifference obj= new FindMinimumDifference();
obj.returnMinDiff(arr);
}
private int returnMinDiff(int[] array){
int diff=-1;
Arrays.sort(array);
List<Integer> list1= new ArrayList<>();
List<Integer> list2= new ArrayList<>();
int sumOfList1=0;
int sumOfList2=0;
for(int a:array){
for(Integer i:list1){
sumOfList1+=i;
}
for(Integer i:list2){
sumOfList2+=i;
}
if(sumOfList1<=sumOfList2){
list1.add(a);
}else{
list2.add(a);
}
}
List<Integer> list3=new ArrayList<>(list1);
List<Integer> list4= new ArrayList<>(list2);
Map<Integer, List<Integer>> mapOfProbables= new HashMap<Integer, List<Integer>>();
int probableValueCount=0;
for(int i=0; i<list1.size();i++){
for(int j=0; j<list2.size();j++){
if(abs(list1.get(i)-list2.get(j))<
abs(getSumOfEntries(list1)-getSumOfEntries(list2))){
List<Integer> list= new ArrayList<>();
list.add(list1.get(i));
list.add(list2.get(j));
mapOfProbables.put(probableValueCount++, list);
}
}
}
int minimumDiff=abs(getSumOfEntries(list1)-getSumOfEntries(list2));
List resultList= new ArrayList<>();
for(List probableList:mapOfProbables.values()){
list3.remove(probableList.get(0));
list4.remove(probableList.get(1));
list3.add((Integer)probableList.get(1));
list4.add((Integer)probableList.get(0));
if(minimumDiff>abs(getSumOfEntries(list3)-getSumOfEntries(list4))){
// valid exchange
minimumDiff=abs(getSumOfEntries(list3)-getSumOfEntries(list4));
resultList=probableList;
}
}
System.out.println(minimumDiff);
if(resultList.size()>0){
list1.remove(resultList.get(0));
list2.remove(resultList.get(1));
list1.add((Integer)resultList.get(1));
list2.add((Integer)resultList.get(0));
}
System.out.println(list1+""+list2); // the two resulting set of
// numbers with modified data giving expected result
return minimumDiff;
}
private static int getSumOfEntries(List<Integer> list){
int sum=0;
for(Integer i:list){
sum+=i;
}
return sum;
}
private static int abs(int i){
if(i<=0)
i=-i;
return i;
}
}
答案 0 :(得分:3)
首先,对数组进行排序,然后将第一个成员放入组中,将第二个成员放入另一个成员中,这样就不会起作用了,这就是原因:
给定输入[1,2,3,100]
。
结果将是:[1,3]
和[2,100]
,显然是错误的。
正确答案应为:[1,2,3]
和[100]
你可以在google上找到很多关于这个问题的优化算法,但是因为我认为你是初学者,我会试着给你一个你可以实现的简单算法:
在循环结束时,你应该有两个相当平衡的数组。例如:
Array: [1,5,5,6,7,10,20]
i1: `[20] []`
i2: `[20] [10]`
i3: `[20] [10,7]`
i4: `[20] [20,7,6]`
i5: `[20,5] [10,7,6]`
i6: `[20,5] [10,7,6,5]`
i7: `[20,5,1] [10,7,6,5]`
总和为26
和28
。如您所见,我们可以进一步优化解决方案,如果我们交换5
和6
导致[20,6,1]
和[20,7,5,5]
总和相等。
对于此步骤,您可以:
(x,y)
在group1中的所有元素组x
,y
在group2中,|x-y| < |sum(group1) - sum(group2)|
x
交换y
,直至获得最小差异此算法将始终返回最佳解决方案,并且比贪婪方法好得多。然而,就复杂性,速度和记忆而言,它并不是最佳的。如果对于非常大的阵列需要它并且资源有限,则最佳算法可能根据速度/存储比率和接受的误差百分比而不同。
答案 1 :(得分:2)
这是分区问题https://en.wikipedia.org/wiki/Partition_problem
的变体如果您需要最佳解决方案,则必须测试每个可能的输出集合组合。这对于小型集合可能是可行的,但对于大型输入是不可行的。
一个很好的近似是我在下面提出的贪婪算法。
当集合中的数字为时,这种启发式在实践中很有效 大小与其基数相同或更小,但事实并非如此 保证产生最佳分区。
首先,您需要将输入放在可排序的集合中,例如List。
1)对输入集合进行排序。
2)创建2个结果集。
3)迭代排序的输入。如果索引甚至被置于result1中,则将该项放在result2中。
List<Integer> input = new ArrayList<Integer>();
Collections.sort(input);
Set<Integer> result1 = new HashSet<Integer>();
Set<Integer> result2 = new HashSet<Integer>();
for (int i = 0; i < input.size(); i++) {
if (i % 2 == 0) {// if i is even
result1.add(input.get(i));
} else {
result2.add(input.get(i));
}
}
答案 2 :(得分:1)
我似乎已经为此获得了完美的解决方案。 Java程序下面的工作完美。唯一的假设是,给定的问题有唯一的解决方案(只有一个解决方案)。该假设暗示 - 仅非零数。我把这个程序放在下面。我请求每个人告诉程序是否可能在某些情况下失败,或者是否可以某种方式改进/优化它。 对Alexandru Severin先生的算法的认可是本主题中的答案之一。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class FindMinimumDifference {
static List<Integer> list1= new ArrayList<>();
static List<Integer> list2= new ArrayList<>();
public static void main(String[] args) {
int[] arr= new int[]{3,-2,9,7};
// tested for these sample data:- [1,5,9,3] ; [4,3,5,9,11] ;
//[7,5,11,2,13,15,14] ; [3,2,1,7,9,11,13] ;
//[3,1,0,5,6,9] ; [6,8,10,2,4,0] ; [3,1,5,7,0] ; [4,-1,5,-3,7] ; [3,-2,9,7]
System.out.println("the minimum possible difference is: "+returnMinDiff(arr));
System.out.println("the two resulting set of nos. are: "+list1+" and "+list2);
}
private static int returnMinDiff(int[] array){
int diff=-1;
Arrays.sort(array);
for(int a:array){
int sumOfList1=0;
int sumOfList2=0;
for(Integer i:list1){
sumOfList1+=i;
}
for(Integer i:list2){
sumOfList2+=i;
}
if(sumOfList1<=sumOfList2){
list1.add(a);
}else{
list2.add(a);
}
}
List<Integer> list3=new ArrayList<>(list1);
List<Integer> list4= new ArrayList<>(list2);
if(list3.size()!=list4.size()){ // both list should contain equal no. of entries.
//If not, add 0 to the list having lesser no. of entries
if(list3.size()<list4.size()){
list3.add(0);
}else{
list4.add(0);
}
}
Map<Integer, List<Integer>> mapOfProbables= new HashMap<Integer, List<Integer>>();
int probableValueCount=0;
for(int i=0; i<list3.size();i++){
for(int j=0; j<list4.size();j++){
if(abs(list3.get(i)-list4.get(j))
<abs(getSumOfEntries(list3)-getSumOfEntries(list4))){
List<Integer> list= new ArrayList<>();
list.add(list3.get(i));
list.add(list4.get(j));
mapOfProbables.put(probableValueCount++, list);
}
}
}
int minimumDiff=abs(getSumOfEntries(list1)-getSumOfEntries(list2));
List resultList= new ArrayList<>();
for(List probableList:mapOfProbables.values()){
list3=new ArrayList<>(list1);
list4= new ArrayList<>(list2);
list3.remove(probableList.get(0));
list4.remove(probableList.get(1));
list3.add((Integer)probableList.get(1));
list4.add((Integer)probableList.get(0));
if(minimumDiff>abs(getSumOfEntries(list3)-getSumOfEntries(list4))){ // valid exchange
minimumDiff=abs(getSumOfEntries(list3)-getSumOfEntries(list4));
resultList=probableList;
}
}
if(resultList.size()>0){ // forming the two set of nos. whose difference of sum comes out to be minimum
list1.remove(resultList.get(0));
list2.remove(resultList.get(1));
if(!resultList.get(1).equals(0) ) // (resultList.get(1).equals(0) && !list1.contains(0))
list1.add((Integer)resultList.get(1));
if(!resultList.get(0).equals(0) || (resultList.get(0).equals(0) && list2.contains(0)))
list2.add((Integer)resultList.get(0));
}
return minimumDiff; // returning the minimum possible difference
}
private static int getSumOfEntries(List<Integer> list){
int sum=0;
for(Integer i:list){
sum+=i;
}
return sum;
}
private static int abs(int i){
if(i<=0)
i=-i;
return i;
}
}
答案 3 :(得分:0)
似乎您对算法比对代码更感兴趣。所以,这是我的伪代码: -
int A[];//This contains your elements in sorted (descending) order
int a1[],a2[];//The two sub-arrays
int sum1=0,sum2=0;//These store the sum of the elements of the 2 subarrays respectively
for(i=0;i<A.length;i++)
{
//Calculate the absolute difference of the sums for each element and then add accordingly and thereafter update the sum
if(abs(sum1+A[i]-sum2)<=abs(sum2+A[i]-sum1))
{a1.add(A[i]);
sum1+=A[i];}
else
{a2.add(A[i]);
sum2+=A[i];}
}
这适用于所有整数,无论是积极的还是消极的。
答案 4 :(得分:0)
对于这个问题,假设我们可以将数组分成两个子数组,使它们的总和相等。 (即使认为它们不相同,它也会起作用)
因此,如果数组中的元素总和为S.您的目标是找到一个总和为S / 2的子集。你可以为此编写一个递归函数。
O(N^2)
您也可以为此编写等效的printf
动态编程代码。
我只是在展示这个想法。
所以当你发现这个集合的总和为S / 2时,你自动将数组分成两部分,总和相同(这里是S / 2)。