Несколько идей:
- Читайте целые числа, используя
std::scanf
, а не std::istream
.Последний, как известно, медленнее по нескольким причинам, даже при вызове std::ios::sync_with_stdio(false)
. - Чтение файла путем сопоставления его с памятью.
- Разбор целых чисел быстрее, чем
scanf
и strtol
.
Пример:
#include <cstdio>
int main() {
int n, m, a[1600000];
if(2 != std::scanf("%d %d", &n, &m))
throw;
for(int i = 0; i < n; ++i)
if(1 != std::scanf("%d", a + i))
throw;
}
Вы также можете развернуть этот цикл scanf
для чтения нескольких целых чисел за один вызов.Например:
#include <cstdio>
constexpr int step = 64;
char const fmt[step * 3] =
"%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d "
"%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d "
"%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d "
"%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d"
;
void main() {
int a[1600000];
int n, m;
if(2 != std::scanf("%d %d", &n, &m))
throw;
for(int i = 0; i < n; i += step) {
int expected = step < n - i ? step : n - i;
int* b = a + i;
int read = scanf(fmt + 3 * (step - expected),
b + 0x00, b + 0x01, b + 0x02, b + 0x03, b + 0x04, b + 0x05, b + 0x06, b + 0x07,
b + 0x08, b + 0x09, b + 0x0a, b + 0x0b, b + 0x0c, b + 0x0d, b + 0x0e, b + 0x0f,
b + 0x10, b + 0x11, b + 0x12, b + 0x13, b + 0x14, b + 0x15, b + 0x16, b + 0x17,
b + 0x18, b + 0x19, b + 0x1a, b + 0x1b, b + 0x1c, b + 0x1d, b + 0x1e, b + 0x1f,
b + 0x20, b + 0x21, b + 0x22, b + 0x23, b + 0x24, b + 0x25, b + 0x26, b + 0x27,
b + 0x28, b + 0x29, b + 0x2a, b + 0x2b, b + 0x2c, b + 0x2d, b + 0x2e, b + 0x2f,
b + 0x30, b + 0x31, b + 0x32, b + 0x33, b + 0x34, b + 0x35, b + 0x36, b + 0x37,
b + 0x38, b + 0x39, b + 0x3a, b + 0x3b, b + 0x3c, b + 0x3d, b + 0x3e, b + 0x3f);
if(read != expected)
throw;
}
}
Другим вариантом является ручной анализ целых чисел (здесь может помочь отображение файла в память, и есть гораздо более быстрые алгоритмы синтаксического анализа целых чисел, чем этот и стандартный atoi/strtol
, см. Fastware - Андрей Александреску ):
int main() {
int n, m, a[1600000];
if(2 != std::scanf("%d %d", &n, &m))
throw;
for(int i = 0; i < n; ++i) {
int r = std::getchar();
while(std::isspace(r))
r = std::getchar();
bool neg = false;
if('-' == r) {
neg = true;
r = std::getchar();
}
r -= '0';
for(;;) {
int s = std::getchar();
if(!std::isdigit(s))
break;
r = r * 10 + (s - '0');
}
a[i] = neg ? -r : r;
}
}
Еще один способ - отобразить файл в память и быстрее его проанализировать:
#include <boost/iostreams/device/mapped_file.hpp>
inline int find_and_parse_int(char const*& begin, char const* end) {
while(begin != end && std::isspace(*begin))
++begin;
if(begin == end)
throw;
bool neg = *begin == '-';
begin += neg;
int r = 0;
do {
unsigned c = *begin - '0';
if(c >= 10)
break;
r = r * 10 + static_cast<int>(c);
} while(++begin != end);
return neg ? -r : r;
}
void main() {
boost::iostreams::mapped_file f("random-1600000.txt", boost::iostreams::mapped_file::readonly);
char const* begin = f.const_data();
char const* end = begin + f.size();
int n = find_and_parse_int(begin, end);
int m = find_and_parse_int(begin, end);
int a[1600000];
for(int i = 0; i < n; ++i)
a[i] = find_and_parse_int(begin, end);
}
Исходный код эталонного теста .
Обратите внимание, что результаты могут значительно различаться в разных версиях компиляторов и стандартных библиотек:
- CentOS выпуск 6.10, g ++ - 6.3.0, Intel CoreПроцессор i7-4790 @ 3,60 ГГц
---- Best times ----
seconds, percent, method
0.167985515, 100.0, getchar
0.147258495, 87.7, scanf
0.137161991, 81.7, iostream
0.118859546, 70.8, scanf-multi
0.034033769, 20.3, mmap-parse-faster
- Ubuntu 18.04.2 LTS, g ++ - 8.2.0, процессор Intel Core i7-7700K @ 4,20 ГГц
---- Best times ----
seconds, percent, method
0.133155952, 100.0, iostream
0.102128208, 76.7, scanf
0.082469185, 61.9, scanf-multi
0.048661004, 36.5, getchar
0.025320109, 19.0, mmap-parse-faster