В этом году моя команда и, как следствие я, решили перейти с плюсов на Яву.
Как следствие - я стал получать МЛ в весьма неожиданных(для меня) местах.
Например МЛ - вот по этому решению задачи D сегодня.
http://pastebin.com/EDvva5p4
Вопрос к людям с богатым опытом использования Явы на контестах, что именно не так я делаю?
На данный момент - после вердикта МЛ - я обычно стремлюсь избавиться от структур/локальных переменных в циклах, упорядачиваю индексы в массивах от меньшего к большему, но после подобных преобразований читать код становится действительно сложно.
Нет ли более простых тактик избежания МЛ?
P.S. Спасиюо udalov - на дорешивании АС по этой задаче я-таки получил. Однако память все равно кушается совершенно(для меня) не предсказуемо. Исправленное решение заняло 259000 килобайт памяти, то есть вписалось тютелька-в-тютельку.
Хотелось бы услышать больше конструктивной критики/советов по поводу укращения аппетитов Явы.
Как следствие - я стал получать МЛ в весьма неожиданных(для меня) местах.
Например МЛ - вот по этому решению задачи D сегодня.
http://pastebin.com/EDvva5p4
Вопрос к людям с богатым опытом использования Явы на контестах, что именно не так я делаю?
На данный момент - после вердикта МЛ - я обычно стремлюсь избавиться от структур/локальных переменных в циклах, упорядачиваю индексы в массивах от меньшего к большему, но после подобных преобразований читать код становится действительно сложно.
Нет ли более простых тактик избежания МЛ?
P.S. Спасиюо udalov - на дорешивании АС по этой задаче я-таки получил. Однако память все равно кушается совершенно(для меня) не предсказуемо. Исправленное решение заняло 259000 килобайт памяти, то есть вписалось тютелька-в-тютельку.
Хотелось бы услышать больше конструктивной критики/советов по поводу укращения аппетитов Явы.
ИМХО весьма "сомнительный (в общем случае) полезный совет".
ArrayList внутри себя вроде как содержит обычный массив для хранения элементов.
Поэтому, когда у нас он переполняется, то происходит не особо быстрая процедура создания более большого массива и перекопирование содержимого старого в новый.
Авторы наверняка позаботились о том, чтобы найти "золотую середину" между расходом памяти и быстродействием, поэтому устанавливая свои размеры не плохо бы прикинуть - а не переборщим ли мы с памятью и не будет ли добавление/удаление элементов слишком часто приводить к этому копированию.
На мой взгляд - массив ребер (10^6 * 10 * 4 ~ 40mB), TreeSet пар - (10^6)* (8 + c) - где c - константа TreeSet'a - вряд ли она гигантская, ну, скажем 50 байт на одну запись.
Итого - примерно 100 mB.
Так я думал до System test'a =)
Понятно, что всегда можно запустить макс тест - посмотреть память, получить более точные оценки. Уже который раз обещаю себе впредь быть умнее=)
Однако куда Ява кушает память - для меня по прежнему загадка.
Понятно в чем проблема .
Добавлю ещё то, что размер хранилища ArrayList выделяется нарастающими порциями. То есть чем больше добавляете - тем больший запас создается на последующие добавления, поэтому потребность в памяти при большом количестве элементов может быть весьма существенная и это тоже стоит учесть.
Для сравнения, в C#, в то время, когда я им был вынужден заниматься, коллекции были весьма "отвратные" (например, ассоциирование с уже существующим ключом в Dictionary бросало эксепшен, да и эквивалента Set не было), а про косяки в алгоритмах, я думаю, все уже наслышаны (тогда там в качестве сортировки был легко убиваемый quicksort).
Java-специфичные TL и ML появляются на довольно редких извращенных задачах. Например, удалить дубликаты в списке из десятка миллионов треугольников за 2 секунды получается непросто, от объекта "треугольник" приходится отказываться, как и от алгоритмов и структур стандартной библиотеки.
Ну и есть плюсы в виде умных IDE, умеющих находить множество таких ошибок, которые в C++ научатся автоматически находить еще очень нескоро (если вообще научатся).
На мой взгляд, и я в этом не одинок, как главный язык для контеста Java очень хороша.
И хотя из-за использования более продвинутый среды результаты контестов скорее улучшились, подобные места меня печалят и заставляют задуматься, что же я делаю не так.
Плюсы - вполне приятный язык и на нем можно вполне комфортно разрабатывать программы. Однако на онсайтах большей частью плагинов и сред пользоваться нельзя, что несколько затрудняет эффективное программирование на котнесте. Так, например, к VC есть очень приятный плагин Visual Assist, который нельзя использовать на контестах. Тоже самое касается LLVM Clang - не встречал ни на одном из онсайтов. Вообще на онсайтах редко встречается что-то кроме Visual Studio. Эта среда - вполне удобна для разработки под .net, но как среда разработки под плюсы, без плагинов - на мой взгляд слабовата.
ну или что-то похожее.
Eclipse нашел и подсветил ошибку, что-то вроде: переменная answerY может быть заинлайнена. Подсветку я заметил перед сабмитом, место показалось "странным". Я нашел и исправил ошибку, заслал, AC. До конца контеста оставались считанные секунды.
Если бы мне было не влом, я бы порылся в петрозаводских архивах и нашел бы точно исходник, а также точно указал бы место ошибки и "вердикт" Eclipse.
В общем и целом, control flow в Java проверяется тщательнее, чем в C++, даже стандартным компилятором (хотя компиляторы C++ к этому уровню подбираются). А уж IDE, анализирующие все дерево разбора целиком, проверяют все еще более доскональным образом.
Кроме того, в Java IDEA (ну и в Resharper заодно) есть intentions, они подсказывают, как можно улучшить код. Иногда такие подсказки также выявляют баги кодирования.
P.S.: Парсер - лох :-(
Кроме того Ява - основной язык одново одного из моих сокомандников.
А вот на онсайтах ее почему то нет=(
4560 мс
> Хотелось бы услышать больше конструктивной критики/советов по поводу укращения аппетитов Явы.
В данном случае проблема больше не в яве, а в том как этот сайт меряет используемую память (на topcoder-е вон ограничение 64 метра, но проблем с ML в ява с десять раз меньше). Судя по всему на этом сайте память меряется тем же методом как и для C/C++... а ява - это чуть другая технология, тут сборщик мусора есть. Т.е. тут нельзя просто взять и освободить память... память освободится когда в ней будет надобность - для практических задач это очень хорошо подходит, местами даже имеет выигрыш в производительности по сравнению с С/С++... но не здесь. Если вы напишете byte a[] = new byte[200*1024*1024]; a = null; a = new byte[200*1024*1024]; a = null;, то вроде бы прога не использует больше 200МБ памяти, но на этом сайте вы вероятно получите ML exceed, поскольку память освобождается исключительно сборщиком мусора, а он запускается по таймеру или при достижении лимита указанного виртуальной машине, но вот незадача - тут java запускается с параметром -Xmx256m, т.о. виртуальная машина контролирует, чтобы ваша прога не юзала больше 256МБ, но вот сайт то меряет объем памяти процесса средствами ОС как и для решений на С/С++, и как можно догадаться существуют накладные расходы и процесс в ОС занимает чуть больше. А контестант не может указать параметр запуска, чтобы задать более жесткое ограничение, например -Xmx200m - это бы часто решило проблему.
Иногда может помочь возможность запускать сборщик принудительно (System.gc()), но во-первых это очень тормознуто, т.к. запускается полный сборщик, во-вторых в большинстве случаев это не поможет - контестант, как я сказал, не может задавать параметры виртуальной машине, т.е. и тип/параметры сборки мусора. А на некоторых этапах и для некоторых участков памяти сборщик мусора запускается копирующий - а как вы понимаете из названия для его работы временно надо в два раза больше памяти. Я не знаю как тут оно точно меряется... но на ява лучше писать решения, которые не будут выделять более 200МБ, иначе вам не сюда.
Просто это уже не первый случай когда я вот-так спотыкаюсь и хотелось бы как-нибудь прервать череду подобных неудач.
Казалось бы, самое разумное - посмотреть оптимальные решения, которые разбираются с первой частью лучше (либо с графом без объектов, либо вообще без графа, который тут ни при чем), а со второй частью гораздо лучше (выводом ответа сразу, как только получены размеры компонент связности).
Что ж, повторюсь. С первым более-менее разобрались. Второй.
Мой совет заключался в том, что не надо задавать вопрос "как соптимизировать", когда поленились посмотреть уже написанные оптимальные. И кажется, я нигде не упоминал то, что сам написал. Посмотрите у Egor'a (disjoint sets + вывод ответа вместо ненужной эмуляции), посмотрите у niyaznigmatul (как искать компоненты в графе, если disjoint sets чем-то не угодили).
> потому и писал про избавления от локальных переменных в циклах.
Вот это спорный вопрос... Оба компилятора в яве (в байт-код и JIT) не такие уж и тупые, особенно последний. Локальная переменная в небольшом цикле может быть оптимизирована до регистровой (если она не объявлена volatile, разумеется). А вот оптимизировать глобальную переменную оно может уже не догадаться.
Я в своем АС решении D на С++ ошибся ноликом в 3х местах.
В итоге я мог хранить граф на 10^7 вершин и 10^7 ребер.
Такое решение съело 246,5 Мб. Еще чуть чуть - и привет ML.
Посылка 375577 если кому интересно))
Edge[] first;
class Edge
{
int from, to;
Edge next;
void add() {next = first[from];first[from]=this;}
public Edge(int x, int y) { from = x ; to = y; add();}
}
добавление - new Edge(x, y)
проход - for (Edge e = first[x]; e!= null; e = e.next)
UPD: можно добавить remove() который удаляет ребро если оно первое в списке. Удобно для эйлерова цикла.