37 / 51
Feb 2024

I jeszcze taki przykład 37600%36425=1175 i właśnie 1175 jest największym możliwym bokiem kwadratu. Tak więc, jak nadmieniłem, w niektórych przypadkach, trzeba będzie sprawdzić cały zakres <0,1175>

Ciekawe jest Twoje podejście (w sensie rozkmina zadania). Tak, są takie przypadki, ale stwierdzenie, że trzeba będzie sprawdzić cały zakres <0,1175> jest nieprawdziwe. Dla przypadku 37600 36425 kilkoma obliczeniami da się dojść do wartości 1175, a gdyby warunek sprawiający “korzystność” tego przypadku nie występował to i tak bym sprawdził zaledwie <2,~200> (nie znaczy, że wynik będzie w tym przedziale), ale to już wystarczająco się nagadałem. Teraz kombinuj.

Minęło kilka dni i w mojej ocenie zoptymalizowałem kod maksymalnie.
Tj. zastosowałem ulepszony alg. Euklidesa a nie klasyczny alg, a także rozpatrzyłem oddzielny przypadek dla kwadratu, by maksymalnie proces przyśpieszyć.
Tu jest mój kod:


Sędzia jest natomiast innego zdania i twierdzi, że kod nadal nie jest maksymalnie zoptymalizowany - przekroczono limit czasu. Czy mogę prosić kogoś o wskazówkę?
Z góry dziękuję!

Jest miejsce na optymalizację dla fragmentu:

    while(b%(a/counter)!=0)
        counter++;
    a = a/counter;

Dla liczby pierwszej (a=b=duża liczba pierwsza) pętla while wykona się mniej więcej a/2 razy. To jest wciąż za dużo razy.

Dzięki wielkie pawoj20 i redysz, poprawiłem i zaakceptowało mi :smiley:
Życzę powodzenia pozostałym i wytrwałości, sam bym zakwalifikował to zadanie jako średnie.

3 years later

Witam. Napisałem program do tego zadania, działa w visual studio 2019 i w codeblocks, a na Ideone dla pewnych konkretnych przypadków nie działa, nie rozumiem dlaczego. Może za słabo rozumiem rekurencję, porobiłem “Print it” i rezultat też mnie zadziwił. Oto kod, z góry dziękuję za wyjaśnienie/rozjaśnienie:) https://ideone.com/xPWmmV7

  1. Jeśli masz VS to zapewne masz też / możesz mieć łatwo debugger. Warto z niego skorzystać. Ja mam się gorzej, bo nigdy nie chce mi się ogarniać na SPOJa armat większych niż Vim i kompilator.

  2. Jeżeli argument przekazany do funkcji nie jest modyfikowany, oznaczaj go jako const. Taka drobna poprawka jakościowa i dobry nawyk na przyszłość. Np:

    NWD(const int Liczba1, const int Liczba2)
    
  3. zaprzyjaźnij się z assertem. Pomaga przy debuggowaniu na SPOJu. Aczkolwiek nie w tym przypadku - również uwaga ogólna. Na start IFy są jak najbardziej ok, by się sprawdzić i pozytywnie zaskoczyć, że kod działa. Ale z czasem zarządzanie nimi staje się męczące. I nieprofesjonalne.

  4. W tym przypadku wskazane jest, żebyś rozpisał na kartce co się dzieje w kodzie krok po kroku. Trochę to zajmie, ale łatwo namierzysz błąd. W 16 linii. Co robisz ze zwracanym wynikiem?

No tak, przecież funkcja rekurencyjna musi wrócić z zagnieżdżenia :man_facepalming: Assert jak zrozumiałem jest bardziej rozbudowaną metodą printit? A czego lepiej używać zamiast ifów ?:thinking:

"Niestety, Pan Jan nie jest mocny z matematyki …"i dla niego, kwadrat, to też prostokąt ;-). A może odwrotnie, to matematycy nie potrafią odróżnić kwadrata od prostokąta? :wink:

Jak nie musisz, nie używaj endl’a. Lepiej '\n'
Ja raczej deklaruję zmienne, przed (poza) pętlą…

IFy są ok. Chodzi mi o to, żeby zamiast robić ify w debuggu typu:

if(x == 0) cout << "Oj, x to zero!";

oraz pisać same couty upewniające Cię co do logiki w kodzie typu:

void f(int x, int y) {
   cout << "Wolam f dla " << x << " oraz " << y << endl;
   //...
}

klepać, jak już coś, ewentualne zabezpieczenia np. tak:

assert(x != 0);

Porównaj: https://ideone.com/idgbib1 z https://ideone.com/vPuShA1

Zapamiętam assert, jako bajer na przyszłość :slightly_smiling_face: Dzięki cout mogę łatwo wyświetlić wartość zmiennych, i nr linii też. Ale warto uczyć się nowych rzeczy więc skorzystam. A te ify w main to nie były do debugu, na początku pomyślałem że NWD nie działa jeśli drugi argument jest większy niż pierwszy.
A dlaczego lepsze ‘\n’ niż endl? :thinking: Co do zmiennych, w tym przykładzie akurat to nie ma znaczenia, ale często w takiej pętli “testów” zapominałem zerować jakieś zmienne i kolejne testy były nie poprawne. A tak się same zerują. Jeśli mamy zagnieżdżoną pętlę for(), to “j” w wewnętrznej pętli też jest wiele razy(dokładniej “i” razy :slightly_smiling_face:) inicjowane.

Miałeś SIGABORT? (sigsegv)
Masz przekroczenie limitu czasu?
Będziesz miał WA - błędną odpowiedź.
Więc po prostu skorzystaj z podpowiedzi :wink:

Inicjowanie == zerowanie !? ?! No no :slight_smile:

Miałem sigsegv przez głupotę którą wskazał mr. T, limit czasu oczywiście też, ale jakieś zadania z dzielnikami na spoju już rozwiązałem, i przypomniało mi się jak to skrócić (w czasie). I oczywiście masz rację, inicjacja nie żeruje zmiennych, chyba że dopisze się do niej =0. A jeśli Wynik+=Podwynik to chyba trzeba ją tak zainicjować, i wtedy nie trzeba pamiętać o żerowaniu jej na końcu pętli. Nie wiem czy to dobrze czy źle, dopiero się uczę, to chyba widać. Tak samo nie wiem dlaczego lepiej używać /n niż endl. Wiem że czepiam się szczegółów, co potrafi być irytujące, ale uważam je za ważne, tym bardziej w kodowaniu🙂

Skoro się uczysz: koncepcja default initialization jest Ci znana?

Co to za (nowa?, twoja?) koncepcja?
W swoim programie na ideone, usiłujesz udowodnić, że zmienne globalne są inicjalizowane wartością zero? W taki sposób niczego nie udowadniasz, a nawet gdybyś użył zmiennych lokalnych, to też niczego, w taki sposób nie dowiedziesz. A że zmienne globalne są tak inicjalizowane, to chyba każdy wiedzieć powinien?
I to jest ta default initialization koncepcija? Wprawdzie nigdy o czymś takim jeszcze nie słyszałem, ale człowiek uczy się całe życie.

Wracając do zadania.
Dla takiej konstrukcji, wykorzystanej przez pytającego, w tym zadaniu, wewnątrz pętli:

cin >> bok1 >> bok2;

zmienne bok1, bok2, nie muszą być zerowane. Dowolna, poprzednia zwartość jest za każdym razem [za pierwszym też], nadpisywana, zamazywana nowymi, wczytanymi wartościami.

https://en.cppreference.com/w/cpp/language/default_initialization1

Raczej jedynie pokazać, że coś takiego istnieje.

Ale początkujący może mieć problemy z nazwaniem mechanizmu i wyszukaniem jego szczegółów przy bardziej złożonych konstrukcjach (czy zmienne lokalne też są tak inicjalizowane? Czy to faktycznie default-initialization czy zero-initialization czy może obie odpowiedzi są poprawne? Czy te zmienne w moim kodzie są statyczne?). Zwłaszcza, jeżeli na start nauczy się uproszczeń jako pewników i nie dowie o istnieniu jakiejkolwiek poważnej dokumentacji.

Nie wiedziałem że zmienne globalne są tak inicjalizowane, wiem za to że lepiej ich nie używać, żeby np. nie zalegały w pamięci, i ktoś tam o jakimś ogniu czy piekle przy nich wspominał.

Oczywiście że wiem co oznacza ta instrukcja, przedtem pisałem:

Oczywiście gdy deklaruję z inicjacją. Nie widzę w tym nic strasznego bo w wielu przykładach jest pokazywane takie coś:
for(int i=1; i<=9; i++){
for(int j=0; j<=9; j++)

}
A nie np. takie
int i, j;
for(i=1; i<=9; i++){
for(j=0; j<=9; j++)

}
To oczywiście tylko moja(uczącej się osoby) opinia

1 year later

Napisałem to w Pythonie i znowu (w innym zadaniu też był taki problem) sędzia stwierdził, że program zbyt długo się wykonuje. Ten sam kod w C (który wykonuje dokładnie te same operacje) przeszedł bez problemu. Czy limit czasu jest w jakikolwiek sposób korygowany dla danego języka? To trochę irytujące, że działający kod jest odrzucany przez wyśrubowane czasy. Python jest po prostu wolniejszy i nawet zoptymalizowany kod wykonuje się relatywnie długo.

Niestety wiele starszych zadań powstało głównie z myślą o c/cpp i nikt nie zastanawiał się, czy da się je rozwiązać w Pythonie. Spoj nie ma opcji ustawienia oddzielnych limitów dla wolniejszych języków programowania.
Zanim zaczniesz pracować nad jakimś zadaniem warto sprawdzić, czy są już rozwiązania w twoim języku. Jeśli nie, jest duże ryzyko, że po prostu się nie da.
Nowsze zadania czasem biorą pod uwagę wolniejsze języki, ale to też nie zawsze, bo różnica w czasie wykonania między cpp a Pythonem jest po prostu zbyt duża.