我正在创建一个程序,其中涉及一段代码,我需要从单个项目列表(名称)中选择对。
初始列表来自单个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语句永远不会成立。
以列表为例:
- 甲
- 乙
- ç
- d
- 电子
醇>
程序在执行期间可以先将A到D排序,然后再创建一个名称对,如下所示:
- A - B
- B - C
- C - D
- D - C
醇>
哪个会留下E - E
作为最后一对,但不允许成对(因为项目/名称不是唯一)。由于配对分配是随机的,有时它会起作用,有时它并不是坦白地说很烦人......
我确信解决方案非常简单,但出于某种原因,我似乎无法找到解决此问题的方法。任何帮助表示赞赏。
答案 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
是名称数量,k
是2
,因为我们正在创造对。
创建独特的对后,我将它们随机播放以获得随机性。
要使用更好的随机播放算法,可以使用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);
}
}
}