บีบอัดข้อมูลด้วยไวยากรณ์ที่ไม่มีบริบท


9

เป็นไปได้ที่จะบีบอัดข้อมูลบางประเภทเช่นข้อความของมนุษย์หรือซอร์สโค้ดด้วยไวยากรณ์แบบเส้นตรง โดยทั่วไปคุณสร้างไวยากรณ์ที่ภาษามีคำเดียว - ข้อมูลที่ไม่ได้บีบอัด ในงานนี้คุณต้องเขียนโปรแกรมที่ใช้วิธีการประมวลผลข้อมูลนี้

อินพุต

อินพุตเป็นสตริงที่มีความยาวไม่เกิน 65535 ไบต์ มีการประกันว่าอินพุตนั้นตรงกับนิพจน์ทั่วไป[!-~]+(เช่นอย่างน้อยหนึ่งตัวอักษร ASCII ที่พิมพ์ได้ซึ่งไม่รวมช่องว่าง)

ตัวอย่างอินพุตคือ

abcabcbcbcabcacacabcabab

เอาท์พุต

เอาท์พุทเป็นชุดของกฎที่เป็นไวยากรณ์ที่อธิบายว่าหนึ่งคำ (อินพุต) แต่ละ nonterminal จะแทนด้วยตัวเลขทศนิยมมากกว่า 9 สัญลักษณ์เริ่มต้นคือหมายเลขสัญลักษณ์สิบ เอาต์พุตตัวอย่างที่สอดคล้องกับอินพุตตัวอย่างได้รับด้านล่าง ไวยากรณ์ของมันจะอธิบายเพิ่มเติมด้านล่าง:

10=11 11 12 12 11 13 13 11 14 14
11=a 12
12=b c
13=a c
14=a b

แต่ละกฎมีรูปแบบที่<nonterminal>=<symbol> <symbol> ...มีจำนวนของสัญลักษณ์ที่คั่นด้วยช่องว่างโดยพลการทางด้านขวา แต่ละเอาต์พุตที่ปฏิบัติตามข้อ จำกัด ต่อไปนี้และได้รับสตริงอินพุตที่ถูกต้อง

ข้อ จำกัด

เพื่อที่จะหยุดคนไม่ให้ทำสิ่งที่แปลกประหลาดมีข้อ จำกัด มากมายเกิดขึ้น:

  • แต่ละ nonterminal ต้องปรากฏอย่างน้อยสองครั้งทางด้านขวาของกฎ ตัวอย่างเช่นไวยากรณ์ต่อไปนี้สำหรับอินพุตไม่abcabcถูกต้องเนื่องจากกฎ 12 ปรากฏเพียงครั้งเดียว:

    10=12
    11=a b c
    12=11 11
    
  • ไม่มีลำดับของสัญลักษณ์สองตัวที่อยู่ติดกันอาจปรากฏมากกว่าหนึ่งครั้งในด้านขวามือของกฎทั้งหมดยกเว้นว่ามีการทับซ้อนกัน ตัวอย่างเช่นไวยากรณ์ต่อไปนี้สำหรับอินพุตไม่abcabcbcถูกต้องเนื่องจากลำดับbcปรากฏสองครั้ง:

    10=11 11 b c
    11=a b c
    

    ไวยากรณ์ที่ถูกต้องจะเป็น:

    10=11 11 12
    11=a 12
    12=b c
    
  • โปรแกรมของคุณจะต้องสิ้นสุดในเวลาน้อยกว่าหนึ่งนาทีสำหรับแต่ละอินพุตที่ถูกต้องที่มีความยาวไม่เกิน 65535 ไบต์

  • ตามปกติคุณจะไม่สามารถใช้สิ่งอำนวยความสะดวกใด ๆ ในภาษาของคุณหรือฟังก์ชั่นห้องสมุดใด ๆ ที่ทำให้การแก้ปัญหาเล็กน้อยหรือใช้ส่วนใหญ่ของมัน

ตัวอย่างอินพุต

สร้างอินพุตตัวอย่างด้วยโปรแกรม C ต่อไปนี้

#include <stdlib.h>
#include <stdio.h>

int main(int argc, char **argv) {
  unsigned int i,j = 0,k;

  if (argc != 3
     || 2 != sscanf(argv[1],"%u",&i)
      + sscanf(argv[2],"%u",&k)) {
    fprintf(stderr,"Usage: %s seed length\n",argv[0]);
    return EXIT_FAILURE;
  }

  srand(i);

  while(j < k) {
    i = rand() & 0x7f;
    if (i > 34 && i != 127) j++, putchar(i);
  }

  return EXIT_SUCCESS;
}

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

เกณฑ์การชนะ

นี่คือรหัสกอล์ฟ โปรแกรมที่มีซอร์สโค้ดสั้นที่สุดชนะ สำหรับเครดิตพิเศษให้เขียนโปรแกรมที่สร้างอินพุตจากเอาต์พุตอีกครั้ง


ฮ่า. ฉันมีกี่รุ่นนี้นำมาใช้ ( แต่ไม่แข็งแรงเล่นกอล์ฟ) ใน Java สำหรับKolmogorov ซับซ้อนคำถาม ...
ปีเตอร์เทย์เลอร์

@PeterTaylor คำถามอะไรกันแน่?
FUZxxl

ไม่จำเป็นต้องหาคำตอบสั้น ๆ พอที่จะเป็นการโพสต์ที่คุ้มค่า (ฉันกำลังเพิ่มกลยุทธ์การสร้างไวยากรณ์และเอ็นจินไวยากรณ์ช้า) แต่สคริปต์หลักในโครงการนั้นลองใช้พวกเขาในcodegolf.stackexchange.com/questions/1682 , codegolf .stackexchange.com / คำถาม / 6043 , codegolf.stackexchange.com/questions/4191 , codegolf.stackexchange.com/questions/4356และองค์ประกอบอื่น ๆ ของคำถามอื่น ๆ
Peter Taylor

คำตอบ:


3

GolfScript 111ตัวอักษร 108

1/{.}{:^1<{^1$/,2>.{;,)^<.0?)!}*}do-1<.,1>{^1$/[10):10]*0+\+}{;^}if(\}while][0]%.,,]zip{))9+`"="+\~" "*+}%n*

นี่เป็นวิธีที่ค่อนข้างซุ่มซ่ามเมื่อใช้ GolfScript รุ่นที่สองทำงานได้ดีกว่ารุ่นเริ่มต้นมาก มันยาวกว่ารหัสที่ตั้งใจไว้มาก แต่การใช้งานของฉันมีลูปซ้อนกันและสิ่งนี้ทำให้เกิดปัญหากับล่าม

ตัวอย่าง:

> abcba
10=a b c b a

> abcabcbc
10=11 11 12
11=a 12
12=b c

> abcabcbcbcabcacacabcabab
10=11 12 12 13 14 14 c 11 15
11=15 13
12=c b
13=14 b
14=c a
15=a b

1

หดกลับ - อัลกอริทึมไม่สามารถจัดการได้ทุกกรณี C, 422 (คงที่เพื่อลบ dups ในเอาต์พุตและอักขระดร็อป)

การติดตั้งครั้งแรกจะเริ่มเล่นกอล์ฟ

เนื่องจากกฎไม่จำเป็นต้องบีบอัดเดรัจฉานกำลังจริงวิธีการนี้จะทำ ...

สามารถประมวลผลความยาว 65535 ภายใน 10 วินาที

n,m[99999];
c,r[99999][2];

g,i,s,t;

main(){
    for(;(m[n]=getchar())>32;n++);

    while(!g){ // loop until no further changes
        g=1;
        for(s=0;s<n-1;s++) {
            for(t=s+2;t<n-1;t++)if(m[s]==m[t]&&m[s+1]==m[t+1]){
                // create rule
                r[c][0]=m[s];
                r[c++][1]=m[s+1];
                g=0;
                // substitute
                for(i=t=s;i<n;i++){
                    if(m[i]==r[c-1][0]&&m[i+1]==r[c-1][1]){
                        m[t++]=-c;
                        i++;
                    }else
                        m[t++]=m[i];
                }
                n=t;
            }
        }
    }

    for(s=-1;s<c;s++){
        printf("%d=",s+11);
        for(t=0;t<(s<0?n:2);t++){
            i=(s<0?m:r[s])[t];
            i<0?printf("%d ",10-i):printf("%c ",i);
        }
        printf("\n");
    }

}

วิ่งตัวอย่าง:

echo abcabcbcbcabcacacabcabab | a.out
10=11 12 13 13 12 14 14 12 12 11 
11=a b 
12=c 11 
13=c b 
14=c a


รหัสของคุณไม่ทำงานตามข้อกำหนด มันสร้างผลลัพธ์ที่ละเมิดกฎลำดับของตัวละครทั้งสองไม่อาจปรากฏขึ้นเป็นครั้งที่สอง ; พิจารณาอินพุต abcdabcd นอกจากนี้โค้ดของคุณจะลบไบต์สุดท้ายออกจากสตรีมอินพุต ดูตัวอย่างการสังเกตผลกระทบทั้งสองนี้ที่นี่: ideone.com/3Xvtyv
FUZxxl

นอกจากนี้ผลลัพธ์ตัวอย่างของคุณก็ไม่ถูกต้องเช่นกัน
FUZxxl

คุณพูดถูก - ฉันล้มเหลว - ฉันจะดูมันเมื่อฉันกลับมาทำงาน: P
baby-rabbit

มันไม่ได้ลบไบต์สุดท้ายออกจากอินพุตสำหรับฉัน - และผลลัพธ์ตัวอย่างของฉันถูกต้อง (สำหรับฉัน) .. มาเล่น "spot the bug" กันเถอะ!
baby-rabbit

ผลลัพธ์ตัวอย่างที่คุณโพสต์แน่นอนคือ รูปแบบที่ขยายออกของกฎ 10 จบลงด้วยกฎ 14 ซึ่งจะลงท้ายด้วย "ca" c สุดท้ายจริง ๆ แล้ว 5 ตำแหน่งก่อนสิ้นสุด
FUZxxl
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.