การบีบอัด Palindrome


15

ท้าทาย

เขียนโปรแกรมที่บีบอัดและคลายขนาดข้อความ ASCII แบบไม่สูญเสียข้อมูล มันควรจะมีความเชี่ยวชาญในการทำงานได้ดีกับ palindromes รวมถึง case-insensitive และ punctuation-palindromes การบีบอัดที่ดีที่สุดกับแหล่งที่เล็กที่สุดชนะ

เกณฑ์การให้คะแนน

total_bytes_saved / sqrt(program_size) - ชนะคะแนนสูงสุด

total_bytes_savedเป็นจำนวนไบต์ที่บีบอัดสตริงที่มีขนาดเล็กกว่าต้นฉบับรวมในกรณีทดสอบด้านล่างทั้งหมด program_sizeมีขนาดเป็นไบต์ของซอร์สโค้ดของโปรแกรมบีบอัดและคลายการบีบอัด รหัสที่ใช้ร่วมกันระหว่างสองจำเป็นต้องนับเพียงครั้งเดียว

ตัวอย่างเช่นหากมี 10 กรณีทดสอบและโปรแกรม 100 ไบต์บันทึก 5 ไบต์ในกรณีทดสอบ 7 กรณีแต่ละกรณีมี 2 กรณี แต่กรณีทดสอบสุดท้ายคือ 2 ไบต์อีกต่อไปโซลูชันจะได้คะแนน 5.3 ( (7 * 5 + 10 * 2 - 2) / sqrt(100) = 5.3)

กรณีทดสอบ

  • tacocat
  • toohottohoot
  • todderasesareddot
  • amanaplanacanalpanama
  • wasitacaroracatisaw?
  • Bob
  • IManAmRegalAGermanAmI
  • DogeeseseeGod
  • A Santa at NASA
  • Go hang a salami! I'm a lasagna hog.

กฎระเบียบ

  1. ช่องโหว่มาตรฐานใช้
  2. การบีบอัดต้องทำงานกับสตริงข้อความ ASCII ที่พิมพ์ได้ทั้งหมด (ไบต์ 32-126, รวมแล้ว) ไม่ใช่แค่ palindromes อย่างไรก็ตามไม่จำเป็นต้องประหยัดพื้นที่สำหรับอินพุตใด ๆ
  3. เอาต์พุตสามารถเป็นลำดับไบต์หรืออักขระใด ๆ โดยไม่คำนึงถึงการนำไปใช้หรือการนำเสนอภายใน (สตริงรายการและอาร์เรย์เป็นเกมที่ยุติธรรมทั้งหมดเป็นต้น) หากเข้ารหัสเป็น UTF-8 ให้นับไบต์ไม่ใช่ตัวอักษร ไม่อนุญาตให้ใช้สตริงแบบกว้าง (เช่น UTF-16 หรือ UTF-32) เว้นแต่ว่ารหัสเดียวที่อาจใช้ได้ระหว่าง 0 ถึง 255
  4. ไม่อนุญาตให้มีการบีบอัดบิวด์อิน / บีบอัดข้อมูล

เพื่อความเพลิดเพลินของเราเองโพสต์สตริงที่บีบอัดด้วยซอร์สโค้ดของคุณ

อัปเดต 1:การให้คะแนนเปลี่ยนจากtotal_bytes_saved / program_sizeเป็นtotal_bytes_saved / sqrt(program_size)เพื่อให้มีน้ำหนักมากขึ้นเป็นการบีบอัดที่ดีขึ้นและมีน้ำหนักน้อยลงในการตีกอล์ฟที่ดุเดือด ปรับคะแนนของคุณตาม

UPDATE 2: ได้รับการแก้ไขแล้วwasitacaroraratisaw?ให้เป็นwasitacaroracatisaw?


2
หากเป็นกรณีเครื่องหมายวรรคตอนและการเว้นวรรคจะถูกลบออกจากอินพุตจะรับประกันได้หรือไม่ว่าอินพุตจะเป็นพาลินโดรมแบบเข้มงวดหรือไม่? แก้ไข: ไม่เป็นไร - ฉันเห็นว่าwasitacaroraratisaw?มันเป็นตัวอย่างที่ตรงกันข้าม
Digital Trauma

2
เราควรสนับสนุนอักขระ ASCII ประเภทใดในอินพุต มันคือ[32-126]อะไร
Arnauld

1
ใช่ฉันไม่คิดว่า1000 *จำเป็นต้องมีส่วนจริงๆและไม่มีฉันไม่คิดว่ามันจะทำให้คะแนนรู้สึก "พอใจ";)
Erik the Outgolfer

1
เราสามารถใช้ built-in การบีบอัด / การบีบอัดได้หรือไม่?
Lynn

4
ด้วยอินพุตเพียงเล็กน้อยจึงไม่มีขอบเขตสำหรับการทำสิ่งที่ฉลาด มันคงจะดีถ้ามีอย่างน้อยสองสามครั้ง
user1502040

คำตอบ:


16

JavaScript (ES6), 3.143 (บันทึก 81 ไบท์, โปรแกรม 664 ไบต์)

R='replace',S=String.fromCharCode,T=c=>c.charCodeAt(),U='toUpperCase',V='0000000',W=(a,b,c=2)=>a.toString(c).slice(b),X=x=>'0b'+x,Y=a=>[...a].reverse().join``,Z=/[^]/g
C=s=>S(...((Y(q=s[U]()[R](/[^A-Z]/g,m=''))==q?(q=q.slice(0,p=-~q.length/2),p%1&&10):11)+q[R](Z,x=>W(T(x),2))+111+s[R](Z,c=>/[a-z]/.test(c)?W("00",m,m=1):m+(/[A-Z]/.test(c,m='')?"01":W(c<'!'?2:T(c)+384)))+V).match(/(?!0+$).{8}/g).map(X))
D=s=>{s=s[R](Z,c=>W(256+T(c),1))+V;M=r=>(s=s[R](p=s.match(`^${r}|`)[0],''),p);for([,a]=M`1.|0`,t=u=i='';!M`111`;)t+=W(X(M`.{5}`)-~8,0,36);for(t+=W(Y(t),a?a/0:1);p;)u+=M`0(?=00)|00?1`?(c=t[i++])?+p[1]?c[U]():c:'':M`10`?' ':M`11`&&S(X(M`.{7}`));return u+W(t,i)}

ตอนนี้ฉันค่อนข้างพอใจกับโปรแกรมนี้ (และระบบการให้คะแนน) ฉันจะเขียนคำอธิบายเล็กน้อย

แนวคิดพื้นฐานคือการบีบอัดอินพุตให้เป็นสตริงของบิตจากนั้นบีบอัด 8 บิตแต่ละชุดเป็นไบต์ เพื่อวัตถุประสงค์ในการอธิบายฉันจะจัดการบิตสตริง

สตริงบิตสามารถแยกออกเป็นหลายส่วน:

input  -> Taco Cat.
output -> 0101000000100011011111110100001100100011101011100000000

0      | 10100 00001 00011 01111 111 | 01 00001 10 01 0001 110101110
header | letter data                 | styling data

ส่วนหัวคือการทำแผนที่ง่าย ๆ :

0  -> odd-length palindrome
10 -> even-length palindrome
11 -> non-palindrome

ข้อมูลจดหมายยังค่อนข้างง่าย ก่อนอื่นตัวอักษรที่ไม่ใช่ทั้งหมดจะถูกดึงออกมาจากสตริงและตัวอักษรทั้งหมดจะถูกแปลงเป็นตัวพิมพ์ใหญ่ หากสตริงที่ได้คือ palindrome ครึ่งที่กลับด้านจะถูกปล้น จากนั้นการทำแผนที่นี้จะถูกนำไปใช้:

A -> 00001
B -> 00010
C -> 00011
D -> 00100
...
Z -> 11010

111ส่วนนี้จะสิ้นสุดลงด้วย หลังจากนั้นข้อมูลการจัดแต่งทรงผมซึ่งเก็บข้อมูลตัวพิมพ์ใหญ่และตัวพิมพ์เล็กและไม่ใช่ตัวอักษร สิ่งนี้ทำงานได้เช่น:

01 -> next letter as uppercase
0...01 (n 0s) -> next (n-1) letters as lowercase
10 -> space
11xxxxxxx -> character with code point 0bxxxxxxx

จากตัวอย่างที่แสดงข้างต้นเรามี

header: 0 -> palindrome
letter data: 10100 00001 00011 01111 111 -> taco
styling data:
  01        -> T
  00001     -> aco
  10        -> <space>
  01        -> C
  0001      -> at
  110101110 -> .

เมื่อถึงจุดสิ้นสุดของสตริงบิตอักขระที่เหลือทั้งหมดจากข้อมูลตัวอักษรจะถูกผนวกเข้ากับผลลัพธ์ สิ่งนี้ช่วยให้เราไม่ต้องทำสิ่งใดสิ่งหนึ่งที่ผ่านมา000...001และช่วยให้เราสามารถตัดบิตเหล่านี้ออกจากสตริง

จะผ่านกรณีทดสอบ:

tacocat -> 3 bytes (-4)
    24 bits: 010100000010001101111111
toohottohoot -> 5 bytes (-7)
    35 bits: 10101000111101111010000111110100111
todderasesareddot -> 7 bytes (-10)
    49 bits: 0101000111100100001000010110010000011001100101111
amanaplanacanalpanama -> 8 bytes (-13)
    59 bits: 00000101101000010111000001100000110000001011100000100011111
wasitacaroracatisaw? -> 11 bytes (-9)
    84 bits: 010111000011001101001101000000100011000011001001111111000000000000000000001110111111
Bob -> 2 bytes (-1)
    16 bits: 0000100111111101
IManAmRegalAGermanAmI -> 13 bytes (-8)
    98 bits: 00100101101000010111000001011011001000101001110000101100111010100010100101000001010100000010100101
DogeeseseeGod -> 7 bytes (-6)
    54 bits: 000100011110011100101001011001100101111010000000000101
A Santa at NASA -> 8 bytes (-7)
    63 bits: 100000110011000010111010100000011110110010000011000011001010101
Go hang a salami! I'm a lasagna hog. -> 20 bytes (-16)
   154 bits: 1000111011110100000001011100011100001100110000101100000010110101001111010011000000110001100000000111010000110011101001110011000110000000001100000111010111

ว้าว. ฉันประทับใจวิธีการนี้มาก ฉันไม่เคยคิดที่จะทำการเข้ารหัสบิตเช่นนี้ (ฉันคิดว่ากรณีของการบรรจุ ASCII เป็น 7 บิต แต่ไม่ประหยัดพื้นที่มากสำหรับ palindromes) ฉันประทับใจที่คุณจัดการเพื่อประหยัดพื้นที่Bobเช่นกัน
Beefster

4
นี่เป็นตัวอย่างที่ดีของพื้นฐานทางวิศวกรรม การอธิบายปัญหาคิดถึงวิธีต่าง ๆ ในการแก้ปัญหาและทำการแลกเปลี่ยนระหว่างความต้องการ (เช่นจำนวนบิตที่จะอุทิศให้กับรูปแบบต่าง ๆ ) ฯลฯ
Robert Fraser

@Beefster ขอบคุณ :-) Bobจริง ๆ เพียงแค่ตกอยู่ในสถานที่ - 1 บิตสำหรับส่วนหัว 10 + 3 บิตสำหรับตัวอักษรสองตัวและ 2 บิตสำหรับตัวอักษรตัวพิมพ์ใหญ่เดียว ไม่สามารถทำให้สั้นลงได้ถ้าฉันพยายามอย่างหนักที่สุด ...
ETHproductions

1
@KevinCruijssen ปัญหาคือสิ่งที่เพิ่มเข้ามาคือสตริงดังนั้นจึงต้องแปลงเป็นตัวเลขก่อน วิธีนี้เป็นไบต์ที่สั้นกว่า-0+9
ETHproductions

1
@ETHproductions อ่าแน่นอน (ไม่ได้สังเกตว่ามันเป็นสตริง)! +9จะเชื่อมโยงกับสตริงในขณะที่-~8จะใช้การ+9คำนวณทางคณิตศาสตร์ (เนื่องจาก-ไม่ได้ทำอะไรเลยสำหรับสตริงดังนั้นจึงตีความได้ว่าเป็นตัวเลข) ในกรณีที่-~8เป็นสมาร์ทสวย :) คำตอบที่ดี btw +1 จากฉัน! ฉลาดมากที่เก็บข้อมูลทั้งหมดเป็นบิตเช่นนั้นแม้บันทึกไบต์Bobไว้
Kevin Cruijssen

2

Python 2: 2.765 (บันทึก 70 ไบต์, โปรแกรม 641 ไบต์)

ฉันเปลี่ยนแนวทางเล็กน้อย ตอนนี้มันทำงานได้ดีกับ palindromes ที่ไม่สมบูรณ์ ไม่มีสตริงที่บีบอัดที่จะยาวกว่าอินพุต Palindromes ที่มีความยาวเท่ากันสมบูรณ์แบบจะบีบอัดให้มีขนาดเดิม 50% เสมอ

A=lambda x:chr(x).isalpha()
def c(s):
 r=bytearray(s);q=len(r);L=0;R=q-1;v=lambda:R+1<q and r[R+1]<15
 while L<=R:
  while not A(r[L])and L<R:L+=1
  while not A(r[R])and R:
   if v()and r[R]==32:r[R]=16+r.pop(R+1)
   R-=1
  j=r[L];k=r[R]
  if A(j)*A(k):
   if L!=R and j&31==k&31:
    r[L]+=(j!=k)*64;r[R]=1
    if v():r[R]+=r.pop(R+1)
   else:r[L]|=128;r[R]|=128
  L+=1;R-=1
 while r[-1]<16:r.pop()
 return r
def d(s):
 r='';t=[]
 for o in s:
  if 15<o<32:r+=' ';o-=16
  while 0<o<16:r+=chr(t.pop());o-=1
  if o==0:continue
  if 127<o<192:o-=64;t+=[o^32]
  elif o>192:o-=128
  elif A(o):t+=[o]
  r+=chr(o)
 while t:r+=chr(t.pop())
 return r

ผล

'tacocat' <==> 'tac\xef'
4/7 (3 bytes saved)
'toohottohoot' <==> 'toohot'
6/12 (6 bytes saved)
'todderasesareddot' <==> 'todderas\xe5'
9/17 (8 bytes saved)
'amanaplanacanalpanama' <==> 'amanaplana\xe3'
11/21 (10 bytes saved)
'wasitacaroracatisaw?' <==> 'wasita\xe3ar\xef\x09?'
12/20 (8 bytes saved)
'Bob' <==> '\x82\xef'
2/3 (1 bytes saved)
'IManAmRegalAGermanAmI' <==> 'I\x8d\xa1n\x81m\x92e\xa7\xa1\xec'
11/21 (10 bytes saved)
'Dogeeseseegod' <==> '\x84ogees\xe5'
7/13 (6 bytes saved)
'A Santa at NASA' <==> 'A S\xa1\xaeta\x12\x14'
9/15 (6 bytes saved)
"Go hang a salami! I'm a lasagna hog." <==> "\x87o hang a salam\xa9!\x11'\x01\x11\x17\x13."
24/36 (12 bytes saved)

และเป็นโบนัสมันช่วยประหยัด 6 ไบต์ใน palindrome ที่ไม่ถูกต้องที่ฉันเคยมีมาก่อน

'wasita\xe3ar\xef\x02\xf2\x06?' <==> 'wasitacaroraratisaw?'
6 bytes saved

คำอธิบาย

การบีบอัดใช้สแต็ก Codepoints จาก 32-127 ได้รับการปฏิบัติอย่างแท้จริง หากตัวละครเป็นตัวอักษรค่าจะถูกผลักลงบนสแต็กเช่นกัน ค่า 128-192 ใช้สำหรับตัวอักษรตัวพิมพ์เล็กดังนั้นตัวพิมพ์เล็ก ( o^32เนื่องจากวิธีการวาง ASCII) วางลงบนสแต็กและตัวอักษรปกติจะถูกเพิ่มลงในสตริง ค่า 192-255 ใช้เพื่อเพิ่มตัวอักษรโดยไม่ต้องกดไปที่สแต็กดังนั้นจึงใช้เมื่อตัวอักษรไม่ตรงกันและสำหรับตัวอักษรกลางใน palindromes ความยาวคี่ Codepoints 1-15 ระบุว่าสแต็กควรถูกตอกจำนวนครั้งนั้น Codepoints 17-31 จะคล้ายกัน แต่จะพิมพ์ช่องว่างก่อนที่จะ popping จากสแต็ก นอกจากนี้ยังมีคำสั่ง "ป๊อปจนกระทั่งว่าง" ที่ส่วนท้ายของอินพุต

คอมเพรสเซอร์ทำงานจากปลายทั้งสองและพับในตัวอักษรที่ตรงกันเป็นค่า 1-31 มันข้ามไปที่ไม่ใช่ตัวอักษร เมื่อตัวอักษรตรงกับตัวพิมพ์ใหญ่ แต่ตัวพิมพ์เล็กไม่ตรงกันจะเพิ่ม 64 ตัวอักษรซ้ายและเพิ่มตัวอักษรขวา ทำให้สามารถประหยัดพื้นที่IManAmRegalAGermanAmIได้ ที่ตรงกลางหรือเมื่อตัวอักษรไม่ตรงกันมันจะ 128 หรือทั้งสองด้าน left == rightฉันไม่สามารถเพิ่มมีเพราะผมจำเป็นต้องหลีกเลี่ยงกรณีพิเศษที่ เมื่อพับป๊อปมาร์กเกอร์ที่อยู่ใกล้เคียงทางด้านขวาฉันต้องตรวจสอบว่าหนึ่งเพื่อนบ้านนั้นจะไม่ล้นใน codepoint 16 เพราะฉันต้องการที่ว่าง (นี่ไม่ใช่ปัญหาสำหรับสตริงกรณีทดสอบใด ๆ )

แก้ไข 1 : ไม่มีเวอร์ชันที่ไม่อัปโหลดอีก


1

Python3, 1.833 (บันทึก 25 ไบต์, 186 ไบต์โปรแกรม)

การเข้ารหัสเอนโทรปีเท่ากับความน่าจะเป็นแบบง่าย ๆ ไม่มีการปรับให้เหมาะสมกับ palindrome

def C(s):
    u=0
    for c in s:u=u*96+ord(c)-31
    return u.to_bytes((u.bit_length()+7)//8,'big')
def D(a):
    u,s=int.from_bytes(a,'big'),''
    while u:s,u=s+chr((u%96)+31),u//96
    return s[::-1]

0

Java 8, คะแนน: 1.355 (บันทึก 20 ไบต์ / 218 (107 + 111) ไบต์)

ฟังก์ชันการบีบอัด (มีอักขระ ASCII ที่ไม่สามารถพิมพ์ได้สามตัว):

s->{int l=s.length();return s.contains(new StringBuffer(s).reverse())?s.substring(l/2)+(l%2<1?"":""):s;}

ฟังก์ชั่น Decompress (มีอักขระ ASCII ที่ไม่สามารถพิมพ์ได้สองตัว):

s->{return s.contains("")?new StringBuffer((s=s.replaceAll("","")).substring(s.length()&1^1)).reverse()+s:s;}

คำอธิบาย:

ลองออนไลน์

บีบอัด palindromes ที่สมบูรณ์แบบเท่านั้น

s->{                      // Method with String as both parameter and return-type
  int l=s.length();       //  Get the length of the input
  return s.contains(new StringBuffer(s).reverse())?
                          //  If the input is a palindrome:
    s.substring(l/2)      //   Only return the second halve of the String
    +(l%2<1?"":"")        //   + either one (if even) or two (if odd) unprintables 
   :                      //  Else:
    s;}                   //   Simply return the input again

s->{                      // Method with String as both parameter and return-type
  return s.contains("")?  //  If the input contains an unprintable:
    new StringBuffer((s=s.replaceAll("",""))
                          //   Remove the unprintables
                     .substring(s.length()&1^1))
                          //   And take either the full string (if even),
                          //   or minus the first character (if odd)
    .reverse()            //    And reverse that part
    +s                    //   And append the rest of the input (minus the unprintables)
   :                      //  Else:
    s;}                   //   Simply return the input again
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.