如何从单个列表中选择唯一对

时间:2013-11-09 11:55:11

标签: java list sorting

我正在创建一个程序,其中涉及一段代码,我需要从单个项目列表(名称)中选择对。

初始列表来自单个ArrayList<String>,其中包含所有人的唯一名称。

我的尝试如下:

//Performance is not really a focus as the lists are small (20 ~ 60 elements),
//thus I use a SecureRandom instead of a Random.
SecureRandom rnd = new SecureRandom();

//List of names
ArrayList<String> Names = new ArrayList<>();

//Names populated somewhere here..

//Make a secondary array which houses the available names...
ArrayList<String> AvailNames = new ArrayList<>();
AvailNames.addAll(Names);

LinkedHashMap<String, String> NamePair = new LinkedHashMap<>();
Iterator<String> Iter = Names.iterator();

// LOOP A
while(Iter.hasNext()){
    String name = Iter.next();
    int index;

   /*
    * LOOP B
    * Find a unique pair randomly, looping if the index is the same.
    * Not the most efficient way, but gets the job done...
    */
    while(true){
        index = rnd.nextInt(AvailNames.size());
        if(!AvailNames.get(index).equals(name)){
            break;
        }
    }

    NamePair.put(name, AvailNames.remove(index));
}

当名字的数量是奇数时,我遇到了LOOP B见上面)无限期运行的问题。

我发现问题在于,有时候,当所有对都被占用时,剩下的姓氏对是非唯一的,导致if语句永远不会成立。

以列表为例:

  
      

  1.   

  2.   
  3. ç
  4.   
  5. d
  6.   
  7. 电子
  8.   

程序在执行期间可以先将A到D排序,然后再创建一个名称对,如下所示:

  
      
  1. A - B
  2.   
  3. B - C
  4.   
  5. C - D
  6.   
  7. D - C
  8.   

哪个会留下E - E作为最后一对,但不允许成对(因为项目/名称不是唯一)。由于配对分配是随机的,有时它会起作用,有时它并不是坦白地说很烦人......

我确信解决方案非常简单,但出于某种原因,我似乎无法找到解决此问题的方法。任何帮助表示赞赏。

3 个答案:

答案 0 :(得分:2)

您可以检测到何时遇到这种情况,只需将您的上一个AvailName与随机选择的某个前一对的第二个元素进行交换。例如,如果您选择了第二对,则将其更改为B-E,最后一对将成为E-C。

这将总是给出两对,每对具有不同的第一和第二元素:所选择的对不能将E作为其第一个元素(您将要生成唯一的一对)或作为其第二个元素(否则为E)不会在AvailName)。

答案 1 :(得分:1)

只是一个想法;您可以计算所有可能的唯一对,然后从中随机抽样。

需要注意的是,这是O(n 2 ),因此对于大量名称来说可能会变慢;并且新的List会变得相当大,因为它包含n(n 2 -1)/ 2个元素。

以下是一个例子:

public static void main(String args[]) {
    final List<String> in = new ArrayList<String>() {
        {
            add("A");
            add("B");
            add("C");
            add("D");
            add("E");
        }
    };
    final List<String[]> pairs = new ArrayList<>();
    for (int i = 0; i < in.size(); ++i) {
        for (int j = i + 1; j < in.size(); ++j) {
            pairs.add(new String[]{in.get(i), in.get(j)});
        }
    }
    Collections.shuffle(pairs);
    for (final String[] pair : pairs) {
        System.out.println(Arrays.toString(pair));
    }
}

输出:

[B, E]
[A, C]
[A, E]
[A, B]
[B, D]
[C, D]
[C, E]
[B, C]
[A, D]
[D, E]

您创建一个新的List<String[]> pairs,然后循环输入List。在每次迭代中,您遍历输入的其余部分 - 这可以保证您永远不会再次反转相同的对。

填充pairs之后,您可以简单shuffle,然后根据需要选择多对。鉴于来自统一分布的Random个样本,您最终应该在pairs中获得任何排序的相等概率。您也可以将SecureRandom传递到other shuffle method

答案 2 :(得分:0)

可能类似以下内容:

<强>更新

为了抵消误解,以下代码将确保:

  • 独特的对
  • 以随机顺序

假设源列表中没有重复元素,您将拥有n!/(n-k)!个唯一对,其中n是名称数量,k2,因为我们正在创造对。

创建独特的对后,我将它们随机播放以获得随机性。

要使用更好的随机播放算法,可以使用Collections.shuffle(pairs, new SecureRandom())

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class NamePairTest {


  private static class NamePair {
    private final String one;
    private final String two;

    public NamePair(String one, String two) {
      super();
      this.one = one;
      this.two = two;
    }


    @Override
    public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result + ((one == null) ? 0 : one.hashCode());
      result = prime * result + ((two == null) ? 0 : two.hashCode());
      return result;
    }

    @Override
    public boolean equals(Object obj) {
      if (this == obj) return true;
      if (obj == null) return false;
      if (getClass() != obj.getClass()) return false;
      NamePair other = (NamePair) obj;
      if (one == null) {
        if (other.one != null) return false;
      } else if (!one.equals(other.one)) return false;
      if (two == null) {
        if (other.two != null) return false;
      } else if (!two.equals(other.two)) return false;
      return true;
    }

    @Override
    public String toString() {
      return one + " - " + two;
    }
  }


  public List<NamePair> createPairs(List<String> names) {
    List<NamePair> pairs = new ArrayList<>();
    // create all possible unique pairs, given the names list.
    for (String one : names) {
      for (String two : names) {
        // a pair must not have the same name twice
        if (!one.equals(two)) {
          NamePair newPair = new NamePair(one, two);
          // only add the pair if it is not already in the list of pairs.
          // this test will only be necessary if the names list contains duplicates.
          if (!pairs.contains(newPair)) pairs.add(newPair);
        } // if
      } // for
    } // for
    // now shuffle the list, as currently it is ordered.
    Collections.shuffle(pairs);
    return pairs;
  }


  public static void main(String[] args) {
    List<String> availableNames = Arrays.asList("A", "B", "C", "D", "E");
    NamePairTest namePairTest = new NamePairTest();
    for (NamePair pair : namePairTest.createPairs(availableNames)) {
      System.out.println(pair);
    }
  }

}