我有数百万个固定大小(100)的int数组。每个数组都已排序并具有唯一元素。对于每个数组,我想找到所有具有70%公共元素的数组。现在我每秒进行大约100万次比较(使用Arrays.binarySearch()),这对我们来说太慢了。
有人可以推荐更好的搜索算法吗?
答案 0 :(得分:3)
您可以尝试合并排序,忽略重复项。这是排序数组的O(n)操作。如果两个数组共有70%的元素,则生成的集合将具有130个或更少的唯一整数。在您的情况下,您不需要结果,因此您只需计算唯一条目的数量,并在达到131或两个数组的末尾时立即停止。
编辑(2)以下代码可以使用4个核心在大约47秒内完成~176亿次比较。使用4 cours使代码多线程只快70%。
仅当int值的范围相当小时,才使用BitSet。否则你必须比较int [](如果你需要,我已经把代码留下了)
在47.712秒内进行了176,467,034,428次比较,发现444,888次比赛
public static void main(String... args) throws InterruptedException {
int length = 100;
int[][] ints = generateArrays(50000, length);
final BitSet[] bitSets = new BitSet[ints.length];
for(int i=0;i<ints.length;i++) {
int[] ia = ints[i];
BitSet bs = new BitSet(ia[ia.length-1]);
for (int i1 : ia)
bs.set(i1);
bitSets[i] = bs;
}
final AtomicInteger matches = new AtomicInteger();
final AtomicLong comparisons = new AtomicLong();
int nThreads = Runtime.getRuntime().availableProcessors();
ExecutorService executorService = Executors.newFixedThreadPool(nThreads);
long start = System.nanoTime();
for (int i = 0; i < bitSets.length - 1; i++) {
final int finalI = i;
executorService.submit(new Runnable() {
public void run() {
for (int j = finalI + 1; j < bitSets.length; j++) {
int compare = compare(bitSets[finalI], bitSets[j]);
if (compare <= 130)
matches.incrementAndGet();
comparisons.addAndGet(compare);
}
}
});
}
executorService.shutdown();
executorService.awaitTermination(1, TimeUnit.HOURS);
long time = System.nanoTime() - start;
System.out.printf("Peformed %,d comparisons in %.3f seconds and found %,d matches %n",comparisons.longValue(),time/1e9, matches.intValue());
}
private static int[][] generateArrays(int count, int length) {
List<Integer> rawValues = new ArrayList<Integer>(170);
for (int i = 0; i < 170; i++)
rawValues.add(i);
int[][] ints = new int[count][length];
Random rand = new Random(1);
for (int[] ia : ints) {
Collections.shuffle(rawValues, rand);
for (int i = 0; i < ia.length; i++)
ia[i] = (int) (int) rawValues.get(i);
Arrays.sort(ia);
}
return ints;
}
private static int compare(int[] ia, int[] ja) {
int count = 0;
int i=0,j=0;
while(i<ia.length && j<ja.length) {
int iv = ia[i];
int jv = ja[j];
if (iv < jv) {
i++;
} else if (iv > jv) {
j++;
} else {
count++; // duplicate
i++;
j++;
}
}
return ia.length + ja.length - count;
}
private static int compare(BitSet ia, BitSet ja) {
BitSet both = new BitSet(Math.max(ia.length(), ja.length()));
both.or(ia);
both.or(ja);
return both.cardinality();
}
答案 1 :(得分:3)
这样的事情应该完成这项工作(假设数组已排序并包含唯一元素):
public static boolean atLeastNMatchingElements(final int n,
final int[] arr1,
final int[] arr2){
/* check assumptions */
assert (arr1.length == arr2.length);
final int arrLength = arr2.length;
{ /* optimization */
final int maxOffset = Math.max(arrLength - n, 0);
if(arr1[maxOffset] < arr2[0] || arr2[maxOffset] < arr1[0]){
return false;
}
}
int arr2Offset = 0;
int matches = 0;
/* declare variables only once, outside loop */
int compResult; int candidate;
for(int i = 0; i < arrLength; i++){
candidate = arr1[i];
while(arr2Offset < arrLength - 1){
compResult = arr2[arr2Offset] - candidate;
if(compResult > 0){
break;
} else{
arr2Offset++;
if(compResult == 0){
matches++;
break;
}
}
}
if(matches == n){
return true;
}
/* optimization */
else if(matches < n - (arrLength - arr2Offset)){
return false;
}
}
return false;
}
样本使用:
public static void main(final String[] args){
final int[] arr1 = new int[100];
final int[] arr2 = new int[100];
int x = 0, y = 0;
for(int i = 0; i < 100; i++){
if(i % 10 == 0){
x++;
}
if(i % 12 == 0){
y++;
}
arr1[i] = x;
arr2[i] = y;
x++;
y++;
}
System.out.println(atLeastNMatchingElements(70, arr1, arr2));
System.out.println(atLeastNMatchingElements(95, arr1, arr2));
}
<强>输出:强>
真
假
我现在尝试优化上面的代码。请检查代码块是否标记为
/* optimization */
明显不同。在优化之后,我会重构代码以将其归结为一个或两个返回语句。
答案 2 :(得分:3)
您可以进行两种快速优化。
如果数组A的start元素大于B的end元素,则它们通常不能有共同的元素。
他们的另一个是三角不等式的东西:
f(B,C) <= 100 - |f(A,B)-f(A,C)|
原因是(假设f(A,B) > f(A,C)
)至少有f(A,B) - f(A,C)
个元素同时存在于A和B中但不存在于C中。这意味着它们不能是B和C.