Szyfr Cezara - ocena rozwiązania

0

Jak ocenicie takie rozwiązanie? Da się to zrobić prościej (bardziej intuicyjnie)?

public String encode(String enc, int offset) {
	     offset %= 26;
	     
	     StringBuilder result = new StringBuilder();
	     
	     for (int i = 0; i < enc.length(); i++) {
	    	 result.append((char) ((enc.charAt(i) % 97 + offset) % 26 + 97));
	     }
	     
	     return result.toString();
	 }
	 
	 public String decode(String enc, int offset) {
	     offset %= 26;
	     
	     StringBuilder result = new StringBuilder();
	     
	     for (int i = 0; i < enc.length(); i++) {
	    	 result.append((char) ((enc.charAt(i) % 97 - offset) % 26 + 97));
	     }
	     
	     return result.toString();
	 }
2
  1. StringBuilder nie jest Ci potrzebny. Kompilatory od wielu lat optymalizują operator konkatenacji,
  2. Nie używaj magicznych liczb - nazwij je,
  3. Offset osobiście nazwałbym key, skoro to szyfrowanie,
  4. enc to skrót od encefalograf? Nie używaj skrótów.
1
	public final int NUMBER_OF_LETTERS_IN_ALPHABET = 'z' - 'a' + 1 ;
	public final int LETTER_VALUE = 'a';
	
	 public String encode(String message, int key) {
		 key %= NUMBER_OF_LETTERS_IN_ALPHABET;
	     
		 StringBuilder result = new StringBuilder();
		 
	     for (int i = 0; i < message.length(); i++) {
	    	 result.append((char) ((message.charAt(i) % LETTER_VALUE + key) % NUMBER_OF_LETTERS_IN_ALPHABET + LETTER_VALUE));
	     }
	     
	     return result.toString();
	 }
	 
	 public String decode(String message, int key) {
	     key %= NUMBER_OF_LETTERS_IN_ALPHABET;
	     
	     StringBuilder result = new StringBuilder();
	     
	     for (int i = 0; i < message.length(); i++) {
	    	 result.append((char) ((message.charAt(i) % LETTER_VALUE - key) % NUMBER_OF_LETTERS_IN_ALPHABET + LETTER_VALUE));
	     }
	     
	     return result.toString();
	 }

Coś takiego? (Konwencja do poprawy, bardziej chodzi mi o zamysł)

2

Podejście dobre, i nic raczej nie ulepszysz, oprócz czytelności, użyciu operatora +, oraz można by atakować to Stream Api

Do matury spokojnie wystarczy, idź ogarniaj MS Access :D

EDIT:** I potem kierunek studiów który respektuje maturę z infy, lub skup się na majcy.**

0

StringBuildera oczywiście zostaw. Kompilator ogarnie proste konkatenacje, ale już tego co napisałeś - w wersji ze Stringiem już nie. A przynajmniej tak mówi dekompilator w IntelliJ.

1

Trochę lepsza mutacja:

http://ideone.com/p8VSJY

  • sprawdzenie czy możemy szyfrować znak
  • wyeliminowany append
  • weliminowane liczby magiczne
  • poprawione wyrażenie szyfrujące
2

Skąd macie informacje, że kompilator zoptymalizuje? U mnie (Java 1.8.111) pętla

        StringBuilder result = new StringBuilder("");
        for (int i=1;i<=100000;i++)
        {
            result.append("A");
        }

wykonuje się około 10 tys. razy szybciej niż pętla

        String result = "";
        for (int i=1;i<=100000;i++)
        {
            result+="A";
        }
0
bogdans napisał(a):

Skąd macie informacje, że kompilator zoptymalizuje? U mnie (Java 1.8.111) pętla

        StringBuilder result = new StringBuilder("");
        for (int i=1;i<=100000;i++)
        {
            result.append("A");
        }

wykonuje się około 10 tys. razy szybciej niż pętla

        String result = "";
        for (int i=1;i<=100000;i++)
        {
            result+="A";
        }

W pętli kompilator nie jest w stanie zoptymalizować i nie zamienia konkatenacji na StringBuildera. Poza pętlami w zdecydowanej większości raczej tak, jednakże poleganie na tym co może zrobić kompilator, a co nie jest zdecydowanie słabe.

1

A teraz ja dorzucę swoje 3 grosze. Rozwiązanie może i jest OK, ale nie uwzględnia kilku rzeczy i nie wykorzystuje elementów Javy 8.

  • nulle i null checki. Mała rzecz, a cieszy.
  • enc.charAt(i) zamień na toCharArray() i dalej na Stream.
  • nie uwzględniasz znaków diakrytycznych, ale da się bez tego przeżyć.
0

Rozwiązanie @vpiotr jest najlepsze. Tutaj wszystkich przepraszam za niedoprecyzowanie.
Na wejściu dostaję plik tekstowy z wyrazami do zaszyfrowania i przesunięciem. Nie ma opcji na nulle (tzn. jest, ale takie zadania z automatu nie są brane pod uwagę, tak było z kolejnym podpunktem zadania), wydajność przy paru tysięcy wyrazów nie gra wielkiej roli.
@Koziołek nie znam jeszcze api streamów, więc to troszkę odpada, zwłaszcza, że miałem problemy z implementacją :/
Znaki diakrytyczne się nie pojawiają, więc kolejny problem z głowy ;)
Dziękuję wszystkim obecnym w wątku za pomoc :)

0

Ja chciałbym poprzeć podpowiedz @Koziołek i wprowadzić lekką dygresję.
Kiedy uczyłem się Javy w 2014 roku, nasz nauczyciel nie wprowadzał Javy 8.
Odbiło się to potężnie na mojej dalszej nauce tego języka. Oczywiście niekorzystnie. Nabrałem "starych" nawyków. Nie dotknąłem przez to paradygmatu funkcyjnego. Nawet tak okrojonego jak w Javie 8.

Dopiero po skończonym semestrze musiałem uczyć się bardzo dużej ilości zmian i zmienić często tok myślenia. Uważam, że powinno byc obowiązkiem obcowanie z Javą 8 od początku nauki Javy, od pierwszych linijek kodu.

1
InterruptedException napisał(a):

Ja chciałbym poprzeć podpowiedz @Koziołek i wprowadzić lekką dygresję.
Kiedy uczyłem się Javy w 2014 roku, nasz nauczyciel nie wprowadzał Javy 8.
Odbiło się to potężnie na mojej dalszej nauce tego języka. Oczywiście niekorzystnie. Nabrałem "starych" nawyków. Nie dotknąłem przez to paradygmatu funkcyjnego. Nawet tak okrojonego jak w Javie 8.

Byłeś w takiej samej sytuacji, jak wszyscy pracujący w Javie do 2014, a nawet dużo lepszej, bo byłeś stosunkowo na świeżo i praktycznie żadnych nawyków nie miałeś.

1
InterruptedException napisał(a):

Ja chciałbym poprzeć podpowiedz @Koziołek i wprowadzić lekką dygresję.
Kiedy uczyłem się Javy w 2014 roku, nasz nauczyciel nie wprowadzał Javy 8.
Odbiło się to potężnie na mojej dalszej nauce tego języka. Oczywiście niekorzystnie. Nabrałem "starych" nawyków. Nie dotknąłem przez to paradygmatu funkcyjnego. Nawet tak okrojonego jak w Javie 8.

Dopiero po skończonym semestrze musiałem uczyć się bardzo dużej ilości zmian i zmienić często tok myślenia. Uważam, że powinno byc obowiązkiem obcowanie z Javą 8 od początku nauki Javy, od pierwszych linijek kodu.

Nie mam na to teraz czasu - jest jeszcze milion innych rzeczy do nauki do matury. Jest na liście "na po maturze" ;).
Jak paręnaście innych tematów :D
Chwilowo dla mnie jest najważniejsze to, żeby działało i wyglądało w miarę ok. To co zaprezentowali inni to wystarcza mi całkowicie, zwłaszcza, że sporo z tego co tworzę to sprawdzanie czy implementacja robi to co powinna, a pomogło w tym np. zastąpienie liczby 97 zmienną (usunięcie hardcodowania). Jeszcze raz dzięki wszystkim ;)

0
datdata napisał(a):

Byłeś w takiej samej sytuacji, jak wszyscy pracujący w Javie do 2014, a nawet dużo lepszej, bo byłeś stosunkowo na świeżo i praktycznie żadnych nawyków nie miałeś.

No właśnie a mogłem nie być. Zaczynający w 2014 roku miałem dużą szansę od początku zasysać mleko okraszone javą 8. Wstarczyło aby od początku pokazał streamy, a nie jedynie bardzo trudny javovy iterator for(;;;) i bardziej ludzki for each.

0

@InterruptedException: ale java 8 to nie tylko "pokazywanie" streamów. Tam jest zdecydowanie więcej rzeczy do ogarnięcia i znacznie więcej teorii, z którą osoba początkująca będzie miała problem.

0

moja implementacja do ocenki

import java.io.*;
import java.util.Scanner;
import java.util.StringTokenizer;

/**
 * Created by Gladus on 23.04.2018.
 */
public class Main {
    public final int NUMBER_OF_LETTERS_IN_ALPHABET = 'Z' - 'A' + 1;
    public final int END_OF_LETTERS_IN_ALPHABET = 'Z';
    public final int BEGINING_OF_LETTERS_IN_ALPHABET = 'A';

    public String encode(String enc, int offset) {
        offset %= NUMBER_OF_LETTERS_IN_ALPHABET;

        String result = "";
        int dlugosc;
        for (int i = 0; i < enc.length(); i++) {

            if (enc.charAt(i) + offset > END_OF_LETTERS_IN_ALPHABET) {
                dlugosc = offset - (offset - enc.charAt(i));


            } else {
                dlugosc = enc.charAt(i) + offset;
            }
            result += ((char) (dlugosc));


        }

        return result.toString();
    }

    public String decode(String enc, int offset) {
        offset %= NUMBER_OF_LETTERS_IN_ALPHABET;

        String result = "";
        int dlugosc;
        for (int i = 0; i < enc.length(); i++) {

            if (enc.charAt(i) - offset < BEGINING_OF_LETTERS_IN_ALPHABET) {
                dlugosc = END_OF_LETTERS_IN_ALPHABET - offset + (enc.charAt(i) - BEGINING_OF_LETTERS_IN_ALPHABET) + 1;

            } else {


                dlugosc = enc.charAt(i) - offset;
            }
            result += ((char) (dlugosc));

        }

        return result.toString();
    }

    public static void main(String[] args) throws IOException {

        Scanner sc = null;

        sc = new Scanner(new File("dane_6_2.txt"));
        String valueOfText, text;
        BufferedWriter writer = new BufferedWriter(new FileWriter("Wynik_6_2.txt"));
        String pies = "";
        StringTokenizer token;
        System.out.println(pies);
        while (sc.hasNextLine()) {
            token = new StringTokenizer(sc.nextLine(), " ");
            text = token.nextToken();


            try {
                valueOfText = token.nextToken();
                int key = Integer.parseInt(valueOfText);
                pies = new Main().decode(text, key);
                System.out.println(pies);
            } catch (Exception e) {
                continue;
            }


            writer.write(pies);
            writer.newLine();


        }

        writer.close();
        sc.close();
    }


}




0

Raczej nie pomoże tylko zaszkodzi.

  • nazwy zmiennych (np. "dlugosc", "enc")
  • bardzo dziwna logika odejmowania (zakładam że działa?)
  • brak użycia StringBuilder
  • konwersja string na string:
String result = "";
...
return result.toString();
1
The Best napisał(a):

moja implementacja do ocenki

Klasa **Main ** czyli:
główny
magistralny
najważniejszy
centralny
czołowy
pryncypalny
środkowy

Czyli klasa od programu który implementuje (...) nazywa oznacza "główny..".

Dalej jedziemy... Mamy klasę **Main ** - czyli główną, a w niej metodę main() **** - czyli główną metodę głównej klasy... - jak na razie brzmi sensownie, logicznie i nazwy mówią same za siebie o co kaman. A w metodzie main() mamy kod:

pies = new Main().decode(text, key);

czyli de facto nie tworzymy instancji (obiekt) tej klasy... ..yyyyy nieee, jednak tworzymy, a z tego co wyjdzie (obiekt) na nim wywołujemy metodę .decode(), która nam coś zwraca.

A może lepiej, czytelniej i tak aby się trzymało to kupy, zamiast tworzyć obiekt bez przypisywania go do zmiennej, napisać tak:

  1. Nazwać klasę **Main **-> Start (lub App). Będzie to klasa którą się aplikację uruchamia. W tej klasie będzie metoda main()
  2. Stworzyć klasę dodatkową CezarCipherImpl.java - że jak nazwa wskazuje tam jest implementacja szyfratora szyfru Cezar'a.
  3. Wewnątrz tej klasy stworzyć metodę m.in. decodeString(). Czyli metoda jak nazwa wskazuje koduje jakiegoś String'a którego przekażemu w argumencie.

Start.java {

 import com.theBest.ciphers,CezarCipherImpl;

public static void main(String[] args) {
...
String plainText = "ala ma kota";

pies = CezarCipherImpl.decode( plainText, key);
...

}

CezarCipherImpl.java {

public static String decode(String plainText, int offset) {
...
}

}

Tutaj co ważne, metoda decode() jest statyczna. Czyli faktycznie nie potrzebujesz tworzyć obiektu z całej klasy CezarCipherImpl tylko po prostu używasz metody, której implementacja jest wewnątrz definicji klasy.

To samo tyczy się stałych zdefiniowanych w tej klasie:

public **static **final int NUMBER_OF_LETTERS_IN_ALPHABET = 'Z' - 'A' + 1;
public static final int END_OF_LETTERS_IN_ALPHABET = 'Z';
public **static **final int BEGINING_OF_LETTERS_IN_ALPHABET = 'A';

Poza tym tak jak przedmówca napisał - konwencje nazewnicze. Albo umawiamy się, że kodujemy w języku polskim, albo w angielskim bądź jeszcze innym. A jeżeli tak, to wszystko nazywamy w tym własnie języku.

Nie **dlugosc **i nie enc tylko **length **i enc. A samo enc również ... może jednak warto zastąpić plainText, który przynajmniej wiadomo co oznacza
Bo enc może oznaczać:
encrypted
encrypt
i diabli wiedzą co jeszcze. A każdy z tych wyrazów NIE oznacza "nie zaszyfrowanego tekstu" badź też "tekst jawny".

Konkatencja Stringów. Nie wiem czy wiesz, ale "dodawanie" (czyli łączenie) wartości łańcucha znaków w Javie nie przebiega tak różowo jak tobie się wydaje.
Odbywa się to na zasadzie kązdorazowego tworzenia nowego obiektu klasy String.

wynik += AAA + BBB
czyli:

wynik = wynik + AAA + BBB

wykonywany w pętli będzie powodował coś takiego:

wykonanie takiego pseudo kodu po kolei:

new String(AAA) + new String (BBB)
new String(AAABBB) + new String(AAABBB)
new String(AAABBBAAABBB)
String wynik = new String(AAABBBAAABBB)

Czyli w każdym przebiegu pętli 6 razy użwany jest konstruktor z klasy String, który tworzy nowy obiekt.
Z kolei ta pętla wywoływana jest u ciebie w ilości: 6 * ilość znaków w tekście jawnym. Czyli... dużo. A to jest tylko jedna linijka kodu pętli.

A więc tak jak przedmówca napisał, zainteresuj się klasami StringBuffer i StringBuilder. Bo one własnie są do tego, aby wartości Stringów do siebie "dodawać".
Zainteresuj się obiema tymi klasami, a jak przeczytasz ich dokumentacje to jest tam wyjaśniona jaka jest między nimi różnica i dlaczego czasami używać warto jednej, a w innych przypadkach durgiej.
Tak na marginesie dodam, że jaka jest różnica między tymi klasami może pojawić się na rozmowie kawalifikacyjnej na junior developera. A przynajmniej dawniej takie pytania się pojawiały.

result += ((char) (dlugosc));

StringBuilder sb = new StringBuilder();

sb.append( (char) dlugosc ); // dlugosc -> someThingLength

return sb.toString(); // i tutaj akurat .toString() jest konieczna i ma rację bytu.

p.s.
A na koniec nieśmiało zapytam co oznacza bądź co ma oznaczać w kodzie zmienna pies? :D

pozdrawiam i idę spać, bo z rana do roboty....

0

@Kaczor(nie wiem jak odpowiedzieć bez całej wiadomości ) Po przeczytaniu początku długo najpierw wybuchłem śmiechem potem było mi tak głupio aby dalej czytać że zwlekałem z tym aż tyle czasu nie mniej dzięki mistrzu wszystkie twoje rady biorę do serca i mam nadziej że moje cuda też jakkolwiek cię rozbawiły :P Założyłem sobie zeszyt i spisuje wszystkie uwagi które staram się stosować. Wielkie dzięki za pomoc . W sumie od 20 maja planuje minimum raz w tygodniu regularnie dawać kod do oceny . Widziałem tą akcje z tymi kotamiProgramowania myślę czy też czegoś podobnego nie zrobić . Może Założę jakiś tag na moje posty. Może kogoś będą interesował moje postępy.

1

Co do nazwy zmiennej "pies" jak jest to tylko i wyłącznie jakaś tam zmienna tymczasowa, to lepiej napisać "temp" bądź "tmp" - czyli temporary

0
Bogaty Wąż napisał(a):

Co do nazwy zmiennej "pies" jak jest to tylko i wyłącznie jakaś tam zmienna tymczasowa, to lepiej napisać "temp" bądź "tmp" - czyli temporary

I to mówiłem znowu ja - Świetny Kaczor (a tak na prawdę dawciobiel)

1
The Best napisał(a):

@Kaczor(nie wiem jak odpowiedzieć bez całej wiadomości ) ... Założyłem sobie zeszyt i spisuje wszystkie uwagi które staram się stosować. ...

Zeszyt zeszytem, ale większość uwag które zapisujesz ktoś już kiedyś spisał. Np.:
https://pl.wikipedia.org/wiki/SOLID_(programowanie_obiektowe)
czy
https://pl.wikipedia.org/wiki/Wzorzec_projektowy_(informatyka)

Są też książki w tym temacie:
https://allegro.pl/listing?string=clean%20code&order=m&bmatch=ss-base-relevance-floki-5-nga-hcp-wp-eng-cul-1-2-0427

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.