求除数对
Finding divisor pairs
我正在尝试解决这个练习http://main.edu.pl/en/archive/amppz/2014/dzi,我不知道如何提高我的代码的性能。当程序必须处理超过500,000个唯一数字(如描述中最多2,000,000个)时,就会出现问题。然后用1-8秒循环遍历所有这些数字。我使用的测试来自http://main.edu.pl/en/user.phtml?op=tests&c=52014&task=1263,我通过命令
进行测试program.exe < data.in > result.out
描述:You are given a sequence of n integer a1, a2, ... an. You should determine the number of such ordered pairs(i, j), that i, j equeals(1, ..., n), i != j and ai is divisor of aj.
The first line of input contains one integer n(1 <= n <= 2000000)
The second line contains a sequence of n integers a1, a2, ..., an(1 <= ai <= 2000000).
In the first and only line of output should contain one integer, denoting the number of pairs sought.
For the input data:
5
2 4 5 2 6
the correct answer is: 6
Explanation: There are 6 pars: (1, 2) = 4/2, (1, 4) = 2/2, (1, 5) = 6/2, (4, 1) = 2/2, (4, 2) = 4/2, (4, 5) = 6/2.
:
-总数为2M,唯一编号为635k,总共有3.45亿次迭代
-总共有2M个数字和200万个唯一数字,总共有1.885亿次迭代
#include <iostream>
#include <math.h>
#include <algorithm>
#include <time.h>
#define COUNT_SAME(count) (count - 1) * count
int main(int argc, char **argv) {
std::ios_base::sync_with_stdio(0);
int n; // Total numbers
scanf("%d", &n);
clock_t start, finish;
double duration;
int minVal = 2000000;
long long *countVect = new long long[2000001]; // 1-2,000,000; Here I'm counting duplicates
unsigned long long counter = 0;
unsigned long long operations = 0;
int tmp;
int duplicates = 0;
for (int i = 0; i < n; i++) {
scanf("%d", &tmp);
if (countVect[tmp] > 0) { // Not best way, but works
++countVect[tmp];
++duplicates;
} else {
if (minVal > tmp)
minVal = tmp;
countVect[tmp] = 1;
}
}
start = clock();
int valueJ;
int sqrtValue, valueIJ;
int j;
for (int i = 2000000; i > 0; --i) {
if (countVect[i] > 0) { // Not all fields are setted up
if (countVect[i] > 1)
counter += COUNT_SAME(countVect[i]); // Sum same values
sqrtValue = sqrt(i);
for (j = minVal; j <= sqrtValue; ++j) {
if (i % j == 0) {
valueIJ = i / j;
if (valueIJ != i && countVect[valueIJ] > 0 && valueIJ > sqrtValue)
counter += countVect[i] * countVect[valueIJ];
if (i != j && countVect[j] > 0)
counter += countVect[i] * countVect[j];
}
++operations;
}
}
}
finish = clock();
duration = (double)(finish - start) / CLOCKS_PER_SEC;
printf("Loops time: %2.3f", duration);
std::cout << "sn";
std::cout << "nnCounter: " << counter << "n";
std::cout << "Total operations: " << operations;
std::cout << "nDuplicates: " << duplicates << "/" << n;
return 0;
}
我知道,我不应该在一开始就对数组进行排序,但是我不知道如何使它更好。
任何提示将是伟大的,谢谢!
这里是改进的算法- 0.5s内2M唯一数。感谢@ pjtrail !
#include <iostream>
#include <math.h>
#include <algorithm>
#include <time.h>
#define COUNT_SAME(count) (count - 1) * count
int main(int argc, char **argv) {
std::ios_base::sync_with_stdio(0);
int n; // Total numbers
scanf("%d", &n);
clock_t start, finish;
double duration;
int maxVal = 0;
long long *countVect = new long long[2000001]; // 1-2,000,000; Here I'm counting duplicates
unsigned long long counter = 0;
unsigned long long operations = 0;
int tmp;
int duplicates = 0;
for (int i = 0; i < n; i++) {
scanf("%d", &tmp);
if (countVect[tmp] > 0) { // Not best way, but works
++countVect[tmp];
++duplicates;
} else {
if (maxVal < tmp)
maxVal = tmp;
countVect[tmp] = 1;
}
}
start = clock();
int j;
int jCounter = 1;
for (int i = 0; i <= maxVal; ++i) {
if (countVect[i] > 0) { // Not all fields are setted up
if (countVect[i] > 1)
counter += COUNT_SAME(countVect[i]); // Sum same values
j = i * ++jCounter;
while (j <= maxVal) {
if (countVect[j] > 0)
counter += countVect[i] * countVect[j];
j = i * ++jCounter;
++operations;
}
jCounter = 1;
}
}
finish = clock();
duration = (double)(finish - start) / CLOCKS_PER_SEC;
printf("Loops time: %2.3f", duration);
std::cout << "sn";
std::cout << "nnCounter: " << counter << "n";
std::cout << "Total operations: " << operations;
std::cout << "nDuplicates: " << duplicates << "/" << n;
return 0;
}
我希望下面的工作比OP的算法快得多(优化斜):
- (值和频率的类型应为32位无符号,计数为64位-在计算计数之前进行提升,如果您的语言不这样做。)
- 读取值的个数,n
- 读取每个值v,在其频率[v]上加1(不需要存储)。
- (freq[MAX](或MAX+1)可以静态分配,可能是最优初始化到所有0)
- 计算freq[1]中包含1的对的个数和值的个数
- 对于每1在2..MAX (with freq[i]> 0):
- 从freq[i]计算(i,i)对的个数
- 对于i在2m..MAX中的每一个倍数m:
- (使用m作为循环计数器并增加它,而不是乘以它)
- 从freq[i]和freq[m]计算对(i,m)的个数
- (如果freq[i] = 1,可以省略(i,i)计算并执行针对freq[i] = 1优化的循环的变体)
- (可以执行前面的(外部)循环从2..MAX/2,然后从MAX/2+1…MAX省略处理多个)
对的数量(我)= <子>频率[我]子> C <子> 2子> i,j) = freq[i] * freq[j]对于i≠j 子>
这避免了排序、sqrt
和除法。
其他优化
可以存储不同的值,而不是扫描数组(顺序无关);由此产生的增益或损失取决于1..MAX中值的密度。
如果最大频率为<216,这听起来很有可能,所有的产品都适合32位。可以利用这一点,将数值类型作为模板编写函数,跟踪最大频率,然后为其余部分选择适当的模板实例。这需要N*(比较+分支),并且可以通过使用32位而不是64位执行D2乘法来获得收益,其中D是不同值的数量。除了N <之外,我认为没有简单的方法可以推断出32位足以满足总数。2 <一口> 16> p>如果对n处理器并行化,可以让不同的处理器处理以n模的不同残数。一口>
我考虑过跟踪偶数值的数量,以避免扫描一半的频率,但我认为对于给定参数内的大多数数据集,这将产生很少的优势。
好吧,我不打算把整个算法都写出来,但它肯定可以更快。所以我猜这就是你需要做的:
你的列表已经排序了,所以你可以从这里做出很多假设。以最大值为例。它不会有任何倍数。值的最大值,将最大值除以2。
这里还有一个非常有用的事实。一个倍数的倍数也是一个倍数。(仍然在吗?))。以列表[2 4 12]为例。现在你发现(4,12)是多对的。如果你现在也找到了(2,4),那么你可以推断出12也是2的倍数。因为你只需要数成对的数,你只需要数一下每个数有多少倍数,当你看到这个数本身是倍数时,把它加起来。这意味着最好是向后迭代排序列表,而不是寻找除数。
可能以这样的方式存储它[ (three 2's ), (two 5's), ...]
ie。存储一个数字出现的频率。同样,您不需要跟踪它的id,因为您只需要为它们提供配对的总数。这样存储你的表很有帮助,因为所有的2都有相同数量的倍数。计算一次,然后相乘。
- 没有找到相关文章