SPOJ pierwsze zadanie

Cześć,

Ostatnio stwierdziłem, że zabiorę się za zadania ze SPOJ. Już na pierwszym zadaniu spotkałem się z przeszkodą. Mianowicie program nie chce mi przejść testów. Co ciekawe w konsoli, na moim komputerze, wszystko działa.
Treść zadania to:
Sprawdź, które spośród danych liczb są liczbami pierwszymi
Link do zadania:

Proszę o wytłumaczenie mi, jak ten kod ma wyglądać, żeby zadziałał.
Poniżej mój kod:

def is_prime?(n)
  return "NIE" if n < 2
  if (2..Math.sqrt(n).floor).none?{ |i| (n % i).zero? }
    return "TAK"
  else 
    return "NIE"
  end
end

is_prime? n

Nie dorzuciłbyś poprawnego formatowania? Na pewno będzie wtedy łatwiej odpowiedzieć Ci na to pytanie.

Jeżeli to jest Twój cały kod, to brakuje Tobie wczytywanie danych testowych. Tak jak masz w przykładowym inpucie:

  • najpierw pobierasz cyfrę “3”, która oznacza liczbę testów
  • następnie dla podanych przypadków: 11, 1 oraz 4, odpalasz swoją funkcję

@michalstroz Kod na forum wstawia się tak: https://meta.discourse.org/t/syntax-highlighting-of-code-blocks/7242/3

Każde zadanie na SPOJu musi wczytywać dane ze standardowego wejścia i wypisywać na standardowe wyjście.
Proponuję Ci zacząć od zadania testowego http://pl.spoj.com/problems/PTEST/. Trzeba tu wczytać 2 liczby i wypisać ich sumę na ekran.

Rozwiązanie:

a = gets.to_i
b = gets.to_i
puts a + b

Testowanie tego programu wpisując ręcznie z klawiatury dane byłoby męczące. Zatem polecam zapisać sobie dane wejściowe do pliku test.in i uruchamiać program przekierowując na wejście z niego dane:

$ cat test0.in
2
3
$ ruby solver.rb < test0.in
5

Inne metody które możesz używać do wczytywania danych to:

  • wszystkie metody wczytujące z obiektu $stdin
  • metody gets, readline, readlines

Do wypisywania możesz użyć:

  • metod obiektu $stdout
  • puts, print, putc, printf itp.

W zadanich typu PRIME_T musisz obsłużyć dynamiczną ilość danych. Tutaj w pierwszej linii masz podaną liczbę, która mówi ile kolejnych linii musisz wczytać i przetworzyć. Program wykorzystujący Twoją metodę is_prime? wyglądałby tak:

n = gets.to_i

n.times do
  number = gets.to_i
  puts is_prime?(number)
end

Lepiej (czyściej) byłoby jednak żeby metoda is_prime? zwracała wartość true/false aniżeli napis tak jak to robi obecnie.

@radarek, @korowiov87, @Lypa
Ok, już wiem na czym rozwiązywanie na spoj polega. Nie zrozumiałem, że należy zastosować metody zczytujące dane.
Dzięki wielkie chłopaki za pomoc.
Kod który tam zastosowałem:

n = gets.to_i

def is_prime?(n)
  return false if n < 2
  (2..Math.sqrt(n).floor).none?{ |i| (n % i).zero? }
end


n.times do
  num = gets.to_i
  if is_prime?(num)
    puts "TAK"
  else
    puts "NIE"
  end
end

Pytanie, czy można go jeszcze jakoś uprościć lub zoptymalizować?
Dodatkowe pytanie. Czy jest możliwość sprawdzenia najlepszych odpowiedzi, po wysłaniu własnej?

Twój kod jest w porządku. Czy można go zoptymalizować? Zachowując algorytm to niewiele można tu poprawić. Istnieją pewne sztuczki, które trochę przyspieszą Twój program, ale kod przestanie być “the ruby way”. Na przykład zobacz czy zamieniając wywołanie Range#none? na pętlę while kod przyspieszy. Takich sztuczek jest więcej, ale nie będę psuł Ci zabawy w poszukiwaniu ich :).

W tym konkretnym zadaniu możesz jeszcze spróbować użyć standardowej biblioteki Rubiego prime.

Warto też przyjrzeć się zakresom i rozmiarom danych jakie w zadaniu występują.

def is_prime2?(n)
  return false if n < 2
  for i in (2..Math.sqrt(n).floor)
    return false if (n % i) == 0
  end
  true
end

gets.to_i.times { puts is_prime?(gets.to_i)?"TAK":"NIE" }

Ok. 20% szybciej, ale mniej “the ruby way”

@radarek, @RORMaster
A czy istnieją jakieś metody lub zestawienia odnośnie wyznaczania, ile dany proces będzie trwał. Jako, że nigdy nie zagłębiałem się w testowanie szybkości działania skryptu, to takie sprawdzanie trochę na ślepo jest czasochłonne.

No ile będzie trwał to zależy od zbioru danych i złożoności O.
Użycie benchmarka jest proste, pobaw sie i porównaj sobie iteratory, pętle itp.
ruby-benchmark

@michalstroz Możesz zerknąć na moją prezentację o benchmarkowaniu jaką wygłosiłem na KRUGu jakiś czas temu http://radarek.github.io/presentations/Benchmarkuj%20z%20glowa%20-%20KRUG%2019-01-2016/#/.

Do tego zadania, można użyć sita eratostenesa: https://pl.wikipedia.org/wiki/Sito_Eratostenesa. Tak jak kolega wcześniej napisał, można użyć biblioteki prime, gdzie jest zaimplementowane sito. Lecz w przypadku zadań na spoju czy codeforces, warto zapoznać się z danym algorytmem i go zaimplementować :wink:

@radarek, @highassfak, @korowiov87
No cóż po wielu próbach poniżej 0.1 nie zszedłem. Co do biblioteki prime wynik był gorszy niż zazwyczaj.
Poniżej jest kod który zaimplementowałem.

def is_prime?(n)
  return false if n < 2
   i, range = 2, Math.sqrt(n).floor
   while i <= range
     return false if (n % i) == 0
     i += 1
   end
   true
end

gets.to_i.times { puts is_prime?(gets.to_i) ? "TAK" : "NIE" }

Na pewno też da się ten kod zrobić jeszcze bardziej wydajnym. Pytanie tylko, czy lepiej implementować taki kod, czy bardziej w stylu “the ruby way”?

Dziękuję za pomoc Panowie.

Sam musisz sobie stawiać cele. Dla mnie spoj nie jest miejscem gdzie piszę ładny, dopieszczony kod. To staram się robić na co dzień w pracy. Na spoju wcielam się w rolę ‘hackera’ i próbuję często robić rzeczy, które normalnie się nie robi. One mimo wszystko też uczą myślenia i kombinowania. To potem w jakiś sposób przekłada się na praktyczne umiejętności.

Myślę, że nie ma sensu katować jednego zadania za długo. Jest duża szansa, że rozwiązując kolejne odkryjesz nowe sztuczki i wtedy wrócisz do poprzednich zadań. Ja tak robiłem wielokrotnie (choćby po odkryciu, że pętla while jest ciut szybsza od pętli działających na blokach).

Pamiętaj też, że wiele zadań na spoju nie da się rozwiązać w ruby z uwagi na wygórowane limity czasowe. Zawsze warto sprawdzić czy już ktoś rozwiązał dane zadanie w tym języku.

Podsumowując: idź dalej, rozwiązuj kolejne zadania, miej z tego radochę. Z pewnością dużo się przy tym nauczysz :).

@michalstroz

Trochę przekombinowałeś z pętlą while. Bardziej czytelna jest pętla for.

@radarek, @RORMaster Dziękuję Panowie za pomoc.