Brak pomysłu na kompresję RLE łańcucha

0

Witam, mam problem z kompresja RLE. Mianowicie nie mam pomysłu jak "zczytywać" kolejne literki. Nie wiem czy robic to po jednej literce czy jak ? Czy może jest do tego jakaś specjalna funkcja ? Proszę o pomoc. Nie chce gotowego programu a tylko pomocy w napisaniu. Z góry dziękuje za pomoc.

0

Wczytaj do stringa? :|
W ogóle to nie rozumiem, o co Ci chodzi.

0

Po prostu zacznij pisać i jak z czymś będziesz miał problem, to wtedy przyjdź do nas z konkretnym problemem.

0

@kamil9499 - musisz napisać coś więcej na temat tego Twojego programu; Póki co nawet nie wiemy co chcesz zrobić (w czym to zastosować), a już nie mówiąc o jakimś początkowym kodzie;

Przede wszystkim opisz dokładniej to co chcesz zrobić, a także napisz jakąś ramę kodu; Jak będziesz miał z czymś konkretnym problem, to wtedy daj znać tutaj.

0

Mam porównać dwie kompresje. Ale nie bardzo wiem jak mam się zabrać za to po wczytaniu ciągu. Mając np. ciąg AAANNNNTTTKKKK mam wczytać pierwszą literkę a potem sprawdzać następne dopóki natrafię na inną ?

3

Algorytm jest opisany na wielu stronach, wystarczy poszukać np. na wiki http://pl.wikipedia.org/wiki/RLE lub http://www.algorytm.org/algorytmy-kompresji/algorytm-rle-run-length-encoding.html

Stringi w pascalu mają bardzo prostą budowę - zachowują się jak tablice znaków, indeksowane są od 1, a do poszczególnych liter można odwoływać się jak do tablic np. mając taki napis s := 'Ala ma kota'; chcąc pobrać 5 literkę możesz zrobić to tak znak := s[5]; od teraz zmienna znak będzie przechowywać piąty znak czyli literę 'm'. Po więcej odsyłam do kursu podstaw języka bo nie mam zamiaru tu streszczać książek.

Wracając do algorytmu... Na początku szczególny przypadek gdy jest tylko 1 znak - wtedy go przepisujesz, lub wypisujesz ilość wystąpień (czyli zawsze 1) przed tym znakiem.
Dalej gdy napis ma dwa i więcej znaków szukasz podciągów. Wyszukiwanie podciągu realizujesz w pętli przez porównywanie i-tej literki z kolejną czyli i + 1 gdzie i to indeks znaku w stringu jeśli są takie same zwiększasz licznik wystąpień tego znaku jeśli różne wypisujesz ilość zliczeń oraz i-ty znak i resetujesz licznik na 1.
W ten sposób będziesz miał już wszystkie podciągi bez ostatniego, zatem poza pętlą wypisujesz licznik i ostatnią literkę napisu.

To tyle, dekompresja jest deczko trudniejsza, ale dopóki nie zrobisz kodowania nie będę się produkował na darmo.
PS Opisany algorytm nie jest najbardziej optymalny pod względem zajmowanego miejsca, ale tym możesz martwić się później - to jest to co w artykule na wiki.

3

Zarówno kompresja, jak i dekompresja to dość proste algorytmy; Co i tak nie zmienia faktu, że podstawy programowania trzeba mieć opanowane;

Najprościej jest to zrobić tak, jak poprzednik podpowiedział - zadeklarować sobie zmienną znakową, która przechowa aktualnie zliczany znak; Następnie licznik, który będzie inkrementowany i zliczane będą dane znaki; Po ich zliczeniu, do ciągu wyjściowego dodawana jest zliczana literka i jej ilość, przekonwertowana za pomocą funkcji IntToStr lub procedury Val;

W drugą stronę jest trudniej, bo oprócz wyciągania znaków, trzeba też wyciągać liczby; W przypadku ciągów zakodowanych, które posiadają tylko pojedyncze cyfry jest łatwo, trudniej jest, jeśli dany znak występuje więcej niż 9 razy; Wtedy trzeba pętelką wyciągać liczby, konwertować je z powrotem do postaci liczb za pomocą funkcji StrToInt i uzupełniać ciąg wyjściowy, dopisując do niego wartości utworzone np. za pomocą funkcji StringOfChar;

Wszystko da się zrobić, trzeba tylko przysiąść, pisać i testować kod;


Ja tam lubię przeszukiwanie wskaźnikowe, więc podam dla zainteresowanych przykład kodowania ciągów:

procedure RLEEncodeString(const AInput: AnsiString; var AOutput: AnsiString);
var
  pchrLetter, pchrToken, pchrLast: PAnsiChar;
begin
  pchrLast := @AInput[Length(AInput) + 1];
  pchrToken := @AInput[1];

  while pchrToken < pchrLast do
  begin
    pchrLetter := pchrToken;

    repeat
      Inc(pchrToken);
    until pchrToken^ <> pchrLetter^;

    AOutput := AOutput + pchrLetter^ + IntToStr(pchrToken - pchrLetter);
  end
end;

i ich dekodowania:

procedure RLEDecodeString(const AInput: AnsiString; var AOutput: AnsiString);
var
  pchrLetter, pchrCntBegin, pchrCntEnd, pchrLast: PAnsiChar;
  strCount: AnsiString;
begin
  pchrLast := @AInput[Length(AInput) + 1];
  pchrLetter := @AInput[1];

  while pchrLetter < pchrLast do
  begin
    pchrCntBegin := pchrLetter + 1;
    pchrCntEnd := pchrCntBegin + 1;

    while pchrCntEnd^ in ['0' .. '9'] do
      Inc(pchrCntEnd);

    SetLength(strCount, pchrCntEnd - pchrCntBegin);
    Move(pchrCntBegin^, strCount[1], pchrCntEnd - pchrCntBegin);

    AOutput := AOutput + StringOfChar(pchrLetter^, StrToInt(strCount));
    pchrLetter := pchrCntEnd;
  end;
end;

Działania raczej nie trzeba tłumaczyć; Test działania pod FPC tutaj - http://ideone.com/ZFcuS9

0

Napisałem porównywanie liter(na razie tych pierwszych) ale jest błąd przy liczniku, bo zawsze zwraca wartość mniejszą o jeden.

procedure porownaj;
  var
   p: integer;
   w: string; {zmienna okreslajaca wynik}
   licznik:byte;

begin
 p:=1;
 licznik:=0;
   while s[p]=s[p+1] do
    begin
     inc(p);
     w:=s[p];
     licznik:=licznik+1;  
    end;
    
    writeln('Po kompresji: ',licznik, w);
end;

dodanie znacznika <code class="delphi"> - furious programming

0

a jak mam teraz przejśc do sprawdzania kolejnych liter ? Jak mam ciag AAAKKBBBB jak juz sprawdzilem 'A' to jak teraz zacząć od 'K' ?

1

wrzuć

  licznik:=1;
   while s[p]=s[p+1] do
    begin
     inc(p);
     w:=s[p];
     licznik:=licznik+1;  
    end;
 
    writeln('Po kompresji: ',licznik, w);

w kolejną pętle, która zostanie przerwana gdy zmienna P osiągnie długość łańcucha +1 (do sprawdzenia długości łańcucha służy funkcja funkcja Length(s))

1

Co do ostatniego komentarza o ile dobrze zrozumiałem w pętli przypisywałeś do zmiennej w "aktualną" literkę i podczas ostatniego przebiegu nie była ona przypisywana. Chyba, że chodzi o coś innego bo nie wiem jak teraz wygląda twój kod. Tak to powinno mniej więcej wyglądać wg. mnie:

  p:=1;
  repeat
    licznik:=1;
    while s[p]=s[p+1] do
    begin
      inc(p);
      licznik:=licznik+1;  
    end;
    writeln('Po kompresji: ',licznik, s[p]);
    inc(p);    
  until p=length(s)+1;
0

To bedzie cos takiego ? bo length(compressed) zawsze zwraca mi 1.

procedure porownaj;

  var
   p: integer;
   licznik:integer;
   compressed: string;

begin
 p:=1;

repeat 
 licznik:=1;
 
   while s[p]=s[p+1] do
    begin
     inc(p);
     licznik:=licznik+1;  
     
    end;
     str(licznik, compressed);
      writeln(licznik, s[p]);
       write(length(compressed));
     inc(p);
    until p=length(s)+1;
    
end;

Nie wiem jak to zrobic. Próbowałem na różne sposoby ale nie udaje mi sie.

2

Nie myślisz co piszesz. Zajrzyj do jakiegoś kursu pascala bo strasznie słabo ci to idzie. Używasz funkcji, których znaczenia nie znasz Str

program ideone;

uses
  SysUtils;

var
  s: string;

procedure porownaj;
var
  p: integer;
  licznik:integer;
  compressed: string;
begin
  compressed := '';
  p:=1;
  repeat
    licznik:=1;
    while s[p]=s[p+1] do
    begin
      inc(p);
      licznik:=licznik+1;  
    end;
    compressed := compressed + IntToStr(licznik) + s[p];
    inc(p);    
  until p=length(s)+1;

  writeln('Po kompresji: ',compressed);
  writeln('Dlugosc ciagu po kompresji: ', Length(compressed));
end;

begin
  s := 'tteeeeessciiiiiiiiiwooooo';
  porownaj;
end.

1 użytkowników online, w tym zalogowanych: 0, gości: 1