В связи с появлением поддержки javascript на codeforces (что меня очень обрадовало), я решил попробовать решить на нём пару задач. Результаты этой попытки, а также этот комментарий сподвигли меня на написание этой статьи. Итак, ниже представлен краткий список наиболее острых проблем и нюансов, с которыми вам скорее всего прийдётся столкнуться, если вы хотите использовать javascript на codeforces.
1) Вывод большого объёма данных мелкими порциями
Итак, рассмотрим следующий пример кода:
for(var i=0; i<1000000; i++) print('foobar');
Его запуск через "запуск" на codeforces даёт следующий результат:
Использовано: 702 мс, 0 КБ
Рассмотрим, как можно оптимизировать вывод:
var answer = '';
for(var i=0; i<1000000; i++) answer+='foobar\n';
print(answer);
Результат:
Использовано: 140 мс, 37852 КБ
Как видим, использование буферизации ускорило код в 5 раз, засчёт уменьшения издержек на вызовы функций. Однако потребление памяти очевидным образом возросло. Но навряд ли объём вывода какой-либо программы может значительно повлиять на получение программой Memory Limit Exceed.
2) Работа с большими числами (не длинная арифметика, просто с большими)
Итак, рассмотрим, как представляются числа в javascript, а точнее, в его данной конкретной реализации, V8. В случае, если число целое, то оно хранится таким образом:
- Если это возможно, то как 31-битное целое
- float64 (C++ double) в противном случае.
Рассмотрим, к чему это приводит: пока наше число целое, всё хорошо. Но числа с плавающей точкой имеют следующую особенность: количество представимых в них целых чисел ограничено размером мантиссы. У double размер мантиссы 52 бита, то есть величина целого числа, с которым мы вприципе можем работать штатными средствами javascript, не прибегая к каким-либо ухищрениям в виде длинной арифметики, не преышает по модулю 2^52 = 4503599627370496. Поскольку в этом числе 16 цифр, мы можем сказать, что это гарантирует нам представление 15 десятичных цифр(против 19 для long long). Это приводит к невозможности решения некоторых задач (например, 374B, которую, я собственно и пытался сдать на javascript), штатными средствами.
3) Дополнительные источники информации + пара полезностей
Про оптимизацию ваших скриптов под V8 можно дополнительно почитать тут. Также, поскольку у меня не было d8
, я использовал для тестирования NodeJS. Помимо прочего, причиной этого стала более полная поддержка NodeJS в IDE и текстовых редакторах (например, WebStorm и Sublime Text соответственно). Для того, чтобы интерфейс взаимодействия оставался неизменным, и можно было спокойно копипастить код, не переделывая с нодовских process.stdout.write или console.log на readline'ы и print'ы, я написал следующий код под ноду:
process.stdin.resume();
process.stdin.setEncoding('utf8');
var input= '', readline, print;
process.stdin.on('data', function(chunk) {
input+=chunk;
});
process.stdin.on('end', function() {
input = input.split('\n');
print = function(data) {process.stdout.write(data+'\n')};
var inputLineIndex = 0;
readline = function(){return inputLineIndex>=input.length?undefined:input[inputLineIndex++]};
process.exit(main() || 0);
});
function main()
{
// Your code here
return 0;
}
Посылать в качестве решения функцию main, и не забыть добавить в конце вызов или обернуть в замыкание:
function main()
{
// Your code here
return 0;
}
main();
// или
(function main()
{
// Your code here
return 0;
})()
P.S. Рассмотрено совсем немного проблем, но это были те проблемы, с которыми я столкнулся СРАЗУ, как только попытался решать задачи на javascript. Если вы можете предложить ещё какие-либо важные "грабли", на которые легко наступить при решении на javascript многих задач, пишите в комментарии, буду рад дополнить статью новыми примерами.
А если вместо этого в массив запихнуть и потом join с '\n' сделать?
А то что-то эта повторная конкатенация по-моему у многих нервный тик в глазу может вызвать с точки зрения эффективности :)
Использовано: 187 мс, 46412 КБ
Это даёт результат, который хуже как по времени выполнения, так и по памяти.
Мистика, что тут сказать :)
Но в любом случае надо было проверить. Спасибо!
Попробовал ещё то же с не константными строками — тоже схожие результаты. Погуглил интернет — уверяют что в почти всех реализациях конкатенация через += вполне хорошо заточена
Хотел я потестить javascript для некоторых своих задачек, но не смог найти консольного интерпретатора( А хочется обычный легковесный интерпретатор, который умеет считывать из консоли readline, выводить print, и математику. Неужели такого нет?
P.S. nodejs не предлагать.
Rhino
jar-ку можно запускать консольно, прямо интерпретатор будет...
Правда это не v8 — эта работает на джаве (и её урезанная версия в самой джаве есть). Она точно помедленнее (хотя вроде бы можно заставить код компилиться в классы) ну и кое в чём работает не так — не знаю, найдёте ли вы отличия с полтыка. Зато можно все джаванские классы дёргать.
А ссылку на скомпилированный под винду v8 вроде MikeMirzayanov прямо в своём посте оставлял.
UPD: принт там вроде из коробки есть, а readline наверное придётся самому определить через BufferedReader например.
UPD2: А интерпретатор в самом браузере кстати (в хроме мне нра) — по-моему отличная идея?
В силу специфики задачек, браузер там может быть только links и ко)
Чем не угодила нода? Я же накидал мини-шаблончик как сделать, чтобы там были readline и print. Или это исключительно религия не позволяет? =)
О ноде как-то сложилось впечатление, что это микроскоп, а мне бы молоток) Да и я не понимаю, как работает этот шаблончик. Нужно будет разобраться, если действительно ничего простенького нет.
А как в d8 с чтением, записью в файл?
read(filename)
— читает все содержимое файла в строку.Записи в файл нет как класса. Желающие самостоятельно разочароваться могут открыть исходники, см. строки 860-914 :)
Кроме того, автор блога не написал про буферизованную функцию
write
:-- отработает примерно за 200 мс, как и вариант с ручной буферизацией.
А как же быть? Разве нет никакого способа больше отправлять вывод в файл на js в d8? Понятно что из консоли можно, а вот в коде как...
Напрямую из кода — нет. В версиях под линукс и макось есть возможность вызвать из кода консольную команду, что-нибудь типа
, но в версии под windows функция
os.system
не определена. А codeforces работает именно под этой ОС. Так что остается просить, чтобы MikeMirzayanov заменил либо d8 на Node.js или io.js, либо windows на linux.А что говорит спецификация JavaScript насчёт хранения целых чисел? Неужели разрешает то, что, начиная с какого-то момента, они представляются неточно?
EDIT: Вообще, какой смысл для целых чисел использовать
float64
вместоint64
?Спецификация ECMAScript говорит нам о существовании единственного числового типа Number, который по сути является float64. Хочется целых чисел, превышающих по модулю 2^52 — добро пожаловать в мир длинной арифметики. В этом — то и проблема.
ты не знаешь других участников, которые на js codeforces пишут?
for some reasons, I couldn't leave comment in Russian. Question: do you know someone else on codeforces who write solutions in JS? Thanks for your post, very usefull!
Open status of big contest, for example http://codeforces.me/contest/580/status, and in filter choose language
for 374B https://github.com/silentmatt/javascript-biginteger/blob/master/biginteger.js looks like where are no simple native solution for js