Блог пользователя Kondrat_Voronov

Автор Kondrat_Voronov, 10 лет назад, По-русски

В связи с появлением поддержки 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 многих задач, пишите в комментарии, буду рад дополнить статью новыми примерами.

  • Проголосовать: нравится
  • +24
  • Проголосовать: не нравится

»
10 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

answer+='foobar\n';

А если вместо этого в массив запихнуть и потом join с '\n' сделать?

А то что-то эта повторная конкатенация по-моему у многих нервный тик в глазу может вызвать с точки зрения эффективности :)

  • »
    »
    10 лет назад, # ^ |
    Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится
    var answer = [];
    for(var i=0; i<1000000; i++) answer[i]='foobar';
    answer=answer.join('\n');
    print(answer);
    

    Использовано: 187 мс, 46412 КБ

    Это даёт результат, который хуже как по времени выполнения, так и по памяти.

    • »
      »
      »
      10 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится

      Мистика, что тут сказать :)

      Но в любом случае надо было проверить. Спасибо!

      Попробовал ещё то же с не константными строками — тоже схожие результаты. Погуглил интернет — уверяют что в почти всех реализациях конкатенация через += вполне хорошо заточена

»
10 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

Хотел я потестить javascript для некоторых своих задачек, но не смог найти консольного интерпретатора( А хочется обычный легковесный интерпретатор, который умеет считывать из консоли readline, выводить print, и математику. Неужели такого нет?

P.S. nodejs не предлагать.

  • »
    »
    10 лет назад, # ^ |
    Rev. 3   Проголосовать: нравится 0 Проголосовать: не нравится

    Rhino

    jar-ку можно запускать консольно, прямо интерпретатор будет...

    Правда это не v8 — эта работает на джаве (и её урезанная версия в самой джаве есть). Она точно помедленнее (хотя вроде бы можно заставить код компилиться в классы) ну и кое в чём работает не так — не знаю, найдёте ли вы отличия с полтыка. Зато можно все джаванские классы дёргать.

    А ссылку на скомпилированный под винду v8 вроде MikeMirzayanov прямо в своём посте оставлял.

    UPD: принт там вроде из коробки есть, а readline наверное придётся самому определить через BufferedReader например.

    UPD2: А интерпретатор в самом браузере кстати (в хроме мне нра) — по-моему отличная идея?

    • »
      »
      »
      10 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится

      В силу специфики задачек, браузер там может быть только links и ко)

  • »
    »
    10 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    Чем не угодила нода? Я же накидал мини-шаблончик как сделать, чтобы там были readline и print. Или это исключительно религия не позволяет? =)

    • »
      »
      »
      10 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится

      О ноде как-то сложилось впечатление, что это микроскоп, а мне бы молоток) Да и я не понимаю, как работает этот шаблончик. Нужно будет разобраться, если действительно ничего простенького нет.

»
10 лет назад, # |
  Проголосовать: нравится +5 Проголосовать: не нравится

А как в d8 с чтением, записью в файл?

  • »
    »
    10 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    read(filename) — читает все содержимое файла в строку.

    Записи в файл нет как класса. Желающие самостоятельно разочароваться могут открыть исходники, см. строки 860-914 :)

    Кроме того, автор блога не написал про буферизованную функцию write:

    for(var i=0; i<1000000; i++) write('foobar\n');
    

    -- отработает примерно за 200 мс, как и вариант с ручной буферизацией.

    • »
      »
      »
      9 лет назад, # ^ |
      Rev. 3   Проголосовать: нравится 0 Проголосовать: не нравится

      А как же быть? Разве нет никакого способа больше отправлять вывод в файл на js в d8? Понятно что из консоли можно, а вот в коде как...

      • »
        »
        »
        »
        9 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится

        Напрямую из кода — нет. В версиях под линукс и макось есть возможность вызвать из кода консольную команду, что-нибудь типа

        os.system("echo " + answer + " > output.txt");
        

        , но в версии под windows функция os.system не определена. А codeforces работает именно под этой ОС. Так что остается просить, чтобы MikeMirzayanov заменил либо d8 на Node.js или io.js, либо windows на linux.

»
10 лет назад, # |
Rev. 3   Проголосовать: нравится 0 Проголосовать: не нравится
В случае, если число целое, то оно хранится таким образом:

• Если это возможно, то как 31-битное целое
• float64 (C++ double) в противном случае.

А что говорит спецификация JavaScript насчёт хранения целых чисел? Неужели разрешает то, что, начиная с какого-то момента, они представляются неточно?

EDIT: Вообще, какой смысл для целых чисел использовать float64 вместо int64?

  • »
    »
    10 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    Спецификация ECMAScript говорит нам о существовании единственного числового типа Number, который по сути является float64. Хочется целых чисел, превышающих по модулю 2^52 — добро пожаловать в мир длинной арифметики. В этом — то и проблема.

»
9 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

ты не знаешь других участников, которые на js codeforces пишут?

»
9 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

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!

»
9 лет назад, # |
Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

for 374B https://github.com/silentmatt/javascript-biginteger/blob/master/biginteger.js looks like where are no simple native solution for js