Ai muốn trở thành người chiến thắng phức tạp Kolmogorov?


22

Nhiệm vụ của bạn hôm nay là phát minh ra một máy nén văn bản.

Bài tập

Bạn sẽ viết hai chức năng:

  • Trình đóng gói là một hàm chấp nhận một chuỗi các ký tự ASCII (U + 0000 đến U + 007F) và xuất ra một chuỗi Unicode (U + 0000 đến U + 10FFFF), chứa ít ký tự nhất có thể.

  • Trình giải nén là một hàm chấp nhận chuỗi Unicode được mã hóa và xuất ra chính xác chuỗi ASCII gốc.

Đầu vào

Đầu vào được ủy quyền duy nhất là chuỗi ASCII (đối với trình đóng gói) và chuỗi Unicode được đóng gói (đối với trình giải nén). Không có đầu vào của người dùng, không có kết nối internet, không sử dụng hệ thống tập tin.

Chức năng của bạn có thể có quyền truy cập vào danh sách các từ tiếng Anh này . Bạn có thể sử dụng danh sách này dưới dạng tệp txt cục bộ hoặc sao chép nội dung của nó trong mã nguồn của bạn dưới dạng một chuỗi hoặc một chuỗi các chuỗi .

Bạn không thể mã hóa các đoạn mã dưới đây trong các chức năng của mình.

Đầu ra

Đầu ra được ủy quyền duy nhất cho cả hai chức năng là một chuỗi.

Đầu ra của trình giải nén phải chứa chính xác các ký tự giống như đầu vào của trình đóng gói.

Đầu vào và đầu ra của bạn có thể sử dụng bất kỳ mã hóa ký tự nào hỗ trợ tất cả Unicode (UTF-8/16/32, GB18030, ...), vì điểm của bạn sẽ chỉ phụ thuộc vào số lượng ký tự Unicode trong đầu ra. Vui lòng chính xác mã hóa bạn đang sử dụng, mặc dù.

Để đếm số lượng ký tự Unicode trong đầu ra của bạn, bạn có thể sử dụng công cụ này: http://otherseff.in/byte-count

Chấm điểm

Mục nhập của bạn phải có thể đóng gói và giải nén 10 đoạn văn bản sau (mà tôi đã lấy trên diễn đàn này).

Điểm của bạn sẽ là tổng kích thước của 10 chuỗi được đóng gói của bạn (bằng ký tự Unicode) + kích thước của hai hàm của bạn (cũng bằng ký tự Unicode)

Đừng đếm kích thước của từ điển nếu bạn sử dụng nó.

Vui lòng bao gồm trong các mục của bạn "điểm số" của từng đoạn và phiên bản đóng gói của chúng.

Điểm số thấp nhất chiến thắng.

Dữ liệu

Dưới đây là các đoạn mã hóa để tính điểm của bạn:

1: Lời bài hát của Rick Roll (1870b): Chúng ta không xa lạ gì với môn đánh gôn, bạn biết luật chơi, và tôi cũng vậy

Chúng ta không xa lạ gì với tình yêu
Bạn biết các quy tắc và tôi cũng vậy
Một cam kết đầy đủ là những gì tôi nghĩ về
Bạn sẽ không nhận được điều này từ bất kỳ chàng trai khác
Tôi chỉ muốn nói cho bạn biết tôi đang cảm thấy như thế nào
Phải làm cho bạn hiểu

Tôi không đời nào bỏ cậu đâu
Không bao giờ làm bạn thất vọng
Sẽ không bao giờ chạy xung quanh và bỏ rơi bạn
Sẽ không bao giờ làm bạn khóc
Sẽ không bao giờ nói tạm biệt
Sẽ không bao giờ nói dối và làm tổn thương bạn

Chúng tôi đã biết nhau rất lâu
Trái tim bạn đang đau nhưng
Bạn quá ngại nói
Bên trong cả hai chúng tôi đều biết chuyện gì đang xảy ra
Chúng tôi biết trò chơi và chúng tôi sẽ chơi nó
Và nếu bạn hỏi tôi cảm thấy thế nào
Đừng nói với tôi là bạn quá mù để xem

Tôi không đời nào bỏ cậu đâu
Không bao giờ làm bạn thất vọng
Sẽ không bao giờ chạy xung quanh và bỏ rơi bạn
Sẽ không bao giờ làm bạn khóc
Sẽ không bao giờ nói tạm biệt
Sẽ không bao giờ nói dối và làm tổn thương bạn

Tôi không đời nào bỏ cậu đâu
Không bao giờ làm bạn thất vọng
Sẽ không bao giờ chạy xung quanh và bỏ rơi bạn
Sẽ không bao giờ làm bạn khóc
Sẽ không bao giờ nói tạm biệt
Sẽ không bao giờ nói dối và làm tổn thương bạn

(Ồ, từ bỏ bạn)
(Ồ, từ bỏ bạn)
(Ôi)
Sẽ không bao giờ cho, sẽ không bao giờ cho
(Cho bạn lên)
(Ôi)
Sẽ không bao giờ cho, sẽ không bao giờ cho
(Cho bạn lên)

Chúng ta đã biết nhau rất lâu
Trái tim bạn đang đau nhưng
Bạn quá ngại nói
Bên trong cả hai chúng tôi đều biết chuyện gì đang xảy ra
Chúng tôi biết trò chơi và chúng tôi sẽ chơi nó

Tôi chỉ muốn nói cho bạn biết tôi đang cảm thấy như thế nào
Phải làm cho bạn hiểu

Tôi không đời nào bỏ cậu đâu
Không bao giờ làm bạn thất vọng
Sẽ không bao giờ chạy xung quanh và bỏ rơi bạn
Sẽ không bao giờ làm bạn khóc
Sẽ không bao giờ nói tạm biệt
Sẽ không bao giờ nói dối và làm tổn thương bạn

Tôi không đời nào bỏ cậu đâu
Không bao giờ làm bạn thất vọng
Sẽ không bao giờ chạy xung quanh và bỏ rơi bạn
Sẽ không bao giờ làm bạn khóc
Sẽ không bao giờ nói tạm biệt
Sẽ không bao giờ nói dối và làm tổn thương bạn

Tôi không đời nào bỏ cậu đâu
Không bao giờ làm bạn thất vọng
Sẽ không bao giờ chạy xung quanh và bỏ rơi bạn
Sẽ không bao giờ làm bạn khóc
Sẽ không bao giờ nói tạm biệt
Sẽ không bao giờ nói dối và làm tổn thương bạn

2: Golfer (412b): Chơi gôn nghệ thuật ASCII

      '\. . |> 18 >>
        \. '. |
       Ô >>. 'o |
        \. |
        / \. |
       / /. ' |
 js ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^

3: số kim cương (233b): In viên kim cương này

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

4: bảng chữ cái bốn lần (107b): In bảng chữ cái bốn lần

abcdefghijklmnopqrstuvwxyz
qwertyuiopasdfghjklzxcvbnm
pyfgcrlaoeuidhtnsqjkxbmwvz
zyxwvutsrqponmlkjihgfedcba

5: Lời bài hát của Old McDonald (203b): Chức năng MacDonald cũ

MacDonald cũ có một trang trại, EIEIO,
Và trong trang trại đó, anh ta có một con bò, EIEIO,
Với một moo moo ở đây và một moo moo ở đó,
Ở đây một moo, có một moo, ở khắp mọi nơi một moo moo,
MacDonald cũ đã có một trang trại, EIEIO!

6: Rock xung quanh lời bài hát đồng hồ (144b): Rock quanh đồng hồ

1, 2, 3 giờ, đá 4 giờ,
5, 6, 7 giờ, đá 8 giờ,
9, 10, 11 giờ, đá 12 giờ,
Chúng ta sẽ đá xung quanh đồng hồ tối nay.

7: Hello World (296b): Nói "Xin chào" với thế giới trong nghệ thuật ASCII

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

8: Phước lành Ailen (210b): Một phước lành Ailen cổ

Có thể con đường đi lên để gặp bạn
Có thể gió luôn luôn ở phía sau của bạn
Có mặt trời tỏa sáng ấm áp trên khuôn mặt của bạn
Những cơn mưa rơi nhẹ trên cánh đồng của bạn
Và cho đến khi chúng ta gặp lại
Chúa có thể giữ bạn trong lòng bàn tay của Ngài

9: Có một lời bài hát của bà già (1208b): Có một bà già

Có một bà già nuốt một con ruồi.  
Tôi không biết tại sao cô ấy nuốt con ruồi đó,  
Có lẽ cô ấy sẽ chết.

Có một bà già nuốt một con nhện,  
Điều đó quằn quại và vặn vẹo và cười khúc khích trong cô.  
Cô nuốt con nhện để bắt ruồi,  
Tôi không biết tại sao cô ấy nuốt con ruồi đó,  
Có lẽ cô ấy sẽ chết.

Có một bà già nuốt một con chim,  
Thật vô lý khi nuốt một con chim.  
Cô nuốt con chim để bắt con nhện,  
Cô nuốt con nhện để bắt ruồi,  
Tôi không biết tại sao cô ấy nuốt con ruồi đó,  
Có lẽ cô ấy sẽ chết.

Có một bà già nuốt một con mèo,  
Hãy tưởng tượng rằng để nuốt một con mèo.  
Cô nuốt con mèo để bắt con chim,  
Cô nuốt con chim để bắt con nhện,  
Cô nuốt con nhện để bắt ruồi,  
Tôi không biết tại sao cô ấy nuốt con ruồi đó,  
Có lẽ cô ấy sẽ chết.

Có một bà già nuốt một con chó,  
Thật là một con heo để nuốt một con chó.  
Cô nuốt con chó để bắt con mèo,  
Cô nuốt con mèo để bắt con chim,  
Cô nuốt con chim để bắt con nhện,  
Cô nuốt con nhện để bắt ruồi,  
Tôi không biết tại sao cô ấy nuốt con ruồi đó,  
Có lẽ cô ấy sẽ chết.

Có một bà già nuốt một con ngựa,  
Cô ấy chết tất nhiên.

10: địa chỉ gettysburg (1452b): Địa chỉ Gettysburg ngẫu nhiên như thế nào

Bốn điểm và bảy năm trước, cha chúng ta đã mang đến cho lục địa này một quốc gia mới, được hình thành trong tự do và dành riêng cho đề xuất rằng tất cả mọi người đều được tạo ra như nhau. Bây giờ chúng tôi đang tham gia vào một cuộc nội chiến lớn, kiểm tra xem quốc gia đó, hay bất kỳ quốc gia nào được quan niệm và tận tâm như vậy, có thể chịu đựng được lâu dài. Chúng tôi được gặp trên một chiến trường vĩ đại của cuộc chiến đó. Chúng tôi đã đến để dành một phần của lĩnh vực đó, như một nơi an nghỉ cuối cùng cho những người ở đây đã cho cuộc sống của họ rằng quốc gia đó có thể sống. Hoàn toàn phù hợp và đúng đắn mà chúng ta nên làm điều này. Nhưng, trong một ý nghĩa lớn hơn, chúng ta không thể cống hiến, chúng ta không thể tận hiến, chúng ta không thể thần thánh mặt đất này. Những người đàn ông dũng cảm, sống và chết, đã vật lộn ở đây, đã tận hiến nó, vượt xa sức mạnh kém cỏi của chúng ta để thêm hoặc gièm pha. Thế giới sẽ ít lưu ý, cũng không nhớ những gì chúng ta nói ở đây, nhưng nó không bao giờ có thể quên những gì họ đã làm ở đây. Thay vào đó, đối với chúng tôi, người sống được dành riêng cho công việc còn dang dở mà họ đã chiến đấu ở đây đã tiến bộ rất cao. Đúng hơn là chúng ta ở đây dành riêng cho nhiệm vụ lớn lao còn lại trước chúng ta - rằng từ những người chết được tôn vinh này, chúng ta đã hết lòng vì lý do mà họ đã đưa ra biện pháp tận tâm cuối cùng - rằng chúng ta ở đây quyết tâm cao rằng những người chết này sẽ không đã chết trong vô vọng - rằng quốc gia này, dưới Thiên Chúa, sẽ có một sự tự do mới - và chính phủ của nhân dân, bởi nhân dân, vì nhân dân, sẽ không bị diệt vong khỏi trái đất.

Tổng cộng (không nén): 6135 ký tự / byte.

Chúc vui vẻ!


7
Đây không phải là một thách thức để phát minh ra một ngôn ngữ, đây là một thách thức để nén một cái gì đó.
Justin

2
Tôi nghĩ rằng không bao gồm kích thước của trình biên dịch / thực thi (máy nén / giải nén) trong điểm số khiến cho thử thách này có một chút kết thúc mở. Tại một số điểm, ranh giới giữa từ điểnmã hóa cứng sẽ trở nên rất mỏng.
Dennis

2
Chết tiệt, và ở đây tôi đã gõ private static final String RICK_ROLL_RETURN = "We're no strangers to love...
Lý thuyết đồ thị

1
Tôi không nghĩ rằng bạn đã giải quyết quan sát của Dennis.
Peter Taylor

1
@xem Tôi nghĩ rằng bài đăng có thể được cải thiện bằng cách sắp xếp thông tin thành các phần như #Task, #Input, #Output, #Scoring. Tôi cũng nghĩ rằng kích thước của mã nguồn cho máy nén và bộ giải nén nên được đưa vào điểm số. Điều này không làm tổn thương gì, nhưng nó giải quyết vấn đề Dennis chỉ ra.
Rainbolt

Câu trả lời:


6

Haskell - 5322 điểm

Byte mã: 686

Kích thước ban đầu : 6147 = 1871+415+234+108+204+145+297+211+1209+1453

Kích thước được mã hóa: 4636 = 1396+233+163+92+153+115+197+164+979+1144

Ghi bàn : 686+ 4636

Nén số ký tự: ~25%

Tối ưu hóa sang một bên, điều này lưu trữ các giá trị giữa 07ftrong các ký tự unicode là các yếu tố chính của chúng.

Nó không hạ thấp số byte của đầu ra được mã hóa, nó chỉ làm giảm số lượng ký tự unicode. Chẳng hạn, bài kiểm tra số 4 chứa các 108ký tự và đầu ra được mã hóa , 92. Kích thước tương ứng của chúng là tuy nhiên 108364byte.

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]

Làm thế nào nó hoạt động

  • Mã hóa

    1. Mỗi ký tự được chuyển đổi thành số tương đương, hãy gọi số này n.
    2. nsau đó được chuyển đổi thành một danh sách các yếu tố chính của nó , ps.
      • Điều thuận tiện xảy ra là các số từ 0 đến 127 có 32 thừa số nguyên tố chung, không bao gồm 1. Điều này có nghĩa là chúng, các yếu tố, có thể được lưu trữ dưới dạng chỉ mục trên chỉ 5 bit.
      • 1 là một trường hợp đặc biệt và được đại diện bởi một danh sách trống.
    3. Với psmã hóa bây giờ có thể bắt đầu.
      1. Mỗi số psđược chuyển đổi thành chỉ mục của nó trong danh sách 32 yếu tố duy nhất (Trong đoạn mã trên, danh sách này được xác định là a).
      2. (Hãy ghi nhớ tại thời điểm này, chúng tôi đang xử lý một danh sách các danh sách các chỉ số của các yếu tố chính) Để tiến hành bước tiếp theo, pscần phải được làm phẳng. Để duy trì tính toàn vẹn của dữ liệu, mỗi danh sách được chuyển đổi thành một danh sách khác gồm hai phần
        1. Phần tử đầu tiên lưu trữ độ dài của nó và nếu nó bao gồm cùng một yếu tố.
          • Có tối đa 6 yếu tố chính trên mỗi danh sách, thông tin này được lưu trữ trên 3 bit ngoài cùng bên phải. Bit thứ năm được sử dụng làm cờ để cho biết danh sách có bao gồm một yếu tố hay không.
        2. Các yếu tố còn lại là chính các chỉ mục hoặc một chỉ mục nếu có ít hơn hai yếu tố khác nhau trong danh sách.
      3. Những danh sách này sau đó được nối vào một danh sách phẳng , fs.
    4. Các phần tử của fssau đó có thể được đóng gói thành các ký tự unicode bằng cách sử dụng dịch chuyển bit.
  • Giải mã

    • Làm các bước mã hóa ngược lại.
    • Nếu bạn đang tự hỏi làm thế nào 1phù hợp với điều này, tôi muốn nhắc nhở bạn điều đó product [] == 1.

Xét nghiệm

Sử dụng giao diện này để kiểm tra sẽ rất đau nên tôi đã sử dụng chức năng này để cung cấp kết quả bên dưới.

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

Mẫu vật

Đầu ra của chức năng mã hóa gcho bài kiểm tra số 4 là cái này
"\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"
hoặc nếu bạn là một người thông thạo tiếng nói, thì đây
𘑥򎑡𡁢𑒁豱󐰢𼝤􈓃𐲂숼𨐢𐨽𘑥𐦅􈐤򏡡𒁃񈰡񐱂𰠱𑃥􈑃򑑁򔓂踾𑃱󐰢񀰡񔢣𐣃𐲂𓂡􈒑󈡩𠐣𘰢𼝤􈓂숼𨐢𐡁򑐡衣𐢢쑡𡁡𙘽򉡁𙐡􉉅𘑇򎱢𑑡𒂡衂򎑂񤝃𐣃𐲄􈱤􈓃􊡡𙑃񌟂숼𨐡𐱡𡈽𑃢쑁򔓂豁򔢥􈑃𑃢쑢𑡡ꡃ񈰢񄟅􈐤𐦃貱󩐡𐡑󠠰𘡤𠐡𴝣裱󑀡𘱢𑑡𡈹

Ghi chú bổ sung

  • Sử dụng http://otherseff.in/byte-count , danh sách thư mục và edTestkích thước của các bài kiểm tra đều nhất quán nhưng vẫn khác với kích thước được chỉ định trong câu hỏi.
  • Test # 10 chứa một vài dấu gạch ngang EM ( ) mà tôi thay thế bằng -vì chúng nằm ngoài 0- 7fphạm vi.
  • Việc nén thêm có thể đạt được bằng cách sử dụng bit thứ tư còn lại trong khi làm phẳng, ví dụ, 00trường hợp cơ sở, 01lặp lại tất cả, 10lặp lại ngoại trừ cuối cùng, 11lặp lại ngoại trừ hai lần cuối.
  • Các tệp kiểm tra và mã đều có sẵn tại đây https://github.com/gxtaillon/codegolf/tree/master/Kolmogorov

Xin chào, cảm ơn vì câu trả lời này! :) Tôi không hiểu điều gì xảy ra trong hệ nhị phân khi bạn chuyển đổi abcdefghijklm...sang 𘑥򎑡𡁢𑒁豱󐰢𼝤..., bạn có thể giải thích thêm một chút không? Ngoài ra, tôi đã sửa số đếm char và chuyển đổi dấu gạch ngang trong số 10, trong câu hỏi. Số char của tôi vẫn khác so với bạn. Tại sao, tôi đã sử dụng công cụ Mothereff.in.
xem

@xem Các chi tiết phức tạp đã được tiết lộ.
gxtaillon

Tâm trí của tôi (theo nghĩa bóng) được thổi theo nghĩa đen bởi ý tưởng rằng các số 0 và 2-127 có thể được mã hóa trên 5 bit. Bạn đã tìm thấy điều này một mình hay nó là một cái gì đó được biết đến? Câu hỏi thưởng: bạn cần bao nhiêu bit để chỉ lưu trữ các ký tự ascii có thể in, tức là 95 ký tự khác nhau?
xem

@xem Các số không được mã hóa trên 5 bit, mỗi yếu tố của chúng là. Tôi sẽ rất vui nếu tôi đã tìm ra cách mã hóa 7 bit chỉ trên 5. Đối với các ký tự ascii, sử dụng phương thức này họ vẫn sẽ cần 5 bit mỗi bit.
gxtaillon

1
Vì trong phạm vi bạn đã chỉ định có tối đa 6 yếu tố cho mỗi số, độ dài sử dụng 3 trong số 5 "khối" 5 bit. Sau đó, các chỉ mục được mã hóa trên 5 bit, vâng. Trong triển khai này, một trong 2 bit không sử dụng trong khối độ dài được sử dụng để có được nén bổ sung.
gxtaillon

4

C ++ (C ++ 11), 2741 điểm

Câu trả lời này sử dụng UTF-32 làm mã hóa cho văn bản nén.

#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();}

Char đếm và ghi bàn

Mã: 593 ký tự (dòng mới bị xóa)

Văn bản nén (ký tự unicode) : 654 + 145 + 82 + 38 + 51 + 104 + 73 + 423 + 506 = 2148 (được tính bằng wc -msố ký tự unicode thay vì byte, số byte được tính như câu trả lời của @ gxtaillon , cao hơn bản gốc, tổng cộng 8413 byte, như được tính bằng wc -c).

Tỷ lệ nén (ASCII thành unicode) : 35,01% (sử dụng 6135 byte từ câu hỏi (giống như wc -c))

Coi chừng

Rất nhiều shell không thể xử lý các ký tự unicode mà chương trình này tạo ra. Do đó, việc giải nén có thể dẫn đến việc văn bản bị cắt ở đâu đó khi trình bao không thể xử lý một ký tự, vì đầu vào được lấy stdintừ trình bao.

Biên dịch

Nó sẽ được biên dịch với clang++g++ -std=c++11, nhưng nó sẽ hiển thị một số cảnh báo về mức độ ưu tiên của toán tử, vì một biểu thức như b<<20-n&0xFFFFFsẽ không được xử lý như ((b << 20) - n) & 0xFFFFF, như người ta có thể mong đợi, mà là (b << (20 - n)) & 0xFFFFF.

Sử dụng

  • Biên dịch chương trình thành một tệp thực thi, vd ./compress.
  • Chạy chương trình như ./compress cđể nén hoặc ./compress dgiải nén. (Cẩn thận, bỏ tùy chọn cung cấp SEGFAULT (kiểm tra lỗi rất tốn kém ...) và các tùy chọn khác (như sử dụng Dthay vì d) có thể cho kết quả không mong muốn
  • Đầu vào được đọc từ stdinvà đầu ra được ghi vàostdout

Làm thế nào nó hoạt động

Bị đánh cắp

#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();
}

Giải trình

Như tất cả các ký tự unicode từ U+0000để U+10FFFFđược cho phép, chúng ta có thể sử dụng 20 bit trên unicode char: U+FFFFFsử dụng 20 bit và vẫn nằm trong phạm vi cho phép. Vì vậy, chúng tôi chỉ cố gắng nhồi nhét tất cả các bit char ASCII riêng lẻ vào các ký tự unicode để lưu trữ nhiều ký tự ASCII trong một ký tự unicode. Tuy nhiên, chúng ta cũng cần lưu trữ số bit được sử dụng trong ký tự unicode cuối cùng, bởi vì các bit rác không được sử dụng có thể được hiểu là các ký tự ASCII được nén phù hợp. Vì số bit được sử dụng tối đa trong ký tự unicode cuối cùng là 20, chúng ta sẽ cần 5 bit cho số đó, được đặt vào đầu dữ liệu nén.

Ví dụ đầu ra

Đây là đầu ra cho ví dụ # 4 (như được đưa ra bởi 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>

( 쏏𦛏đưa ra <U+C3CF><U+266CF>dưới dạng mã ký tự, nhưng tôi có thể đã hiểu sai)


2

Python 3, 289 + 818 = 1107 điểm

Chỉ đánh golf nhẹ.

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'))

Tổng kích thước mã là 289 byte và mã hóa 6135 byte đã cho thành 818 ký tự Unicode - tổng số byte đầu ra là 3201 byte, nhỏ hơn đáng kể so với đầu vào ban đầu.

Mã hóa bằng zlib, sau đó sử dụng mã hóa unicode. Một số logic bổ sung là cần thiết để tránh người thay thế (điều mà Python thực sự ghét).

Ví dụ đầu ra từ số 4, như được thấy bởi less(37 ký tự 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>

Chương trình điều khiển để thử nghiệm:

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)

Số lượng byte đầu ra:

 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
Có phải thực tế là điều này đang sử dụng một thư viện nén gian lận một chút không?
Beta Decay

@BetaDecay: Nó không hạn chế điều đó trong câu hỏi, vì vậy tôi cho rằng đó là trò chơi công bằng.
nneonneo

Ngoài ra, bạn cần bao gồm một bộ giải nén.
Beta Decay

@BetaDecay: plà trình đóng gói, ulà trình giải nén.
nneonneo

1

Python 2 - 1141 điểm

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)

Kích thước mã là 281byte và nó mã hóa các 6135byte thành860 ký tự unicode.

Làm thế nào nó hoạt động:

Để mã hoá:

  1. Nén chuỗi được mã hóa.
  2. Giải thích chuỗi nén dưới dạng số 256 cơ sở.
  3. Chuyển đổi số thành nhị phân.
  4. Chia nhị phân thành các nhóm 19bit, thêm một 1bit vào đầu mỗi bit và sau đó chuyển đổi thành các ký tự Unicode.

Giải mã là ngược lại.

Lưu ý rằng một số phiên bản của python chỉ có thể xử lý các ký tự unicode tối đa 0xFFFFvà do đó mã này sẽ tăng a ValueError.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.