Java:数组的组合,每个数组x

Java:数组的组合,每个数组x,java,combinations,Java,Combinations,我在组中有一个选项池,我正在尝试动态生成用于测试目的的组合。我想定义bucket,让代码生成所有组合,并通过@DataProvider提供给我的TestNG测试。现在我有一些案例是硬编码的,但很明显,这并不是维护代码的最佳方式 我正在努力处理这样一种情况:当y>2时,y“桶”中有x个“球” 在一般情况下,假设您有以下示例: public static void main(String [] args){ Object[][] combinations = getCombinations(

我在组中有一个选项池,我正在尝试动态生成用于测试目的的组合。我想定义bucket,让代码生成所有组合,并通过@DataProvider提供给我的TestNG测试。现在我有一些案例是硬编码的,但很明显,这并不是维护代码的最佳方式

我正在努力处理这样一种情况:当y>2时,y“桶”中有x个“球”

在一般情况下,假设您有以下示例:

public static void main(String [] args){
   Object[][] combinations = getCombinations(
        new String[]
        {
          "1", "2"
        },
        new String[]
        {
          "3", "4"
        }/*,
        new String[]
        {
          "5", "6"
        }*/);
   for (Object[] combination : combinations)
   {
     System.out.println(Arrays.toString(combination));
   }
}

private Object[][] getCombinations(Object[]... arrays)
{
   if (arrays.length == 0)
   {
     return new Object[0][0];
   }

   List<Object[]> solutions = new ArrayList<>();
   Object[] array1 = arrays[0];
   for (Object o : array1)
   {
     for (int i = 1; i < arrays.length; i++)
     {
       for (Object o2 : arrays[i])
       {
         int count = 0;
         Object[] path = new Object[arrays.length];
         path[count++] = o;
         path[count++] = o2;
         solutions.add(path);
       }
     }
   }
return solutions.toArray(new Object[0][0]);
}
添加第三个“桶”会将一切抛到窗外

解决办法如下:

[1,3,5]
[1,3,6]
[1,4,5]
[1,4,6]
[2,3,5]
[2,3,6]
[2,4,5]
[2,4,6]
有没有办法解决这个问题?理想情况下,您可以传递每个铲斗的拾取量

虽然解决方案代码会受到欢迎,但我更感兴趣的是它背后的推理

更新 对于未来的访客,这里是凯文·安德森(Kevin Anderson)的一个通用答案:

单元测试:

import static org.testng.Assert.assertEquals;

import java.util.Arrays;
import java.util.List;

import org.testng.annotations.Test;

public class CombinationNGTest
{
  @Test
  public void testCombinaitonOnePick()
  {
    List<List<Integer>> result
            = Combination.pickKfromEach((List<List<Integer>>) Arrays.asList(
                    Arrays.asList(1, 2),
                    Arrays.asList(3, 4)),
                    1);

    assertEquals(result.size(), 4, result.toString());

    result = Combination.pickKfromEach((List<List<Integer>>) Arrays.asList(
            Arrays.asList(1, 2),
            Arrays.asList(3, 4),
            Arrays.asList(5, 6)),
            1);

    assertEquals(result.size(), 8, result.toString());

    result = Combination.pickKfromEach((List<List<Integer>>) Arrays.asList(
            Arrays.asList(1, 2),
            Arrays.asList(3, 4),
            Arrays.asList(5, 6),
            Arrays.asList(7, 8)),
            1);

    assertEquals(result.size(), 16, result.toString());

    List<List<String>> result2= Combination.pickKfromEach((List<List<String>>) Arrays.asList(
                    Arrays.asList("A", "B"),
                    Arrays.asList("C", "D")),
                    1);

    assertEquals(result2.size(), 4, result.toString());
  }

  @Test
  public void testCombinaitonMultiplePicks()
  {
    List<List<Integer>> result
            = Combination.pickKfromEach((List<List<Integer>>) Arrays.asList(
                    Arrays.asList(1, 2, 3),
                    Arrays.asList(4, 5, 6)),
                    2);

    assertEquals(result.size(), 9, result.toString());
  }
}
导入静态org.testng.Assert.assertEquals;
导入java.util.array;
导入java.util.List;
导入org.testng.annotations.Test;
公共类组合测试
{
@试验
public void testcombinationonepick()
{
列表结果
=组合.pickFromeach((列表)Arrays.asList(
数组。asList(1,2),
数组.asList(3,4)),
1);
assertEquals(result.size(),4,result.toString());
结果=组合.pickFromeach((列表)Arrays.asList(
数组。asList(1,2),
数组。asList(3,4),
数组。asList(5,6)),
1);
assertEquals(result.size(),8,result.toString());
结果=组合.pickFromeach((列表)Arrays.asList(
数组。asList(1,2),
数组。asList(3,4),
数组。asList(5,6),
数组。asList(7,8)),
1);
assertEquals(result.size(),16,result.toString());
List result2=composition.pickfromeach((List)Arrays.asList(
数组。asList(“A”、“B”),
数组.asList(“C”、“D”),
1);
assertEquals(result2.size(),4,result.toString());
}
@试验
public void testcombinationmultiplePicks()
{
列表结果
=组合.pickFromeach((列表)Arrays.asList(
数组。asList(1,2,3),
数组.asList(4,5,6)),
2);
assertEquals(result.size(),9,result.toString());
}
}

您正在努力解决的问题不容易迭代解决,因为复杂性随给定数组的数量而变化。 此问题的解决方案是使用递归函数,该函数生成第一个参数和所有以下数组的排列

不幸的是,我现在无法编写任何完全工作的代码,但我可以尝试给您一个示例:

public static Object[] permuteAll(Object[] objs1, Object[][] objs2) {
    if(objs2.length == 1){
        return permuteAll(objs1, objs2);
    }else{
        return permuteAll(objs2[0], objs2[/*The rest of the objs[][]*/]]);
    }
}

public static Object[] permuteAll(Object[] objs1, Object[] objs2) {
    return ... //Your Code for 2 buckets goes here
}

我还建议使用泛型而不是对象类,但根据您组合对象的方式,您可能无法从中获得任何实际好处…

您遇到了一个过于复杂的解决方案,尽管如此,它恰好适用于两个存储桶的情况。但是,正如您所发现的,它不会自然扩展到三个或更多桶

下面是两个桶的简单解决方案,已泛化并使用
List
s代替数组:

// Find all 2-item combinations consisting of 1 item picked from 
// each of 2 buckets
static <T> List<List<T>> pick1From2(List<List<T>> in)
{
    List<List<T>> result = new ArrayList<>();
    for (int i = 0; i < in.get(0).size(); ++i) {
        for (int j = 0; j < in.get(1).size(); ++j) {
            result.add(Arrays.asList(in.get(0).get(i), in.get(1).get(j)));
        }
    }
    return result;
}
所有索引都从零开始,在达到相应桶的大小时,每个索引都会最大化。要跳转到下一个组合(内部
循环),最后一个索引将递增;如果它已最大化,则将重置为零,并递增下一个更高的索引。如果下一个更高的索引也达到最大值,它将重置并导致下一个索引增加,依此类推
idx[0]
在递增后从不重置,因此外部
while
可以检测
idx[0]
何时已最大化

从每个桶中拾取
k
项目基本上是相同的过程,除了用桶的k组合集替换原始桶外:

// Find all `N * k`-item combinations formed by picking `k` items
// from each of `N` buckets
static <T> List<List<T>> pickKfromEach(List<List<T>> sets, int k)
{
    List<List<List<T>>> kCombos = new ArrayList<>(sets.size());
    for (List<T> ms : sets) {
        kCombos.add(combinations(ms, k));
    }
    ArrayList<List<T>> result = new ArrayList<>();
    int[] indices = new int[kCombos.size()];
    while (indices[0] < kCombos.get(0).size()) {
        List<T> pick = new ArrayList<>(kCombos.size());
        for (int i = 0; i < indices.length; ++i) {
            pick.addAll(kCombos.get(i).get(indices[i]));
        }
        result.add(pick);
        int i = indices.length - 1;
        while (++indices[i] >= kCombos.get(i).size() && i > 0) {
            indices[i] = 0;
            --i;
        }
    }
    return result;
}

static <T> List<List<T>> combinations(List<T> s, int k) throws IllegalArgumentException
{
    if (k < 0 || k > s.size()) {
        throw new IllegalArgumentException("Can't pick " + k
            + " from set of size " + s.size());
    }
    List<List<T>> res = new LinkedList<>();
    if (k > 0) {
        int idx[] = new int[k];
        for (int ix = 0; ix < idx.length; ++ix) {
            idx[ix] = ix;
        }
        while (idx[0] <= s.size() - k) {
            List<T> combo = new ArrayList<>(k);
            for (int ix = 0; ix < idx.length; ++ix) {
                combo.add(s.get(idx[ix]));
            }
            res.add(combo);
            int ix = idx.length - 1;
            while (ix > 0 && (idx[ix] == s.size() - k + ix))
               --ix;
            ++idx[ix];
            while (++ix < idx.length)
                idx[ix] = idx[ix-1]+1;
        }
    }
    return res;
}
//查找通过拾取`k`项而形成的所有`N*k`-项组合
//从每个'N'桶
静态列表pickFromeach(列表集,int k)
{
List kCombos=newarraylist(sets.size());
用于(列表ms:套){
添加(组合(ms,k));
}
ArrayList结果=新建ArrayList();
int[]索引=新的int[kCombos.size()];
而(索引[0]=kCombos.get(i.size()&&i>0){
指数[i]=0;
--一,;
}
}
返回结果;
}
静态列表组合(列表s,int k)引发IllegalArgumentException
{
如果(k<0 | | k>s.size()){
抛出新的IllegalArgumentException(“无法拾取”+k
+“从大小集”+s.size());
}
List res=new LinkedList();
如果(k>0){
int idx[]=新的int[k];
对于(int ix=0;ix
与pick例程一样,
组合
方法使用一个索引数组来枚举组合。但指数的管理方式有点不同。索引从{0,1,2,…,k-1_}开始,当它们达到值{n-k,n-k+1,…,n}时,它们达到最大值。要跳转到下一个组合,将增加尚未达到最大值的最后一个索引,然后将下面的每个索引重置为
// Find all 3-item combinations consisting of 1 item picked from
// each of 3 buckets 
static <T> List<List<T>> pick1From3(List<List<T>> in)
{
    List<List<T>> result = new ArrayList<>();
    for (int i = 0; i < in.get(0).size(); ++i) {
        for (int j = 0; j < in.get(1).size(); ++j) {
            for (int k = 0; k < in.get(2).size(); ++k)
                result.add(Arrays.asList(in.get(0).get(i), in.get(1).get(j), in.get(2).get(k)));
        }
    }
    return result;
}
// Find all `N`-item combinations consisting 1 item picked from 
// each of an `N` buckets
static <T> List<List<T>> pick1fromN(List<List<T>> s)
{
    List<List<T>> result = new ArrayList<>();
    int[] idx = new int[s.size()];
    while (idx[0] < s.get(0).size()) {
        List<T> pick = new ArrayList(s.size());
        for (int i = 0; i < idx.length; ++i) {
            pick.add(s.get(i).get(idx[i]));
        }
        result.add(pick);
        int i = idx.length - 1;
        while (++idx[i] >= s.get(i).size() && i > 0) {
            idx[i] = 0;
            --i;
        }
    }
    return result;
}
// Find all `N * k`-item combinations formed by picking `k` items
// from each of `N` buckets
static <T> List<List<T>> pickKfromEach(List<List<T>> sets, int k)
{
    List<List<List<T>>> kCombos = new ArrayList<>(sets.size());
    for (List<T> ms : sets) {
        kCombos.add(combinations(ms, k));
    }
    ArrayList<List<T>> result = new ArrayList<>();
    int[] indices = new int[kCombos.size()];
    while (indices[0] < kCombos.get(0).size()) {
        List<T> pick = new ArrayList<>(kCombos.size());
        for (int i = 0; i < indices.length; ++i) {
            pick.addAll(kCombos.get(i).get(indices[i]));
        }
        result.add(pick);
        int i = indices.length - 1;
        while (++indices[i] >= kCombos.get(i).size() && i > 0) {
            indices[i] = 0;
            --i;
        }
    }
    return result;
}

static <T> List<List<T>> combinations(List<T> s, int k) throws IllegalArgumentException
{
    if (k < 0 || k > s.size()) {
        throw new IllegalArgumentException("Can't pick " + k
            + " from set of size " + s.size());
    }
    List<List<T>> res = new LinkedList<>();
    if (k > 0) {
        int idx[] = new int[k];
        for (int ix = 0; ix < idx.length; ++ix) {
            idx[ix] = ix;
        }
        while (idx[0] <= s.size() - k) {
            List<T> combo = new ArrayList<>(k);
            for (int ix = 0; ix < idx.length; ++ix) {
                combo.add(s.get(idx[ix]));
            }
            res.add(combo);
            int ix = idx.length - 1;
            while (ix > 0 && (idx[ix] == s.size() - k + ix))
               --ix;
            ++idx[ix];
            while (++ix < idx.length)
                idx[ix] = idx[ix-1]+1;
        }
    }
    return res;
}