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

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

Здравствуйте.

Проблема, которую я только что обнаружил, а I_love_natalia тому свидетель, возникла при генерации antiquicksort-теста. В частности, из-за этого бага я сегодня схлопотал неудачный взлом. Я очень удивился, когда во время контеста программа жертвы, взломанная таким генератором, отработала быстро. Однако под конец контеста я поменял размер массива с 100000 на 99987, и взлом стал успешен!

А теперь коротко о том, что же происходит. Вот тест-кейс:

  1. Запустим генератор (чуть-чуть измененный, брать отсюда: http://pastebin.com/99RwHR6w) в запуске на сервере CF.

  2. Сервер выведет что-то наподобие Sorting ended in 1953ms

  3. Добавим в конец файла два перевода строки и запустим снова

  4. Результат поражает: Sorting ended in 0ms. Вот что переводы строки животворящие делают!

Можно поэкспериментировать с добавлением и удалением пробелов и переводов строк: результаты будут отличаться: либо сортировка проходит за нулевое время, либо за примерно 2 секунды.

Для любителей острых ощущений прилагается видео: ссылка (осторожно, в распакованном виде оно занимает около 600 МБ)

Что это такое? На моем домашнем компьютере такого не происходит. Баг Java-машины, или сервер CF пытается уберечь участников от взлома?

UPDATE: Судя по всему, на некоторых серверах стоит Java 7, на которой сортировка реализована по-другому и выполняется на этих тестах быстро. Это очень нехорошо, т.к. я выбираю язык Java 6, когда хочу послать задачу, а она, оказывается, может тестироваться под Java 7. Прошу всех принять это к сведению, пока администрация все не поправит.

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

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

То есть, получается, генератор генерирует разные последовательности в зависимости от наличия пробелов в исходнике?

  • »
    »
    12 лет назад, # ^ |
    Rev. 2   Проголосовать: нравится +4 Проголосовать: не нравится

    Последовательности одинаковые, а вот время сортировки почему-то разное.

    А вот и нифига, ждите breaking news от I_love_natalia!

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

    Последовательности на самом деле разные. Вот выдача

    System.out.println("hashCode = " + scrambled.hashCode());
    

    Первая выдача

    Scrambling ended in 2140ms with 11104pivots
    hashCode = 5442986
    Sorting ended in 1906ms
    Использовано: 4090 мс, 45228 КБ

    Вторая выдача

    Scrambling ended in 2703ms with 11104pivots
    hashCode = 9047389
    Sorting ended in 16ms
    Использовано: 2250 мс, 47088 КБ

    Поведение кода зависит от пробела в нем.

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

      Кстати, попробуйте вычислить хэш-код предварительно инициализированного массива.

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

Думаю, тут прикол в другом. На codeforces если код скомпилировался — то все его последующие запуски будут на одной и той же машинке. Добавляя пустые строки, вы заставляете его перекомпилироваться и сменить тем самым машинку. А тестирующие машинки разные — я на это уже разок напоролся (в "запуске на сервере" мне повезло с машинкой, а в систестах — нет). Видимо, java там тоже разная :)

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

    Читай выше, только что дописал про поведение.

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

      Ну это пока не опровергает мою гипотезу. Поведение может зависеть не от пробела в коде, а от версии джавы.

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

        Дело в том, что одинаковые (побитово) исходники всегда исполнялись за одинаковое время. Пробовали раз 5-6, наверное.

        Надо спросить у администрации: а одинаковая ли Java стоит на всех серверах?

        Кстати, думаю, кто-нибудь, у кого на компьютере много версий Java стоит, может ответить, а одинаковый ли в них во всех класс java.util.Arrays?

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

          В седьмой жабе уже идет Dual-Pivot Quicksort. Интереснее то, как реализована функция Array.hashCode(). Но исходников OpenJDK 7 на моем ноуте на данный момент нет.

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

            Что, серьезно что ли? Dual-Pivot Quicksort? Facepalm. А сделать, как у всех нормальных людей Introsort им религия не позволяет?

            Edit: вообще, я вспомнил, что я уже выражал свое мнение по этому поводу здесь. Был бы признателен, если бы кто-нибудь объяснил мне, в чем я был не прав тогда.

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

              Серьезно.

              В чем Вы были тогда неправы — сказать не могу, могу только посоветовать сравнить на аналогичных тестах сортировку из JDK 6 и JDK 7.

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

                Да, спасибо, я сразу пошел исходники изучать. Это я просто выражал свое искреннее недоумение этим решением.

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

          Ну наверняка там скомпилированные исходники source'ов закешированы, поэтому побитово одинаковые программы будут исполняться на одной и той же машинке.

          Сейчас проверил — там везде версия 1.6.0_29. Но при этом значения, выдаваемые вашей программой при добавлении пробелов, повторяются, и количество различных значений подозрительно похоже на количество тестирующих машин :)

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

            А, нет. Вот машинку с 1.6.0_30 нашёл :)

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

            Можно вызвать GetTickCount апишную? Или подобным образом идентифицировать машину?

            (GetTickCount от включения считается).

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

              Сходится идеально. Логи запусков в правке, первое число — System.nanoTime(). И да, 0 только на 7ой джаве.

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

                На одном из серверов стоит Java 1.7? Мне кажется, это очень нехорошо. По моему выполнению

                27972789935386 — OK 57875555302061 — OK 40964117868683 — FAIL 45682842921039 — OK

                В частности, ты не попал на еще один сервер.

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

                  Ну да, 40945109233456 — это 7ая джава.

                  Ну, "очень нехорошо" уже то, что машинки разные. Остаётся надеяться, что вконтакт проспонсирует на запупку одинаковых :)

                • »
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  12 лет назад, # ^ |
                  Rev. 2   Проголосовать: нравится +11 Проголосовать: не нравится

                  Я думаю, что ВКонтакте не разорится от приобретения 4 БЕСПЛАТНЫХ одинаковых версий JVM :)

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

                  А почему решили, что машины разные? Они абсолютно одинаковые.

                  Как Java 7 пробралась не знаю, но ее мы изничтожим :)

                  Прошу прощения у dalex, а Java 7 присоединяется.

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

                  Ну где-то полгода назад они точно были разные. По крайней мере, на одних мой код стабильно влезал в ТЛ, на других — стабильно не влезал. И во время контеста при тестировании на макстесте мне повезло с машинкой, а на систестах — нет. После контеста я поэкспериментировал с добавлением пробелов в программу и обнаружил, что разброс был ~100мс (не помню сейчас точно, но 50мс точно было).

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

                  А реджадж контеста будет? ;)

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

                  Предлагаешь реджаджить все посылки на яве за все время существования CF? Тысяч триста? ;)

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

                  Нет, реджаджа не будет, будет расправа над Java 7 :)

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

                  Взлом неудачный отмените человеку, ему же важен результат даже при внеконкурсном ;)

                  Кстати, похоже, "левых" Java-решений не прошло. Так что реджадж и правда не нужен.

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

                  А почему не сделать наоборот и не заменить Java 6 на Java 7?

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

                  Может быть стоит оставить Java 7 отдельным компилятором?

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

Я на жаве пишу не часто, на CF вроде вообще не писал. Но мне одному эта ситуация кажется бредовой? Что ломают библиотечную сортировку. Теперь вместо решения задач люди будут писать свои сортировки рандомно шафлить массивы перед сортировкой. Если жава 7 лишена такого недостатка, может как сказал SergeyLazarev стоит обновиться?

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

    ...люди будут писать свои сортировки...

    оужасже

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

    По-видимому, сортировка и в седьмой джаве ломается, там же тоже квиксорт заявлен. Речь не об этом: сейчас решение может проверяться не на том комплиляторе, что был выбран при отправке, и это надо исправить.

    Кстати, в последний год задач, где нужна была сортировка, почти не было. Вчера авторы навели интригу, написав все до начала раунда, и мы получили то, что есть.

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

      В Java 7 quicksort'ом сортируются только небольшие отрезки, которые потом merge'атся.

      • »
        »
        »
        »
        12 лет назад, # ^ |
        Rev. 2   Проголосовать: нравится +8 Проголосовать: не нравится

        Не совсем так. Все же в большинстве случаев в Arrays.sort для встроенных типов будет отрабатывать Dual-Pivot Quicksort. Mergesort там выполняется в довольно редких случаях почти отсортированных данных, если внимательно посмотреть на код. Видимо, это сделано для того, чтобы уменьшить количество вариантов вырождения в квадратичную сложность, даже не знаю.

        Как написано в комментариях "This algorithm offers O(n log(n)) performance on many data sets that cause other quicksorts to degrade to quadratic performance". Нигде ничего не написано про сложность O(n log(n)) в худшем случае.

        Исходный код для изучения Edit: Сорри забыл основную ссылку

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

    С одной стороны, да: может, несколько глупо.
    С другой: человек может выбрать инструмент + сам потрудиться, чтобы выполнить поставленную задачу. Для любых 10^5 чисел посортить их за 2с. И если он выбирает для этого Java, которая говорит: я посорчу в среднем за nlogn, то участник — ССЗБ

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

    А почему бы и не ломать библиотечные методы? Если участник на С++ напишет printf(s); я с удовольствием напишу ему тест !!!!!?!?!!???!!?!???!??? если такое допустимо входными данными. Или %d%u%s. Участник использует какой-то метод — участник берет всю ответственность на себя.

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

test

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

The victim would be me :D

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

May I ask in which version of Java is the problem actual? Java 7 or Java 6? Thanks

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

    There's no problem any more. The only problem was "when you submit your solution under Java 6 it can run under Java 7 instead".