Кто хочет стать колмогоровским победителем сложности?


22

Ваша миссия сегодня состоит в том, чтобы изобрести текстовый компрессор.

задача

Вы напишите две функции:

  • Пакера является функцией , которая принимает строку ASCII символов (U + 0000 до U + 007F) и выводит строку Unicode (U + 0000 до U + 10FFFF), содержащие наименьшее количество символов возможно.

  • Распаковщик это функция , которая принимает кодированные строки Unicode и выводит в точности исходной строки ASCII.

вход

Единственным разрешенным вводом является строка ASCII (для упаковщика) и упакованная строка Юникода (для распаковщика). Нет ввода пользователя, нет подключения к Интернету, нет использования файловой системы.

Ваши функции могут иметь доступ к этому списку английских слов . Вы можете использовать этот список как локальный текстовый файл или скопировать его содержимое в исходный код в виде строки или массива строк .

Вы не можете жестко закодировать фрагменты ниже в ваших функциях.

Выход

Единственным разрешенным выходом для обеих функций является строка.

Выходные данные распаковщика должны содержать те же символы, что и входные данные упаковщика.

Ваши входы и выходы могут использовать любую кодировку символов, поддерживающую весь Unicode (UTF-8/16/32, GB18030, ...), так как ваш результат будет зависеть только от количества символов Unicode в выходных данных. Пожалуйста, уточните, какую кодировку вы используете.

Чтобы подсчитать количество символов Unicode в вашем выводе, вы можете использовать этот инструмент: http://mothereff.in/byte-counter

счет

Ваша запись должна быть в состоянии упаковать и распаковать 10 следующих фрагментов текста (которые я взял на этом форуме).

Ваша оценка будет равна сумме размеров ваших 10 упакованных строк (в символах Юникода) + размер ваших двух функций (также в символах Юникода)

Не считайте размер словаря, если вы его используете.

Пожалуйста, включите в свои записи «оценку» каждого фрагмента и его упакованную версию.

Самый низкий балл побеждает.

Данные

Вот фрагменты для кодирования, чтобы вычислить ваш счет:

1: Тексты песен Рика Ролла (1870b): Мы не новички в программировании гольфа, вы знаете правила, и я тоже

Мы не новички в любви
Вы знаете правила и я тоже
Я всегда думаю о полной приверженности
Вы не получите это от любого другого парня
Я просто хочу рассказать вам, как я себя чувствую
Должен заставить тебя понять

Никогда тебя не брошу
Никогда не подведу
Никогда не побегу и не покину тебя
Никогда не заставлю тебя плакать
Никогда не прощаюсь
Никогда не скажу неправду и не сделаю тебе больно

Мы знали друг друга так долго
Твое сердце болит, но
Ты стесняешься сказать это
Внутри мы оба знаем, что происходит
Мы знаем игру и будем играть в нее
И если вы спросите меня, как я себя чувствую
Не говори мне, что ты слишком слеп, чтобы видеть

Никогда тебя не брошу
Никогда не подведу
Никогда не побегу и не покину тебя
Никогда не заставлю тебя плакать
Никогда не прощаюсь
Никогда не скажу неправду и не сделаю тебе больно

Никогда тебя не брошу
Никогда не подведу
Никогда не побегу и не покину тебя
Никогда не заставлю тебя плакать
Никогда не прощаюсь
Никогда не скажу неправду и не сделаю тебе больно

(Ох, сдавайся)
(Ох, сдавайся)
(Ох)
Никогда не дам, никогда не дам
(Откажусь)
(Ох)
Никогда не дам, никогда не дам
(Откажусь)

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

Я просто хочу рассказать вам, как я себя чувствую
Должен заставить тебя понять

Никогда тебя не брошу
Никогда не подведу
Никогда не побегу и не покину тебя
Никогда не заставлю тебя плакать
Никогда не прощаюсь
Никогда не скажу неправду и не сделаю тебе больно

Никогда тебя не брошу
Никогда не подведу
Никогда не побегу и не покину тебя
Никогда не заставлю тебя плакать
Никогда не прощаюсь
Никогда не скажу неправду и не сделаю тебе больно

Никогда тебя не брошу
Никогда не подведу
Никогда не побегу и не покину тебя
Никогда не заставлю тебя плакать
Никогда не прощаюсь
Никогда не скажу неправду и не сделаю тебе больно

2: Гольфист (412b): гольф ASCII-арт

      '\. , |> 18 >>
        \. '. |
       О >> 'о |
        \. |
        / \. |
       / /. ' |
 JGS ^^^^^^^ `^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^

3: номер ромба (233b): напечатать этот алмаз

        1
       121
      12321
     1234321
    123454321
   12345654321
  1234567654321
 123456787654321
12345678987654321
 123456787654321
  1234567654321
   12345654321
    123454321
     1234321
      12321
       121
        1

4: алфавит четыре раза (107b): печатать алфавит четыре раза

АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЫЭЮЯ
qwertyuiopasdfghjklzxcvbnm
pyfgcrlaoeuidhtnsqjkxbmwvz
zyxwvutsrqponmlkjihgfedcba

5: Текст старого Макдональда (203b): функция Старого Макдональда

У старого Макдональда была ферма, EIEIO,
И на той ферме у него была корова, EIEIO,
С Му Му здесь и Му Му там,
Вот му, там му, везде му му,
У старого Макдональда была ферма, EIEIO!

6: Рок круглосуточно (144b): Рок круглосуточно

1, 2, 3 часа, 4 часа рок,
5, 6, 7 часов, 8 часов рок,
9, 10, 11 часов, 12 часов рок,
Сегодня вечером мы будем качаться круглосуточно.

7: Hello World (296b): Скажите «Hello» миру в искусстве ASCII

 _ _ _ _ _ _ _
| | | | ___ | | | ___ __ _____ _ __ | | __ | | |
| | _ | | / _ \ | | / _ \ \ \ / \ / / _ \ | «__ | | / _` | |
| _ | __ / | | (_) | \ VV / (_) | | | | (_ | | _ |
| _ | | _ | \ ___ | _ | _ | \ ___ () \ _ / \ _ / \ ___ / | _ | | _ | \ __, _ (_)
                    | /

8: ирландское благословение (210b): старое ирландское благословение

Пусть дорога поднимется навстречу тебе
Пусть ветер всегда будет у тебя за спиной
Пусть солнце сияет на вашем лице
Дожди падают на твои поля
И пока мы не встретимся снова
Пусть Бог держит тебя в дупле Своей руки

9: Была лирика старой леди (1208b): Там была старая леди

Была пожилая женщина, которая проглотила муху.  
Я не знаю, почему она проглотила эту муху,  
Возможно, она умрет.

Была пожилая женщина, которая проглотила паука,  
Это извивалось, покачивалось и покачивалось внутри нее.  
Она проглотила паука, чтобы поймать муху,  
Я не знаю, почему она проглотила эту муху,  
Возможно, она умрет.

Была пожилая женщина, которая проглотила птицу,  
Как нелепо глотать птицу.  
Она проглотила птицу, чтобы поймать паука,  
Она проглотила паука, чтобы поймать муху,  
Я не знаю, почему она проглотила эту муху,  
Возможно, она умрет.

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

Была пожилая женщина, которая проглотила собаку,  
Какая свинья, чтобы проглотить собаку.  
Она проглотила собаку, чтобы поймать кошку,  
Она проглотила кошку, чтобы поймать птицу,  
Она проглотила птицу, чтобы поймать паука,  
Она проглотила паука, чтобы поймать муху,  
Я не знаю, почему она проглотила эту муху,  
Возможно, она умрет.

Была пожилая женщина, которая проглотила лошадь,  
Она умерла, конечно.

10: адрес Геттисберга (1452b): насколько случайным является адрес Геттисберга

Четыре десятка и семь лет назад наши отцы создали на этом континенте новую нацию, зачатую в свободе и посвященную утверждению, что все люди созданы равными. Сейчас мы вовлечены в великую гражданскую войну, проверяя, может ли эта нация или любая нация, столь задуманная и столь преданная, долго существовать. Мы встретились на великом поле битвы этой войны. Мы пришли, чтобы посвятить часть этой области как место последнего упокоения тем, кто здесь отдал свои жизни за то, чтобы этот народ мог жить. Это вполне уместно и правильно, что мы должны сделать это. Но, в более широком смысле, мы не можем посвятить, мы не можем посвятить, мы не можем освящать эту землю. Храбрые люди, живые и мертвые, которые боролись здесь, освятили это, намного выше нашей бедной силы добавлять или отвлекать. Мир мало заметит, и долго не вспомнит, что мы здесь говорим, но он никогда не забудет, что они здесь сделали. Скорее, мы живем, чтобы посвятить себя здесь незавершенной работе, которую они так велико продвинули до сих пор. Скорее, мы должны быть здесь преданы великой задаче, стоящей перед нами - что из этих почитаемых мертвецов мы берем повышенную преданность тому делу, ради которого они в последний раз проявили преданность, - что мы здесь решительно решаем, что эти мертвые не будут напрасно умерли - что этот народ под Богом получит новое рождение свободы - и что народное управление народами для народа не погибнет от земли.

Всего (без сжатия): 6135 символов / байт.

Повеселись!


7
Это не проблема, чтобы изобрести язык, это проблема, чтобы сжать что-то.
Джастин

2
Я думаю, что отсутствие учета размера компилятора / исполнителя (компрессора / декомпрессора) в счете делает эту задачу немного открытой. В какой-то момент грань между словарем и жестким кодированием станет очень тонкой.
Деннис

2
Черт, а вот я уже печатал private static final String RICK_ROLL_RETURN = "We're no strangers to love...
Теория графов

1
Я не думаю, что вы обратились к наблюдению Денниса.
Питер Тейлор

1
@xem Я думаю, что пост можно улучшить, организовав информацию в такие разделы, как #Task, #Input, #Output, #Scoring. Я также считаю, что размер исходного кода для компрессора и декомпрессора должен быть включен в оценку. Это ничего не ранит, но решает проблему, на которую указал Деннис.
Рейнболт

Ответы:


6

Хаскелл - 5322 балла

Байт кода: 686

Первоначальный размер : 6147 = 1871+415+234+108+204+145+297+211+1209+1453

Закодированный размер: 4636 = 1396+233+163+92+153+115+197+164+979+1144

Гол : 686+ 4636

Сжатие количества символов: ~25%

Код

Помимо оптимизаций, это сохраняет значения между 0и 7fв символах Юникода как их главные факторы.

Это не уменьшает количество байтов кодированного вывода, оно только уменьшает количество символов Unicode. Например, тест № 4 содержит 108символы и закодированный вывод 92. Их соответствующие размеры, однако, 108и 364байты.

import Data.Bits
import Data.List
import Data.Numbers.Primes
import qualified Data.Text as T
a=nub$concat$map(primeFactors)[0..127]
d(a:r)c=let s=shift c 5in if s<=0x10ffffthen d r(s+a)else c:d r a
d[]c=[c]
f(a:r)=let o=a.&.0x1fin(if o/=a then f((shiftR a 5):r)else f r)++[o]
f[]=[]
g=T.pack.map(toEnum).(\(a:r)->d r a).concatMap j.map(map(\x->head$elemIndices x a)).map(primeFactors.fromEnum).T.unpack
h=T.pack.map(toEnum.product.map((!!)a)).i.f.reverse.map(fromEnum).T.unpack
i(a:r)=let z=a`clearBit`4;x=if a`testBit`4then(take z$repeat$head r,tail r)else splitAt z r in[fst x]++i(snd x)
i[]=[]
j x@(a:_)=let l=length x in if(take l(repeat a))==x then[l`setBit`4,a]else[l]++x
j[]=[0]

Как это работает

  • кодирование

    1. Каждый символ преобразуется в его числовой эквивалент, давайте назовем этот номер n.
    2. nзатем преобразуется в список его основных факторов ps.
      • Удобно, что числа от 0 до 127 имеют 32 общих простых множителя, исключая 1. Это означает, что они, факторы, могут храниться в виде индексов всего в 5 битах.
      • 1 это особый случай и представлен пустым списком.
    3. С psкодировки теперь можно начинать.
      1. Каждое число psпреобразуется в его индекс в списке из 32 уникальных факторов (в приведенном выше коде этот список обозначен как a).
      2. (Имейте в виду, что в данный момент мы имеем дело со списком списков индексов простых факторов). Чтобы перейти к следующему шагу, psнеобходимо сплющить. Для сохранения целостности данных каждый список преобразуется в другой список из двух частей.
        1. Первый элемент хранит его длину и, если он состоит из того же фактора.
          • В каждом списке не более 6 основных факторов, эта информация хранится в самых правых 3 битах. Пятый бит используется в качестве флага, чтобы указать, состоит ли список из одного фактора.
        2. Остальные элементы - это сами индексы или один индекс, если в списке меньше двух разных факторов.
      3. Эти списки затем объединяются в один плоский список fs.
    4. Затем элементы fsмогут быть упакованы в символы Юникода, используя сдвиг битов.
  • Декодирование

    • Выполните шаги кодирования в обратном порядке.
    • Если вам интересно, как 1вписывается в это, я хотел бы напомнить вам об этом product [] == 1.

тесты

Использование этого интерфейса для тестирования было бы болезненным, поэтому я использовал эту функцию, чтобы получить результаты ниже.

edTest f = do
    t <- readFile f
    let txt = T.pack t
        enc = g txt
        dec = h enc
        tst = txt == dec
    putStrLn $ (show $ T.length txt) ++ "," ++ (show $ T.length enc) ++ "," ++ (show $ T.length dec)++","++(show tst)
    putStrLn $ if not tst then T.unpack txt ++ "\n---NEXT---\n" ++ T.unpack dec else ""


λ> edTest "1"
1871,1396,1871,True

λ> edTest "2"
412,233,412,True

λ> edTest "3"
234,163,234,True

λ> edTest "4"
108,92,108,True

λ> edTest "5"
204,153,204,True

λ> edTest "6"
145,115,145,True

λ> edTest "7"
297,197,297,True

λ> edTest "8"
211,164,211,True

λ> edTest "9"
1209,979,1209,True

λ> edTest "10"
1453,1144,1453,True

Образец

Вывод функции кодирования gдля теста № 4 такой
"\99429\582753\135266\70785\35953\855074\247652\1082563\68738\49724\164898\68157\99429\67973\1082404\587873\73795\298017\330818\198705\69861\1082435\595009\607426\36414\69873\855074\265249\346275\67779\68738\77985\1082513\821353\132131\101410\247652\1082562\49724\164898\67649\594977\34915\67746\50273\135265\103997\563265\103457\1086021\99399\584802\70753\73889\34882\582722\411459\67779\68740\1084516\1082563\1091681\103491\313282\49724\164897\68705\135741\69858\50241\607426\35905\608421\1082435\69858\50274\71777\43075\298018\280517\1082404\67971\36017\955425\67665\919600\100452\132129\214883\35057\856097\101474\70753\135737"
или, если вы адепт тарабарщины, это
𘑥򎑡𡁢𑒁豱󐰢𼝤􈓃𐲂숼𨐢𐨽𘑥𐦅􈐤򏡡𒁃񈰡񐱂𰠱𑃥􈑃򑑁򔓂踾𑃱󐰢񀰡񔢣𐣃𐲂𓂡􈒑󈡩𠐣𘰢𼝤􈓂숼𨐢𐡁򑐡衣𐢢쑡𡁡𙘽򉡁𙐡􉉅𘑇򎱢𑑡𒂡衂򎑂񤝃𐣃𐲄􈱤􈓃􊡡𙑃񌟂숼𨐡𐱡𡈽𑃢쑁򔓂豁򔢥􈑃𑃢쑢𑡡ꡃ񈰢񄟅􈐤𐦃貱󩐡𐡑󠠰𘡤𠐡𴝣裱󑀡𘱢𑑡𡈹

Дополнительные заметки

  • Используя http://mothereff.in/byte-counter , списки каталогов и edTestразмер тестов согласованы, но все же отличаются от указанного размера в вопросе.
  • Тест # 10 содержит пару EM дефиса ( ) , что я заменен , -так как они находятся вне 0- 7fдиапазона.
  • Дальнейшее сжатие может быть достигнуто с использованием оставшегося четвертого бита при выравнивании, например, в 00базовом случае, 01повторить все, 10повторить, кроме последнего, 11повторить, кроме двух последних.
  • Тестовые файлы и код доступны здесь https://github.com/gxtaillon/codegolf/tree/master/Kolmogorov

Здравствуйте, спасибо за этот ответ! :) Я не понял, что происходит в двоичном формате, когда вы конвертируете abcdefghijklm...в 𘑥򎑡𡁢𑒁豱󐰢𼝤..., не могли бы вы объяснить это немного подробнее, пожалуйста? Кроме того, я исправил количество символов и преобразовал тире # в вопросе 10. Мои чары все еще отличаются от ваших. Не знаю почему, я использовал инструмент mothereff.in.
xem

@xem Сложные детали были раскрыты.
gxtaillon

Мое сознание (образно) буквально взорвано идеей, что числа 0 и 2-127 могут быть закодированы в 5 битах. Вы нашли это сами или это было что-то известное? Дополнительный вопрос: сколько бит нужно хранить только для печати символов ascii, т.е. 95 различных символов?
xem

@xem Числа не закодированы в 5 бит, каждый из их факторов. Я был бы очень рад, если бы нашел способ кодирования 7 бит только на 5. Что касается символов ascii, то при использовании этого метода им все равно потребуется 5 бит каждый.
gxtaillon

1
Поскольку в указанном диапазоне не более 6 факторов на число, длина использует 3 из 5-битового «блока». Тогда индексы кодируются в 5 битах, да. В этой реализации один из 2 неиспользуемых битов в блоке длины используется для получения дополнительного сжатия.
gxtaillon

4

C ++ (C ++ 11), 2741 баллов

Этот ответ использует UTF-32 в качестве кодировки для сжатого текста.

#include <cstdio>
#include <iostream>
#include <locale>
#include <string>
#define L locale
using namespace std;long b,n,i,l;void c(){string d;char x;while((x=cin.get())!=EOF)d+=x;b=(d.size()*7)%20;n=5;wcout.imbue(L());for(char y:d){b=b<<7|y&127;n+=7;if(n>=20)wcout.put(b>>(n-=20)&0xFFFFF);}if(n)wcout.put(b<<20-n&0xFFFFF);}void d(){wstring d;wchar_t w;wcin.imbue(L());while((w=wcin.get())!=EOF)d+=w;l=-1;for(wchar_t y:d){b=b<<20|y;n+=20;if(l<0)l=b>>15&31,n-=5;while(n>=7&(i<d.size()-1|n>20-l))cout.put(b>>(n-=7)&127);++i;}}int main(int t,char**a){L::global(L("en_US.utf8"));**++a<'d'?c():d();}

Подсчет и подсчет символов

Код: 593 символа (завершающий символ новой строки убран)

Сжатые тексты (символы Юникода) : 654 + 145 + 82 + 38 + 51 + 104 + 73 + 423 + 506 = 2148 (считается wc -mдля количества символов Юникода, а не байтов, число байтов, как и в ответе @ gxtaillon) , выше, чем оригиналы, всего 8413 байт, как считается wc -c).

Коэффициент сжатия (ASCII к Unicode) : 35,01% (используя 6135 байт из вопроса (так же, как wc -c))

Осторожно:

Многие оболочки не могут обрабатывать символы Юникода, которые производит эта программа. Таким образом, распаковка может привести к тому, что текст будет где-то обрезан, когда оболочка не сможет обработать символ, так как ввод осуществляется через stdinоболочку.

составление

Он должен компилироваться с clang++и g++ -std=c++11, но он покажет некоторые предупреждения о приоритете операторов, так как выражение like b<<20-n&0xFFFFFбудет восприниматься не так ((b << 20) - n) & 0xFFFFF, как можно было бы ожидать, а скорее как (b << (20 - n)) & 0xFFFFF.

использование

  • Скомпилируйте программу в исполняемый файл, например ./compress.
  • Запустите программу как ./compress cдля сжатия, так и ./compress dдля распаковки. (Осторожно, опуская опцию, вы получите SEGFAULT (проверка ошибок настолько символична ...), а другие опции (например, использование Dвместо d) могут дать неожиданные результаты.
  • Вход считывается, stdinа вывод записывается вstdout

Как это работает

Ungolfed

#include <cstdio>
#include <iostream>
#include <locale>
#include <string>

using namespace std;

long b, n, i, l;

// Compress
void c() {
    string d;
    char x;
    // Read from STDIN until EOF
    while((x = cin.get()) != EOF)
        d += x;
    // Calculate the number of bits used from the last unicode character
    // (maximum 19) and store it into the first 5 bits
    b = (d.size() * 7) % 20;
    n = 5;
    // Set the output locale to allow unicode
    wcout.imbue(locale());
    // For each character in the input...
    for (char y : d) {
        // Add its bit representation (7 bits) to the right of the buffer
        // by shifting the buffer left and ORing with the character code
        b = (b << 7) | (y & 127);
        // Add 7 to the bit counter
        n += 7;
        // If enough data is present (20 bits per output character),
        // calculate the output character by shifting the buffer right,
        // so that the last 20 bits are the left 20 bits of the buffer.
        // Also decrement the bit counter by 20, as 20 bits are removed.
        if (n >= 20)
            wcout.put((b >> (n -= 20)) & 0xFFFFF);
    }
    // If there are still bits in the buffer, write them to the front of
    // another unicode character
    if (n)
        wcout.put((b << (20 - n)) & 0xFFFFF);
}

// Decompress
void d() {
    wstring d;
    wchar_t w;
    // Set STDIN to UNICODE
    wcin.imbue(locale());
    // Read wide characters from STDIN (std::wcin) until EOF
    while ((w = wcin.get()) != EOF)
        d += w;
    // `l' represents the number of bits used in the last unicode character.
    // It will be set later
    l = -1;
    // For each input character...
    for (wchar_t y : d) {
        // Add it to the buffer and add 20 to the bit counter
        b = (b << 20) | y;
        n += 20;
        // If the number of bits in the last unicode character has not been
        // set yet, read the first 5 buffer bits into `l'. This is
        // necessary because the last character may contain more than 7
        // (one entire uncompressed character) unused bits which may
        // otherwise be interpreted as garbage.
        if (l < 0) {
            l = (b >> 15) & 31;
            n -= 5;
        }
        // As long as there is data to turn into output characters
        // (at least 7 bits in the buffer and either not the last
        // unicode character or before the unused bits)
        while (n >= 7 && ((i < d.size() - 1) || (n > (20 - l)))
            cout.put((b >> (n -= 7)) & 127); // Output the left 7 bits in the buffer as an ASCII character
        ++i; // Increment the character index, so that we know when we reach the last input character
    }
}
int main(int t, char**a) {
    // Set the default locale to en_US.utf8 (with unicode)
    locale::global(locale("en_US.utf8"));
    // Decide whether to compress or decompress.
    // This is just fancy pointer stuff for a[1][0] < 'd' ? c() : d()
    (**(++a) < 'd') ? c() : d();
}

объяснение

Поскольку все символы Юникода от U+0000до U+10FFFFразрешены, мы можем использовать 20 бит на символ юникода: U+FFFFFиспользует 20 бит и все еще включен в допустимый диапазон. Таким образом, мы просто пытаемся втиснуть все отдельные биты ASCII-символов в символы Unicode, чтобы сохранить несколько символов ASCII в одном символе Unicode. Однако нам также необходимо сохранить количество битов, использованных в последнем символе Юникода, поскольку в противном случае неиспользуемые биты мусора могут быть интерпретированы как надлежащие сжатые символы ASCII. Поскольку максимальное количество используемых битов в последнем символе юникода равно 20, нам потребуется 5 битов, которые помещаются в начало сжатых данных.

Пример вывода

Это вывод для примера # 4 (как указано less):

<U+4E1C5><U+8F265><U+CD9F4><U+69D5A><U+F66DD><U+DBF87><U+1E5CF><U+A75ED>
<U+DFC79><U+F42B8><U+F7CBC><U+BA79E><U+BA77F>쏏𦛏<U+A356B><U+D9EBC><U+63ED8>
<U+B76D1><U+5C3CE><U+6CF8F><U+96CC3><U+BF2F5><U+D3934><U+74DDC><U+F8EAD>
<U+7E316><U+DEFDB><U+D0AF5><U+E7C77><U+EDD7A><U+73E5C><U+786FD><U+DB766>
<U+BD5A7><U+467CD><U+97263><U+C5840>

( 쏏𦛏укажите в <U+C3CF><U+266CF>качестве кодов символов, но я мог ошибиться)


2

Python 3, 289 + 818 = 1107 баллов

Только слегка в гольф.

import zlib as Z
def p(s):
 z=int.from_bytes(Z.compress(s),'big');o=''
 while z:
  z,d=divmod(z,1<<20)
  if d>0xd000:d+=1<<16
  o+=chr(d)
 return o[::-1]
def u(s):
 i=0
 for c in s:
  d=ord(c)
  if d>0xe000:d-=1<<16
  i=(i<<20)+d
 return Z.decompress(i.to_bytes(i.bit_length()//8+1,'big'))

Общий размер кода составляет 289 байт и кодирует данные 6135 байт в 818 символов Unicode - общее количество выходных байтов составляет 3201 байт, что значительно меньше исходных входных данных.

Кодирует, используя zlib, затем вторично, используя кодировку Unicode. Некоторая дополнительная логика была необходима, чтобы избежать суррогатов (что Python действительно ненавидит).

Пример вывода из # 4, как видно из less(37 символов Unicode):

x<U+AC0DC><U+BB701><U+D0200><U+D00B0><U+AD2F4><U+EEFC5>𤆺<U+F4F34>멍<U+3C63A><U+2F62C><U+BA5B6><U+4E70A><U+F7D88><U+FF138><U+40CAE>
<U+CB43E><U+C30F5><U+6FFEF>𥠝<U+698BE><U+9D73A><U+95199><U+BD941><U+10B55E><U+88889><U+75A1F><U+4C4BB><U+5C67A><U+1089A3><U+C75A7>
<U+38AC1><U+4B6BB><U+592F0>ᚋ<U+F2C9B>

Программа драйвера для тестирования:

if __name__ == '__main__':
    import os
    total = 0
    for i in range(1,10+1):
        out = p(open('data/%d.txt'%i,'rb').read())
        total += len(out)
        open('out/%d.bin'%i,'w',encoding='utf8').write(out)
    print(total)
    for i in range(1,10+1):
        out = u(open('out/%d.bin'%i,'r',encoding='utf8').read())
        open('data2/%d.txt'%i,'wb').write(out)

Количество выходных байтов:

 607 out/1.bin
 128 out/2.bin
 101 out/3.bin
 143 out/4.bin
 177 out/5.bin
 145 out/6.bin
 186 out/7.bin
 222 out/8.bin
 389 out/9.bin
1103 out/10.bin
3201 total

1
Разве это не тот факт, что библиотека сжатия немного обманывает?
Beta Decay

@BetaDecay: Это не ограничивает это в вопросе, поэтому я решил, что это была честная игра.
nneonneo

Также вам нужно включить декомпрессор.
бета-распад

@BetaDecay: pупаковщик, uраспаковщик.
nneonneo

1

Python 2 - 1141 балл

from zlib import *;v=256
def e(b):
 x=0
 for c in compress(b,9):x=(x*v)+ord(c)
 b=bin(x)[2:]
 return "".join(unichr(int("1"+b[a:a+19],2))for a in range(0,len(b),19))
def d(s):
 y=int("".join(bin(ord(a))[3:]for a in s),2);x=""
 while y:y,d=(y/v,chr(y%v));x=d+x
 return decompress(x)

Размер кода составляет 281байты, и он кодирует 6135байты в 860символы Юникода.

Как это работает:

Кодировать:

  1. Сожмите строку для кодирования.
  2. Интерпретировать сжатую строку как базовое число 256.
  3. Преобразуйте число в двоичное.
  4. Разбить двоичный файл на группы 19битов, добавить 1бит в начало каждого из них, а затем преобразовать в символы Юникода.

Декодирование обратное.

Обратите внимание, что некоторые версии Python могут обрабатывать только символы Unicode до 0xFFFF, и, таким образом, этот код будет вызывать a ValueError.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.