Strona główna » Algorytmy » Szyfry » Szyfr Tabelka
 

Szyfr Tabelka

O szyfrze

Szyfr Tabelka polega na zamianie każdego znaku w tekście jawnym na pary cyfr. Przed szyfrowaniem ustalamy klucz - tabelkę. Tabelkę tworzy się wypisując jako nagłówki wszystkie cyfry parzyste, a jako nagłówki kolumn cyfry nieparzyste. Każde pole z 25 w tabelce wpisujemy jeden znak. Potem przy szyfrowaniu znaku szukamy go w tabelce i odczytujemy nagłówek wiersza oraz kolumny. Teksty nagłówków zapisane koło siebie tworzą parę cyfr, która odpowiada znakowi do zaszyfrowania. Przyjmując, że nagłówek kolumny to y, a nagłówek wiersza to x to dany znak możemy zaszyfrować na dwa sposoby: jako xy lub yx.

Weźmy przykładowo tabelkę 5x5, którą wypełnimy znakami alfabetu łacińskiego (z alfabetu pomijamy literę V):

x13579
0ABCDE
2FGHIJ
4KLMNO
6PQRST
8UWXYZ

Spróbujmy teraz zaszyfrować wyraz INFORMACJA. Szukamy I. Mamy w kolumnie 4, wierszu 2. Pobieramy kolejno nagłówki: 7 i 2. Zapisujemy to jako 72 lub 27. Kolejność nie ma tu znaczenia, ponieważ w tabelce obie współrzędne pozwolą prawidłowo rozszyfrować tekst. Szyfrujemy dalej N: 74, F: 12, O: 49, R: 56, M: 54, A: 10, C: 50, J: 92, A: 01 - A występuje tutaj drugi raz, więc możemy zapisać liczbę na drugi sposób, aby trudniej było go złamać. Oczywiście ma to sens jeśli ktoś nie będzie wiedział, że używamy szyfru tabelki. Wszystkie cyfry zapisujemy koło siebie i uzyskujemy np. 27741249565410509201.

Podczas rozszyfrowania należy pamiętać, że bierzemy po dwie kolejne cyfry, które nie oznaczają pozycji, a nagłówki tabelki, więc rozszyfrowując: bierzemy kolejno 27 i odczytujemy, że to 2 wiersz i 4 kolumna. Co jeśli byśmy mieli liczbę 72? Zamieniamy je miejscami. Możemy to zrobić, ponieważ cyfry parzyste są nagłówkami wierszy, a nieparzyste kolumn.

Informacja dodatkowe

  1. Istnieje możliwość powiększenia tabelki w celu zwiększania pozycji w tabelce. Należy pamiętać jednak, że należałoby wtedy zwiększyć ilość cyfr potrzebnych do zaszyfrowania każdego znaku.
  2. W celu poprawienia skuteczności szyfrowania warto pozamieniać nagłówki wierszy i/lub nagłówki kolumn. Należy jednak pamiętać, że należy mieć w nagłówkach danego typu tylko jeden typ liczb.
  3. Każdy znak w tabelce może wystąpić raz, ale na dowolnej pozycji

Implementacja

Zadanie

Napisz program, który na wejściu otrzyma tabelkę 5x5 oraz 5 linii po 5 znaków rozdzielonych spacjami, które będą tworzyły tabelkę. Zakładamy, że i-ty wiersz ma nagłówek 2·(i - 1), a j-ta kolumna ma nagłówek 2·j - 1. Szósta linijka to tekst zaszyfrowany złożony tylko ze znaków, wprowadzonych do tabelki. Na wyjście powinien zostać wypisany zaszyfrowany tekst zgodnie z szyfrowaniem Tabelka. Przykładowo dla wejścia:

  1. A B C D E
  2. F G H I J
  3. K L M N O
  4. P Q R S T
  5. U W X Y Z
  6. INFORMACJA
otrzymamy:
  1. 27741249565410509201

Szyfrowanie

Funkcja findChar() pomoże znaleźć określony znak w tabelce i zwróci zaszyfrowany tekst.

  1. char* findChar(char** tabelka, int w, int h, char look){
  2.   char* wynik = new char[2];
  3.   for(int i = 0; i < h; i++){
  4.     for(int j = 0; j < w; j++){
  5.       if(tabelka[i][j] == look){
  6.         wynik[0] = (char)(2*i) + '0';
  7.         wynik[1] = (char)(2*j + 1) + '0';
  8.         return wynik;
  9.       }
  10.     }
  11.   }
  12.   return wynik;
  13. }

(2.) Alokujemy bufor do którego zapiszemy nagłówki tabeli. Nie ma tutaj potrzeby na dodawanie znaku końca ciągu - wiemy, że zaszyfrowany znak jest reprezentowany przez dwuznakowy wyraz. (3.) Dla każdego wiersza i (4.) każdego znaku (5.) sprawdzamy gdzie jest nasz szukany znak look. Kiedy go znajdziemy: (6., 7.) ustalamy bufor na odpowiednie wartości i (8.)go zwracamy. Jeśli nie znajdziemy znaku to przejdziemy do linijki (12). Nie ustalamy żadnej wartości bufora, ponieważ nie powinniśmy się znaleźć w tym miejscu zgodnie z wytycznymi zadania.

Główna funkcja cipher() będzie kontrolować funkcję findChar() i zapisywać jej wyniki tworząc zaszyfrowany tekst:

  1. char* cipher(char* txt, char** tabelka, int w, int h){
  2.   int dl = strlen(txt)*2;
  3.   char* wynik = new char[dl+1];
  4.   for(int i = 0; txt[i]; i++){
  5.     char* buf = findChar(tabelka, w, h, txt[i]);
  6.     wynik[2*i] = buf[0];
  7.     wynik[2*i + 1] = buf[1];
  8.     delete[] buf;
  9.   }
  10.   wynik[dl] = '\0';
  11.   return wynik;
  12. }

(1.) Funkcja przyjmuje tekst do zaszyfrowania, tabelkę oraz jej wymiary. cipher() zwróci wskaźnik na zaszyfrowany tekst. (2.) Wyliczamy długość tekstu wynikowego i (3.) alokujemy odpowiednio dużo pamięci. (4.) Dla każdego znaku wywołujemy funkcję findChar() i odczytujemy bufor. (4., 5.) Na odpowiednie pozycje przepisujemy bufor i (6.) go usuwamy. (8.) Dopisujemy znak końca tekstu zaszyfrowanego i (9.) zwracamy wynik.

Deszyfrowanie

Do deszyfrowania stworzymy funkcję pomocniczą getChar(), która pozwoli na uzyskanie odpowiedniego znaku z tabeli:

  1. char getChar(char** tabelka, int x, int y){
  2.   if(y % 2 == 0) return getChar(tabelka, y, x);
  3.   return tabelka[(x+1)/2][y/2];
  4. }

(1.) Możemy szyfrować znak na dwa sposoby, więc jeśli pierwsza "współrzędna" nie jest parzysta to zamieniamy je miejscami. (2.) zwracamy znak na prawdziwej pozycji.

Funkcja deszyfrująca jest prostsza niż szyfrująca. Zakładamy poprawność wprowadzonej tabelki i nie sprawdzamy czy nie będziemy pobierać znaków poza tabelką, więc nie potrzebujemy znać jej wielkości.

  1. char* decipher(char* txt, char** tabelka){
  2.   int dl = strlen(txt)/2;
  3.   char* wynik = new char[dl+1];
  4.   for(int i = 0; txt[i]; i+=2)
  5.     wynik[i/2] = getChar(tabelka, txt[i]-'0', txt[i+1]-'0');
  6.   wynik[dl] = '\0';
  7.   return wynik;
  8. }

(2.) Wyliczamy długość. (3.) Alokujemy pamięć pod wynik. (4.) Dla każdej pary znaków pobieramy odpowiedni znak z tabeli odczytując go przy pomocy: getChar(). (5.) Dodajemy znak końca linii i (6.) zwracamy wynik.

Testowanie funkcji

Poniższa funkcja sprawdza szyfrowanie i odszyfrowywanie na podstawie wprowadzonego klucza oraz tekstu.

  1. int main () {
  2.   const int rozmiar = 5;
  3.   char** tabelka = new char*[rozmiar];
  4.   for(int i = 0; i < rozmiar; i++){
  5.     tabelka[i] = new char[rozmiar];
  6.     for(int j = 0; j < rozmiar; j++){
  7.       cin >> tabelka[i][j];
  8.     }
  9.   }
  10.   cin.clear();
  11.   cin.sync();
  12.   char* txt = new char[128];
  13.   cin.getline(txt, 128);
  14.   char* txtc = cipher(txt, tabelka, rozmiar, rozmiar);
  15.   cout << txtc << endl;
  16.   char* txtd = decipher(txtc, tabelka);
  17.   cout << txtd << endl;
  18.   for(int i = 0; i < rozmiar; i++)
  19.     delete[] tabelka[i];
  20.   delete[] tabelka, txt, txtc, txtd;
  21.   system("pause");
  22.   return 0;
  23. }

Zadania

Zadanie 1

Zmodyfikuj kod, aby program losowo wybierał czy odczytane pary liczb szyfrujących będą zapisane w postaci (x, y) czy (y, x).

Zadanie 2

Zmodyfikuj funkcję, aby:

  1. A B C D E
  2. F G H I J
  3. K L M N O
  4. P Q R S T
  5. U W X Y Z
  6. Informacja

otrzymujemy:

  1. 27741294565410509210