Игра в Code Golf: сжатие кода и его сабмит на конкурс платформы AtCoder

RubyЗанимательные задачки
Автор оригинала: Fish Maguro
Привет, Хабр! Представляю вашему вниманию перевод статьи "【コードゴルフ】コードをDeflate圧縮してAtCoderに提出する【圧縮ゴルフ】".

Вы когда-нибудь слышали о Code Golf? Это что-то вроде игры, где все стараются написать определенный код максимально маленьким количеством символов.

Одно из решений (171-байтный код), засабмиченных в контест на AtCoder*, подвергается широкой критике, поэтому я решил разобраться, в чем же там проблема.

(Прим. переводчика: AtCoder — платформа, где проводятся различные соревнования среди разработчиков. Судя по домену .jp, платформа — японская, но пользователи там со всего мира. Например, на момент перевода этой статьи в топе сайта есть 3 пользователя из России.)

Сжатие кода


Как я понимаю, сжатие кода (=уменьшение его размера-веса) сокращает его символьно. Участники Code Golf, можно сказать, душу вкладывают в сокращение каждого символа, каждого байта. А так как цель такого соревнования — написать максимально короткий код, нет причин его не сжимать.

Сначала взгляните на следующий код.

#!ruby -Knrzlib
eval Zlib.inflate'x�=�Q
� D��OS�c

]r� �����ݳ�By�
              ����O{4�.��i�aQ(`}cB���I2e�ߣ��IeT�јL>������)u,�p�S�W��H~.�,�:
z:Ӊ��g�O7ʲ��vQ�1h�^<����=&�\u7'

Я с трудом могу это прочитать. Но по первой строчке я понимаю, что код написан на Ruby.

#!ruby -Knrzlib

Это шебанг, и в качестве опции командной строки указано -Kn и -rzlib.

-Kn указывает, что написанный код нужно рассматривать в качестве двоичного, не содержащего символы код. Например, #coding: binary выполняет те же функции.

-rzlib — требование библиотеки zlib. Сокращение от require «zlib».

eval Zlib.inflate'…

Следующая строка.

Zlib.inflate — это метод распаковки сжатого кода. Так как видно, что после символа ' еще есть строка, мы понимаем, что эта часть кода распаковывает сжатый код и применяет к нему eval.

Попробую сам


Я подумал, что было бы неплохо создать шаблон по сжатию кода.

Для этого необходимо выполнить три шага: 1) написать код, 2) сжать код и 3) выдать конечный код. В свою очередь, чтобы уменьшить степень сжатия, нужно повторять шаги 1 и 2.

Пишем код


Сначала напишем код. (Ну, этот шаг особых проблем не сулит)

puts [6484,a=?+,0,1,3,?<,2,3,3]+[0,1].map{|x|[a,x,3,x]+(0..29).map{v=x+4;u=x*60+9+_1;[a,v,v,v,a,v,3,6,*[a,6,6,6]*(29-_1),?<,6,x,u,a,v,u,v]}}+(0..59).map{|t|[a,2,2,2]+(0...t).map{[a,8+t-_1,69+_1,5,?<,3,5,6,a,2,6,2]}}

Длина этого кода составляет 216 байт.

Теперь попробуем сжать.

Сжимаем


С использованием этого скрипта мне удалось уменьшить его до 194 байтов!

$ ruby deflate.rb agc047_e.rb > agc047_e.min.rb
194 B

Сабмитим на AtCoder


Код я сжал и хотел уже отправить его, но возникла одна проблема.

К сожалению, я не могу просто скопировать и вставить этот код и отправить его как есть. Код, сгенерированный сжатием, является двоичным. Однако экран отправки AtCoder — UTF-8. В большинстве случаев сжатый код содержит байтовые строки, которые недопустимы в UTF-8, поэтому, если вы скопируете и вставите его как есть, он будет искажен.

Поэтому я отправлю код в кодировке URI напрямую с помощью DevTools.

Откроем экран отправки и запустим DevTools. Страницу для отправки решения на контест держим открытой.



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

Выбираем запрос под названием “submit” и кликаем по нему правой кнопкой мыши, нажимаем Copy, затем Copy as fetch.



Открываем консоль и вставьте только что скопированный код.



Вставляем после sourceCode= наш код в кодировке URI (не показано на скриншоте). Для энкодинга в URI используем этот скрипт. (Сохраняем как deflate-uriencode.rb)

$ ruby deflate-uriencode.rb agc047_e.rb
194 B
%23%21ruby+-Knrzlib%0Aeval+Zlib.inflate%27x%DA-%8DQ%0A%830%10D%AF%D2Ou%B7A%13%5D%14%2B%1E%24%04%C9%01%0AB%13%094%B9%7Bwc%99%8F%81%99%E1%CD%19%C3%E7ai%9CG%F4%DB%0E%D8%E3%80%06%F7%17j6%E3%C0r%E0%D4%DB%9F%DF%9C%B2%F5%988N%0E%9A%5E%29%BD%B4%B5%B8%B6%04%E3%1A%B7%D4Q%0F%0B%1C%C3%CA%BB%ABJ%DC+a%C7%09%89%5C%D7%E8%E5y%0C%AD%5C%10%D3b%DDD%BC%5C%29%95%3A%FD%A99%C8%9D%16%DDw*%DC%05%A73%04f+%C9%19N%822l%84%B2%DE%97%F2%03%93%919%B0%DE%97%F2%03%93%919%B0%27

Преобразовываем agc047_e.rb в deflate-uriencode.rb.

Из второй строчки вывода копируем все, что находится после %23, и вставлем после вышеупомянутого sourceCode=.



Теперь можем отправлять наше решение.

Изменяем код (делаем его еще короче)


Сократим код. Есть два способа сократить код после сжатия.

  • Сократить исходный код
  • Увеличить степень сжатия

Попробую использовать оба метода. Сокращение исходного кода — это любимый способ участников Code Golf.

Увеличиваем степень сжатия


Как я могу увеличить степень сжатия? Теперь мы используем сжатие Deflate, которое представляет собой комбинацию сжатия длин серий и кодирования Хаффмана. Обратите внимание на этот код Хаффмана. Код Хаффмана отличается тем, что степень сжатия увеличивается по мере уменьшения энтропии до сжатия. Энтропия уменьшается по мере смещения вероятностей появления кода. Следовательно, если вероятность появления кодов смещена, степень сжатия будет увеличиваться по мере смещения.

Эффективный способ уменьшить вероятность появления кода — уменьшить тип символа. Для этого можно переименовать переменную.

В первом коде давайте переименуем переменные x и v в t и p. Затем, поскольку он помещается с именем функции puts или map, тип символа можно уменьшить.

puts [6484,a=?+,0,1,3,?<,2,3,3]+[0,1].map{|t|[a,t,3,t]+(0..29).map{p=t+4;u=t*60+9+_1;[a,p,p,p,a,p,3,6,*[a,6,6,6]*(29-_1),?<,6,t,u,a,p,u,p]}}+(0..59).map{|t|[a,2,2,2]+(0...t).map{[a,8+t-_1,69+_1,5,?<,3,5,6,a,2,6,2]}}

$ ruby deflate.rb agc047_e.rb > agc047_e.min.rb
275 B

Хм, он увеличился.

Уберем p и заменим его на s.

puts [6484,a=?+,0,1,3,?<,2,3,3]+[0,1].map{|t|[a,t,3,t]+(0..29).map{s=t+4;u=t*60+9+_1;[a,s,s,s,a,s,3,6,*[a,6,6,6]*(29-_1),?<,6,t,u,a,s,u,s]}}+(0..59).map{|t|[a,2,2,2]+(0...t).map{[a,8+t-_1,69+_1,5,?<,3,5,6,a,2,6,2]}}

$ ruby deflate.rb agc047_e.rb > agc047_e.min.rb
184 B

На этот раз он правильно уменьшается.

(Я не знаю, почему он увеличился, поэтому, пожалуйста, если есть люди знающие, расскажите).
Таким образом, через метод проб и ошибок, мы смогли сократить код.

Ссылка на оригинал данной статьи тут

Мы будем очень рады, если вы расскажете нам, понравилась ли вам данная статья, понятен ли перевод, была ли она вам полезна?
Теги:rubybashcode golfdevtools
Хабы: Ruby Занимательные задачки
+6
1k 10
Комментарии 3

Похожие публикации

Digital-маркетолог
21 января 202175 588 ₽GeekBrains
Data Scientist
21 января 2021126 000 ₽Нетология
Графический дизайнер
22 января 202121 600 ₽GeekBrains
Python-разработчик с нуля
22 января 202169 900 ₽Нетология
Fullstack-разработчик на JavaScript
22 января 202183 200 ₽SkillFactory

Лучшие публикации за сутки