[英]Significant C++ code execution slowdown
因此,我必须解决一个USACO问题,涉及计算所有<= 100M的素数并打印其中的所有素数,这些素数是回文数,而限制是16MB内存和1秒执行时间。 因此,我不得不进行很多优化。 请看下面的代码块:
for(int i = 0; i < all.size(); ++i)
{
if(all[i] < a) continue;
else if(all[i] > b) break;
if(isPrime(all[i]))
{
char buffer[50];
//toString(all[i], buffer);
int c = all[i];
log10(2);
buffer[3] = 2;
//buffer[(int)log10(all[i])+1] = '\n';
//buffer[(int)log10(all[i])+2] = '\0';
//fputs(buffer, pFile);
}
}
现在,它在令人满意的0.5秒范围内执行,但是当我将log10(2)
更改为log10(all[i])
它飞涨了将近2秒! 没有明显的原因。 我将all [i]分配给变量c,它根本不会减慢执行速度,但是当我将all [i]作为参数传递时,它会使代码慢4倍! 有什么想法为什么会发生以及如何解决?
整个代码:
/*
ID: xxxxxxxx
PROG: pprime
LANG: C++11
*/
#include <fstream>
#include <iostream>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <string>
#include <cstring>
#include <algorithm>
#include <list>
#include <ctime>
#include <cstdio>
using namespace std;
typedef struct number Number;
ifstream fin("pprime.in");
ofstream fout("pprime.out");
int MAXN = 100000000;
unsigned short bits[2000000] = {};
vector<int> primes;
vector<int> all;
int a, b;
short getBit(int atPos)
{
int whichNumber = (atPos-1) / 16;
int atWhichPosInTheNumber = (atPos-1) % 16;
return ((bits[whichNumber] & (1 << atWhichPosInTheNumber)) >> atWhichPosInTheNumber);
}
void setBit(int atPos)
{
int whichNumber = (atPos-1) / 16;
int atWhichPosInTheNumber = (atPos-1) % 16;
int old = bits[whichNumber];
bits[whichNumber] = bits[whichNumber] | (1 << atWhichPosInTheNumber);
}
void calcSieve()
{
for(int i = 2; i < MAXN; ++i)
{
if(getBit(i) == 0)
{
for(int j = 2*i; j <= (MAXN); j += i)
{
setBit(j);
}
primes.push_back(i);
}
}
}
int toInt(list<short> integer)
{
int number = 0;
while(!integer.empty())
{
int current = integer.front();
integer.pop_front();
number = number * 10 + current;
}
return number;
}
void toString(int number, char buffer[])
{
int i = 0;
while(number != 0)
{
buffer[i] = number % 10 + '0';
number /= 10;
}
}
void DFS(list<short> integer, int N, int atLeast)
{
if(integer.size() > N)
{
return;
}
if(!(integer.size() > 0 && (integer.front() == 0 || integer.back() % 2 == 0)) && atLeast <= integer.size())
{
int toI = toInt(integer);
if(toI <= b) all.push_back(toInt(integer));
}
for(short i = 0; i <= 9; ++i)
{
integer.push_back(i);
integer.push_front(i);
DFS(integer, N, atLeast);
integer.pop_back();
integer.pop_front();
}
}
bool isPrime(int number)
{
for(int i = 0; i < primes.size() && number > primes[i]; ++i)
{
if(number % primes[i] == 0) return false;
}
return true;
}
int main()
{
int t = clock();
ios::sync_with_stdio(false);
fin >> a >> b;
MAXN = min(MAXN, b);
int N = (int)log10(b) + 1;
int atLeast = (int)log10(a) + 1;
for(short i = 0; i <= 9; ++i)
{
list<short> current;
current.push_back(i);
DFS(current, N, atLeast);
}
list<short> empty;
DFS(empty, N, atLeast);
sort(all.begin(), all.end());
//calcSieve
calcSieve();
//
string output = "";
int ends = clock() - t;
cout<<"Exexution time: "<<((float)ends)/CLOCKS_PER_SEC<<" seconds";
cout<<"\nsize: "<<all.size()<<endl;
FILE* pFile;
pFile = fopen("pprime.out", "w");
for(int i = 0; i < all.size(); ++i)
{
if(all[i] < a) continue;
else if(all[i] > b) break;
if(isPrime(all[i]))
{
char buffer[50];
//toString(all[i], buffer);
int c = all[i];
log10(c);
buffer[3] = 2;
//buffer[(int)log10(all[i])+1] = '\n';
//buffer[(int)log10(all[i])+2] = '\0';
//fputs(buffer, pFile);
}
}
ends = clock() - t;
cout<<"\nExexution time: "<<((float)ends)/CLOCKS_PER_SEC<<" seconds";
ends = clock() - t;
cout<<"\nExexution time: "<<((float)ends)/CLOCKS_PER_SEC<<" seconds";
fclose(pFile);
//fout<<output;
return 0;
}
我认为您已经倒退了。 生成所有可能的回文集(如果DFS
实际上正在执行此功能……使我感到困惑),然后检查其中哪些是素数似乎很奇怪。 特别是因为无论如何您都必须生成素数。
另一件事是,你正在做一个线性搜索isPrime
,这是不考虑的事实数组进行排序的优势。 请改用二进制搜索。
而且,对DFS
函数使用list
而不是vector
会损害您的运行时。 尝试使用deque
。
现在,所有这些都说明了,我认为您应该以其他方式进行此操作。 有很多回文不是最主要的回文。 生成它们有什么意义? 您只需检查一个简单的堆栈即可检查数字是否是回文。 像这样:
bool IsPalindrome( unsigned int val )
{
int digits[10];
int multiplier = 1;
int *d = digits;
// Add half of number's digits to a stack
while( multiplier < val ) {
*d++ = val % 10;
val /= 10;
multiplier *= 10;
}
// Adjust for odd-length palindrome
if( val * 10 < multiplier ) --d;
// Check remaining digits
while( val != 0 ) {
if(*(--d) != val % 10) return false;
val /= 10;
}
return true;
}
这完全避免了调用log10
的需要,并且消除了所有回文产生。 筛子非常快,之后您只需测试几千个素数,其中大部分不会是回文。
现在,您的整个程序将变成这样:
calcSieve();
for( vector<int>::iterator it = primes.begin(); it != primes.end(); it++ ) {
if( IsPalindrome(*it) ) cout << *it << "\n";
}
需要指出的另一件事。 实际上有两件事:
int MAXN = 100000000;
unsigned short bits[2000000] = {};
bits
太短,无法代表1亿个标志。 bits
未初始化。 要解决这两个问题,请尝试:
unsigned short bits[1 + MAXN / 16] = { 0 };
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.