Kolmogorov의 복잡성 승자가되고 싶은 사람은 누구입니까?


22

오늘 당신의 임무는 텍스트 압축기를 발명하는 것입니다.

태스크

두 가지 함수를 작성합니다.

  • 패커 ASCII 문자 (U + 007F에 U + 0000)의 문자열을 수락 가능한 최소의 문자를 포함하는, 유니 코드 문자열 (U + 10FFFF에 U + 0000)를 출력하는 함수이다.

  • 해석기는 인코딩 된 유니 코드 문자열을 받아 정확하게 원래 ASCII 문자열을 출력하는 함수이다.

입력

승인 된 유일한 입력은 ASCII 문자열 (패커 용)과 압축 유니 코드 문자열 (포장 풀기 용)입니다. 사용자 입력 없음, 인터넷 연결 없음, 파일 시스템 사용 없음.

함수는이 영어 단어 목록에 액세스 할 수 있습니다 . 이 목록을 로컬 txt 파일로 사용하거나 소스 코드의 내용을 문자열 또는 문자열 배열 로 복사 할 수 있습니다 .

함수에서 아래 스 니펫을 하드 코딩 할 수 없습니다.

산출

두 기능 모두에 대해 승인 된 유일한 출력은 문자열입니다.

언 패커의 출력은 패커의 입력과 정확히 동일한 문자를 포함해야합니다.

입력 및 출력은 모든 유니 코드 (UTF-8 / 16 / 32, GB18030, ...)를 지원하는 모든 문자 인코딩을 사용할 수 있습니다. 점수는 출력의 유니 코드 문자 수에만 의존하기 때문입니다. 그래도 어떤 인코딩을 사용하고 있는지 확인하십시오.

출력에서 유니 코드 문자 수를 계산하려면 다음 도구를 사용할 수 있습니다. http://mothereff.in/byte-counter

채점

출품작은 10 개의 다음 텍스트 스 니펫 (이 포럼에서 취한)을 포장하고 풀 수 있어야합니다.

점수는 10 묶음 문자열의 크기 (유니 코드 문자) + 두 함수의 크기 (유니 코드 문자)의 합입니다.

사전을 사용하는 경우 크기를 세지 마십시오.

각 스 니펫의 "점수"와 압축 버전을 입력하십시오.

최저 점수가 이깁니다.

데이터

점수를 계산하기 위해 인코딩 할 스 니펫은 다음과 같습니다.

1 : Rick Roll의 가사 (1870b) : 우리는 골프를 코딩하는 데 익숙 하지 않습니다. 규칙을 알고 있습니다.

우린 사랑할 사람이 없어
당신은 규칙을 알고 나도
전념은 제가 생각하는 것입니다
당신은 다른 사람에게서 이것을 얻지 못할 것입니다
기분이 어떤지 말해주고 싶어
이해하게 해줘

절대 포기하지 않을거야
절대 실망시키지 않을거야
절대 돌아 다니지 않고 당신을 버리지 않을 것입니다
울지 않을거야
작별 인사를하지 않을거야
거짓말을하지 말고 다치게 하지마

우리는 오랫동안 서로를 알고 있었다
당신의 마음은 아프지 만
넌 너무 부끄러워
안에 우리 둘 다 무슨 일이 있었는지 알고
우리는 게임을 알고 있고 그것을 할 것입니다
기분이 어떤지 물어 보면
네가 너무 눈이 멀다고 말하지 마

절대 포기하지 않을거야
절대 실망시키지 않을거야
절대 돌아 다니지 않고 당신을 버리지 않을 것입니다
울지 않을거야
작별 인사를하지 않을거야
거짓말을하지 말고 다치게 하지마

절대 포기하지 않을거야
절대 실망시키지 않을거야
절대 돌아 다니지 않고 당신을 버리지 않을 것입니다
울지 않을거야
작별 인사를하지 않을거야
거짓말을하지 말고 다치게 하지마

(오, 포기 해)
(오, 포기 해)
(오)
절대 포기하지 않을거야
(너를 포기하다)
(오)
절대 포기하지 않을거야
(너를 포기하다)

우리는 오랫동안 서로를 알고
당신의 마음은 아프지 만
넌 너무 부끄러워
안에 우리 둘 다 무슨 일이 있었는지 알고
우리는 게임을 알고 있고 그것을 할 것입니다

기분이 어떤지 말해주고 싶어
이해하게 해줘

절대 포기하지 않을거야
절대 실망시키지 않을거야
절대 돌아 다니지 않고 당신을 버리지 않을 것입니다
울지 않을거야
작별 인사를하지 않을거야
거짓말을하지 말고 다치게 하지마

절대 포기하지 않을거야
절대 실망시키지 않을거야
절대 돌아 다니지 않고 당신을 버리지 않을 것입니다
울지 않을거야
작별 인사를하지 않을거야
거짓말을하지 말고 다치게 하지마

절대 포기하지 않을거야
절대 실망시키지 않을거야
절대 돌아 다니지 않고 당신을 버리지 않을 것입니다
울지 않을거야
작별 인사를하지 않을거야
거짓말을하지 말고 다치게 하지마

2 : 골퍼 (412b) : 골프 ASCII 예술

      '\. . |> 18 >>
        \. '. |
       O >>. 'O |
        \. |
        / \. |
       / /. ' |
 jgs ^^^^^^^`^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^

3 : 숫자 다이아몬드 (233b) : 이 다이아몬드 인쇄

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

4 : 알파벳 4 번 (107b) : 알파벳을 4 번 인쇄합니다

abcdefghijklmnopqrstuvwxyz
qwertyuiopasdfghjklzxcvbnm
pyfgcrlaoeuidhtnsqjkxbmwvz
zyxwvutsrqponmlkjihgfedcba

5 : Old McDonald의 가사 (203b) : Old MacDonald 기능

올드 맥도날드는 농장, EIEIO,
그 농장에서 그는 소, EIEIO,
여기는 moo moo가 있고 거기에는 moo moo가 있습니다.
여기 moo, moo, 어디서나 moo moo,
오래된 맥도날드는 농장, EIEIO!

6 : Rock around the clock의 가사 (144b) : Rock Around the Clock

1시, 2시, 3시, 4시,
5시, 6시, 7시, 8시,
9시, 10시, 11시, 12시,
우리는 오늘 밤 24 시간 내내 뛸거야

7 : Hello World (296b) : ASCII 예술로 세상에 "Hello"라고 말하십시오 .

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

8 : 아일랜드의 축복 (210b) : 구 아일랜드의 축복

당신을 만나기 위해 길을 올라가 길 바랍니다
바람이 항상 당신의 뒤에 있기를 바랍니다
태양이 당신의 얼굴에 따뜻해 지길 바랍니다
들판에 비가 내립니다
우리가 다시 만날 때까지
하나님 께서 당신의 손에 움켜 쥐시기를 바랍니다.

9 : 노파 가사 가 있었다 (1208b) : 노부부 가 있었다

파리를 삼킨 노파가있었습니다.  
그녀가 왜 그 파리를 삼 켰는 지 모르겠어요  
아마도 그녀는 죽을 것이다.

거미를 삼킨 노부가 있었어요  
그것은 그녀의 내부에서 흔들리며 울부 짖으며 울렸다.  
그녀는 거미를 삼켜 파리를 잡았습니다.  
그녀가 왜 그 파리를 삼 켰는 지 모르겠어요  
아마도 그녀는 죽을 것이다.

새를 삼킨 노부가 있었어요  
새를 삼키는 것이 어리석은 일입니다.  
그녀는 거미를 잡기 위해 새를 삼켰습니다.  
그녀는 거미를 삼켜 파리를 잡았습니다.  
그녀가 왜 그 파리를 삼 켰는 지 모르겠어요  
아마도 그녀는 죽을 것이다.

고양이를 삼킨 노부가 있었어요  
고양이를 삼킨다 고 상상해보십시오.  
그녀는 새를 잡기 위해 고양이를 삼 켰고  
그녀는 거미를 잡기 위해 새를 삼켰습니다.  
그녀는 거미를 삼켜 파리를 잡았습니다.  
그녀가 왜 그 파리를 삼 켰는 지 모르겠어요  
아마도 그녀는 죽을 것이다.

개를 삼킨 노부가 있었어요  
개를 삼키는 돼지.  
그녀는 개를 삼켜 고양이를 잡았습니다.  
그녀는 새를 잡기 위해 고양이를 삼 켰고  
그녀는 거미를 잡기 위해 새를 삼켰습니다.  
그녀는 거미를 삼켜 파리를 잡았습니다.  
그녀가 왜 그 파리를 삼 켰는 지 모르겠어요  
아마도 그녀는 죽을 것이다.

말을 삼킨 노부가 있었어요  
그녀는 물론 죽었다.

10 : 게 티즈 버그 주소 (1452b) : 게 티즈 버그 주소는 얼마나 임의적인가

4 점 7 년 전, 우리 조상들은이 대륙에 새로운 나라를 낳았고 자유를 생각하며 모든 사람이 동등하게 창조되었다는 제안에 전념했습니다. 이제 우리는 위대한 내전에 참여하여 그 국가 또는 그렇게 고 안되고 헌신 된 어떤 국가가 오래 견딜 수 있는지 테스트합니다. 우리는 그 전쟁의 큰 전장에서 만납니다. 우리는 그 분야의 일부를 그 나라가 살 수 있도록 목숨을 바친 사람들을위한 마지막 휴게소로 바쳤습니다. 우리가이 작업을 수행하는 것은 전적으로 적합하고 적절합니다. 그러나 더 큰 의미에서, 우리는 헌신 할 수없고, 헌신 할 수 없으며,이 땅을 거둘 수 없습니다. 이곳에서 어려움을 겪고있는 살아남은 자와 용감한 자들은 그것을 가라 앉히거나 감당할 수없는 우리의 가난한 힘보다 훨씬 더 헌신했습니다. 세상은 우리가 여기서 말하는 것을 거의 기억하지 못하고 오래 기억하지 않을 것입니다. 그러나 그들이 여기서 한 일을 결코 잊지 못할 것입니다. 오히려 여기에서 싸운 사람들이 지금까지 고귀하게 발전한 미완성 된 일에 헌신하는 것이 우리를위한 것입니다. 오히려 우리는 우리 앞에 남아있는 위대한 임무에 헌신하기 위해 노력하는 것입니다.이 영광스러운 죽음으로부터 우리는 그들이 헌신의 마지막 수단을 모두 제공 한 원인에 대한 헌신을 강화합니다. 이 나라는 하나님의 밑에 자유의 새로운 탄생을 겪을 것이며, 사람들이 사람들을 위해, 정부가 땅에서 멸망하지 않을 것이라는 헛된 죽음을 겪었다.

총계 (압축되지 않은) : 6135 자 / 바이트.

즐기세요!


7
이것은 언어를 발명하는 것이 아니라 무언가를 압축하는 것입니다.
Justin

2
점수에 컴파일러 / 실행기 (압축기 / 압축 해제 기)의 크기를 포함시키지 않으면이 도전이 약간 개방적이라고 생각합니다. 언젠가는 사전하드 코딩 사이의 선 이 매우 얇아 질 것입니다.
Dennis

2
Darn, 그리고 나는 이미 타이핑하고있었습니다 private static final String RICK_ROLL_RETURN = "We're no strangers to love...
Graph Theory

1
데니스의 관찰에 대해서는 언급하지 않았다고 생각합니다.
피터 테일러

1
@ xem 나는 #Task, #Input, #Output, #Scoring과 같은 섹션으로 정보를 구성하여 게시물을 개선 할 수 있다고 생각합니다. 또한 컴프레서와 디 컴프레서를위한 소스 코드의 크기가 점수에 포함되어야한다고 생각합니다. 이것은 아무것도 해치지 않지만 Dennis가 지적한 문제를 해결합니다.
Rainbolt

답변:


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자신의 주요 요인으로 유니 코드 문자입니다.

인코딩 된 출력의 바이트 수를 낮추지 않고 유니 코드 문자 수만 줄입니다. 예를 들어 테스트 # 4에는 108문자와 인코딩 된 출력이 포함되어 있습니다 92. 각각의 크기는하지만입니다 108364바이트.

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를 제외한 32 개의 공통 소수를 갖는 것이 편리합니다 1. 즉, 요인은 5 비트에 대한 인덱스로 저장할 수 있습니다.
      • 1 특별한 경우이며 빈 목록으로 표시됩니다.
    3. ps인코딩 지금 시작할 수 있습니다.
      1. 각 수는 ps32 개의 고유 요인 목록에서 색인으로 변환됩니다 (위 코드에서이 목록은로 식별 됨 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마지막을 제외한 반복 , 마지막 2를 제외한 반복 을 병합하는 동안 나머지 네 번째 비트를 사용하여 추가 압축을 수행 할 수 있습니다 .
  • 테스트 파일과 코드는 모두 https://github.com/gxtaillon/codegolf/tree/master/Kolmogorov 에서 확인할 수 있습니다

안녕하세요,이 답변에 감사드립니다! :)로 변환 abcdefghijklm...할 때 바이너리에서 어떤 일이 발생하는지 이해하지 못했습니다. 𘑥򎑡𡁢𑒁豱󐰢𼝤...조금 더 설명해 주시겠습니까? 또한 문제의 문자 수를 수정하고 # 10에서 em 대시를 변환했습니다. 그래도 내 문자 수는 여전히 당신과 다릅니다. Dunno 이유, 나는 mothereff.in 도구를 사용했습니다.
xem

@xem 복잡한 세부 사항이 공개되었습니다.
gxtaillon

내 마음은 문자 그대로 숫자 0과 2-127이 5 비트로 인코딩 될 수 있다는 생각에 의해 말 그대로 날려 왔습니다. 당신은 이것을 혼자서 찾았습니까, 아니면 알려진 것이 었습니까? 보너스 질문 : 인쇄 가능한 ASCII 문자, 즉 95 개의 다른 문자 만 저장하려면 몇 비트가 필요합니까?
xem

@xem 숫자는 5 비트로 인코딩되지 않으며 각 요인은 다음과 같습니다. 5에 대해서만 7 비트를 인코딩하는 방법을 찾았다면 매우 기쁠 것입니다. ASCII 문자의 경우이 방법을 사용하면 여전히 5 비트가 각각 필요합니다.
gxtaillon

1
지정한 범위에는 숫자 당 최대 6 개의 요소가 있으므로 길이는 5 비트 "블록"중 3 개를 사용합니다. 그런 다음 인덱스는 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의 대답과 동일합니다) wc -c) 로 계산 된대로 원본보다 높고 총 8413 바이트입니다 .

압축 비율 (ASCII 대 유니 코드) : 35.01 % (질문에서 6135 바이트 사용 (와 동일 wc -c))

조심하십시오 :

많은 쉘이이 프로그램이 생성하는 유니 코드 문자를 처리 할 수 ​​없습니다. 따라서 압축을 해제하면 쉘에서 문자를 입력 할 때 쉘이 문자를 처리 할 수 ​​없을 때 어딘가에서 텍스트가 잘릴 수 있습니다 stdin.

컴파일

컴파일해야합니다 clang++ 하고 g++ -std=c++11,하지만 같은 표현으로, 연산자 우선 순위에 대한 몇 가지 경고가 표시됩니다 b<<20-n&0xFFFFF으로 처리되지 ((b << 20) - n) & 0xFFFFF않고 하나는 예상대로,하지만 (b << (20 - n)) & 0xFFFFF.

용법

  • 프로그램을 실행 파일로 컴파일하십시오 (예 :) ./compress.
  • ./compress c압축 또는 ./compress d압축 해제 프로그램을 실행하십시오 . (주의, 옵션을 생략 하면 SEGFAULT (오류 검사가 너무 비싸므로 ...) 및 기타 옵션 (예 :D 대신d )은 예기치 않은 결과를 초래할 수 있습니다
  • 입력을 읽고 stdin출력합니다stdout

작동 원리

언 골프

#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 문자를 하나의 유니 코드 문자로 저장하기 위해 모든 개별 ASCII 문자 비트를 유니 코드 문자로 작성하려고합니다. 그러나 사용하지 않은 가비지 비트는 적절한 압축 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

파이썬 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 개의 유니 코드 문자로 인코딩합니다. 총 출력 바이트 수는 3201 바이트이며 원래 입력보다 훨씬 작습니다.

zlib를 사용하여 인코딩 한 다음 유니 코드 인코딩을 사용하여 2 차적으로 인코딩합니다. 대리자를 피하기 위해 약간의 추가 논리가 필요했습니다 (Python은 정말 싫어합니다).

less(37 유니 코드 문자) 로 표시되는 # 4의 출력 예 :

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

또한 압축 해제기를 포함해야합니다.
Beta Decay

@BetaDecay : p는 패커이고, 언 u패커입니다.
nneonneo

1

파이썬 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은 최대 유니 코드 문자 만 처리 할 수 0xFFFF있으므로이 코드는을 발생 ValueError시킵니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.