Loading...
zlobjul avatar zlobjul 4 Точки

JA1 - Task-2-Array-Sum

Здравейте , 

Написах задачата и резултата ми е 50 / 100 , тоест точно половината ги смята правилно , останалата половина - не. Разгледах кода и не виждам проблем в логиката. Как бих могъл да се ориентирам защо една част не ги смята правилно ? Някакви насоки как да си дебъгна кода в такива ситуации ? 

Благодаря , 

Поздрави , 

Любо

Тагове:
0
C++ Fundamentals
georgi.stef.georgiev avatar georgi.stef.georgiev 921 Точки
Best Answer

Здравей,

Понеже имаш няколко твои отговора, ще ги засегна всичките в този отговор.

Напълно възможно и нормално е да има случаи, за които програмата ти работи, и други, за които не работи. Даже е напълно възможно да работи в повечето случаи, но да има ситуации, в които не работи. Facebook работи в повечето случаи, но това не означава, че няма бъгове в някои определени ситуации. Затова има 10 теста, не 2 теста. Логиката "как може за 9 да работи, а за 10 да не работи", можеш да го продължиш на "как може за 8 да работи, но не и за 9", "как може за 7 да работи но не и за 8" и така нататък, и накрая ще те доведе до извода, че няма смисъл изобщо от тестове и направо каквото напишем трябва да го броим за вярно и да не се занимаваме :D

"Разгледах кода и не виждам проблем в логиката" - това е проблемът. Код не се дебъгва с разглеждане - аз се занимавам професионално от повече от 5 години вече (и непрофесионално още няколко), и досега случаите, в които съм оправил нещо само с разглеждане, се броят на пръсти.

"Смених арей-а с вектор и ми даде 100 % , въпреки , че и с арей си го смяташе правилно ...." - не. Не го е "смятало правилно", щом за определени входни данни е имало неправилен изход. Смятало го е правилно за входните данни, които ти си ползвал (които най-вероятно са тези в условието). Промяната, която си направил, звучи свързана с размери на масиви и с индекси и излизане извън размерите на съответните масиви (защото във векторът добавяш елементи и той сам си контролира размера, докато на масива трябва ти да определиш размера - така че сигурно е имало грешка в определянето на размера на масива, или поне в достъпването на елементите му).

Не го приемай всичкото това като заяждане - просто тези изказвания могат да те доведат до характер към програмирането, който е доста вреден за развитието ти и исках да ти покажа проблемите в този начин на мислене.

Имаш обаче и доста хубави и полезни за развитието изказвания, от типа на защо и как да подходиш - нека обърнем и на тях внимание.

Като за начало е нормално случай не всичко да ти работи от първото предаване в judge. Има един лаф в програмирането - "Ако една програма не работи вярно от първия път, в нея има грешка. Но ако работи от първия път, значи в нея има много грешки". Това се дължи предимно на факта, че първия път като пишеш решение на задача (особено ако нямаш много опит - макар че и опитът не е гаранция), обикновено мислиш за това как да си изпишеш кода, че да постигнеш някакво решение на очевидната част от задачата. Обаче често не мислиш за особени случаи, или дори за стандартни случаи, които не са описани изрично в условието.

Пример - намери най-голямото число в поредицата от цели числа, примерно за 1 5 3 12 10 отговорът е 12. Пишеш max = 0; пишеш цикъл да прочети числата и всеки път да променя max ако текущото число е по-голямо от него. Изглежда да работи. Пускаш в judge - половината тестове не минават. Защо? Ами половината тестове в judge съдържат в себе си само отрицателни числа и max = 0; винаги е по-голямо от тях. Сега, тук проблемът е очевиден, не само защото е малка програма, но и защото вече си се сблъсквал с това най-вероятно, но илюстрира добре това, което имам предвид. Обикновено първият ти приоритет е да измислиш "някакво" решение и често или се пропускат детайли от условието, или нещо в самото ти решение предразполага технически програмата ти да не работи добре в определени ситуации. Други интересни ситуации за тази задача са: какво става ако има само 1 число, какво става ако числата са подредени в нарастващ ред (значи на всяка стъпка се променя max), какво става ако са подредени в намаляващ ред (max се променя само веднъж, в началото), какво става ако всички числа са еднакви, какво става ако се редуват положителни и отрицателни и т.н. - забележи как всички тези примери по някакъв начин дефинират някакъв клас от поведения на програмата ти. Във всички тези "класове" от поведения трябва да си уверен, че програмата ти работи вярно.

Следващото важно нещо - има горе долу 3 стъпки в оправянето на всеки един бъг в някакъв софтуер. 1. Възпроизвеждане (repro), 2. изолиране и чак накрая самото 3. поправяне (fix).

1. Възпроизваждането (репродуцирането) означава да пресъздадеш същия проблем, който един клиент/потребител ти е докладвал, на твоята машина или в твоята работна среда - тоест да разбереш при какви входни данни/операции се случва този проблем и да си ги имаш заделени някъде, за да можеш много пъти да го възпроизвеждаш при теб, докато се опитваш да го оправиш. За примера това е да въведеш входни данни съдържащи само отрицателни числа.

2. Изолирането е процесът, в който, след като имаш консистентно възпроизвеждане, търсиш точно коя част от кода предизвиква проблемът. Нещото, което е източник на проблемът. В примера с максималното число, първо търсиш в цялата програма дали четеш вярно числата (да), след това дали извеждаш правилно стойността на max (да), след това дали стойността на max се променя на всяка итерация (и тук е не). Това обаче е симптом на проблема - ти търсиш какво води до този проблем. Ако проверката в if-а е грешна, тогава това ти е изолираният проблем - но тя е вярна и затова продължаваш да търсиш, докато не осъзнаваш, че началната стойност на max е по-голяма от всяко отрицателно число. Тоест изолираният проблем ти е началната стойност на max

3. Поправянето е очевидно - изолирания проблемен код сменяш с такъв, който не е уязвим на тази грешка. Понякога има повече от 1 валиден fix за един проблем, а понякога е достатъчно голям, че да не може да се поправи с промяна в изолираното място на проблема, а трябва да се пренапише логиката на програмата (примерно ако езикът не поддържа отрицателни числа трябва напълно да промениш начина на обработка - да речем да четеш числата като положителни, обаче да си пазиш отделно инфо кои от тях са отрицателни и когато правиш сравненията да вземаш предвид тази информация - това е много хипотетичен случай, но за такъв малък пример е трудно да се измисли по-добър случай, в който fix-ът не може да се направи без да се промени програмата). За нашия пример fix-ът е max да се инициализира или с минималната възможна стойност за отрицателни число в езика, или да се инициализира винаги със стойността на първото от числата - и двата варианта биха работили (е, вторият не би работил ако има случай, в който числата са празни, но това може да се провери с един допълнителен if)

Има и 4-та стъпка, в която тестваш с данните за repro, и ако няма repro значи си оправил проблема. Също тестваш и дали останалата логика на програмата ти продължава да работи вярно (тоест дали fix-ът ти не е счупил нещо друго). За тази цел е хубаво да си си подготвил примерни входни и очаквани изходни данни - почваш с дадените в условието и градиш върху тях. Всеки път като оправиш проблем си добавяш данните от repro-то към примерните входове, за да имаш "тестове" за всички ситуации.

Има разбира се и 0-ва стъпка - повечето софтуери не се публикуват без да се изтестват. Дали това ще е пращане в judge или писането на facebook, всеки софтуерен разработчик би следвало да се досети за потенциални проблеми, а не да разчита на потребителите да му докладват проблеми. С judge не знаеш точно какъв е проблемът, на който трябва да направиш repro, така че тази 0-ва стъпка е важна - общо взето трябва след като си написал програмата да се опиташ нарочно да я счупиш. Да се пробваш да въведеш данни, за които ще изведе грешен отговор (в рамките на ограниченията на задачата разбира се, няма смисъл да въвеждаш думи в задачата за max число, ако е казано, че във входните данни няма да има думи). И е хубаво да се опитваш да тестваш по тези "класове от поведения", за които говорих по-рано. Няма много смисъл да пишеш повече от 1-2 теста за всеки "клас" поведение (е, има защото може да си мислиш, че си намерил един клас поведение, обаче да се окаже, че той си има под-класове поведения - но трябва да го балансираш това срещу общото време, което имаш да решиш задачата).

Ако съдим по коментарите ти от по-рано, ти (както и повечето начинаещи) скачаш директно на стъпка 3 - правиш fix без да си направил repro на проблем и без да си изолирал този проблем. Сменил си масива с вектор, aма не знаеш (или поне не изглежда да знаеш) защо това е помогнало - представи си това да беше оправило един тест но да беше счупило още два. Щеше да си доникъде, защото правиш промени без да разбираш проблема и така може да си навлечеш повече проблеми (е, може и да извадиш късмет, но късметът не е нещо, на което се разчита в програмирането). Или пък отиваш директно на стъпка 2 ("Разгледах кода и не виждам проблем в логиката" е точно стъпка 2 без стъпка едно - няма как да не видиш проблем ако имаш данни, за които не работи, ако трябва стъпка по стъпка и с гледане на стойността на всяка променлива, рано или късно ще видиш проблема).

Така че като за начало се старай винаги да следваш стъпките в реда 1, 2, 3. Ако имаш време мисли и за 0-вата стъпка преди да предадеш в judge, макар че е по-ефикасно от гледна точка на време първо да пратиш в judge и да видиш дали няма да изкараш 100%, за да спестиш време от тестване (на изпита това е добър подход, защото бързаш, в истинския живот не съвсем). Ако не, чак тогава почваш стъпка 0, след това 1 и чак след това 2 и 3 след като имаш проблеми за оправяне (и оправяй проблемите един по един, не се опитвай да оправиш две неща едновременно, това най-много да ти навлече повече проблеми - отделно, че понякога един fix за едно нещо се оказва че оправя и друго нещо, и няма смисъл да мислиш повече от едно нещо точно заради това).

Един ден ще имаш достатъчно опит да усещаш проблемите без репро (след като повечето са ти се случвали по стотина пъти), но дотогава най-добре следвай тези стъпки, ако искаш да си уверен, че кодът ти е правилен, както и за да си уверен, че няма непоправим проблем и няма начин да не намериш грешка, ако измислиш правилните входни данни/начални условия.

Надявам се това да помогне.

Поздрави,

Жоро

1
zlobjul avatar zlobjul 4 Точки

Благодаря за изчерпателния отговор Жоро ! 

Да , изразих се грешно , разбира се , че може в 9 от 10 случаи да работи. Точно там е бъга , в 10 % от случите . 

Напълно съм съгласен с подхода , който си описал и смятам да го следвам, но как да дебъгвам след като не знам подадените данни от системата ? 

Да , мога да опитвам различни видове входни данни ( което и направих - става въпрос за 3та задача от JA ) , но така и не срещам проблема , който програмата за оценяване открива. Какво ли не измислях. Освен това в условието изрично е казано : "Note that 0 minutes is a valid result, but negative results aren’t possible." .

Не е уточнено обаче , какво да изкараме ако няма нито един автобус стигащ на време ? Мислех и за това и опитах да изкарам 0 , но уви. 

Проблемът ,общо взето, е , че трябва да опипвам в тъмното какъв е този 1 от 10 случая , който не работи. Отрицателни стойности в тази задача не са възможни. Опитах да приема и случаите, в които 3 автобуса пристигат след като влака е тръгнал и най-близкия следващ е след 24часа . Но отново програмата не го прие .

След като нямам ясна информация за проблемния случай , един от начините , който ми остава е да налучквам, е и разбира се да мисля за изключителните случаи, но в един момент умствения ми капацитет се изчерпва.... :< 

По същество за третата задача : 

1. Входните данни са 2 цифри часове и 2 цифри минути 

2. Обръщам в минути - 01 = 60 мин . , 02 = 120 мин и тн . и събирам с минутите . 

3. Получените резултати са или по-големи от тези на времето на влака ( тоест закъсняват и пристигат след неговото тръгване ) или пристигат преди влака да е тръгнал . 

4. За втория случай просто вадя и времето на влака с пристигналите автобуси и намирам най-удобния автобус. 

За един от тестовете обаче тази логика не сработва... 

 

Поздрави , 

 

Любо

0
02/12/2017 15:31:44
georgi.stef.georgiev avatar georgi.stef.georgiev 921 Точки

"Negative results aren't possible" означава, че няма как да няма автобус, който пристига навреме - защото това би било негативен резултат, "минус толкова" време ще трябва да чака означава, че трябва да е бил там по-рано. С две думи тестовете са такива, че винаги има верен отговор.

Виждам проблемът в решението ти (гледам ти кода сега в judge). Като тестови данни определено си пропуснал един клас от поведения - имай предвид, че освен броя числа и отношението им към времето на влака, трябва да мислиш и за самите стойности на числата. Какви могат да са стойностите, какви разлика предполага в поведението на програмата ти?

Когато изглежда че си предвидил всички общи случаи на поведение на програмата ти и пак имаш грешки, значи може би проблемът се крие в една от "под-програмите" ти - тоест функциите, които ползваш. Изтествал ли си подробно (по този метод с тези стъпки) дали всяка една функция работи добре за всякакви валидни параметри? Ако "като цяло" програмата ти изглежда да работи вярно, значи трябва да провериш дали всеки един компонент от тази програма работи вярно, щом имаш проблем.

Ще ти подскажа едно нещо, но те съветвам да упражниш написаното в предишния абзац, преди да го прочетеш

.

.

.

.

.

.

- една част от програмата ти прави някакви сложни неща за да постигне нещо много просто, което можеш да постигнеш с един кратък ред код съдържащ аритметичен оператор.

Поздрави,

Жоро

0
zlobjul avatar zlobjul 4 Точки

Да, открих грешката . 

По-лесно y=x%100;  отколкото фукнция от 10 реда :)  

Благодаря !  

Поздрави , 

Любо

0
02/12/2017 16:12:21
Можем ли да използваме бисквитки?
Ние използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Можете да се съгласите с всички или част от тях.
Назад
Функционални
Използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Използваме „сесийни“ бисквитки, за да Ви идентифицираме временно. Те се пазят само по време на активната употреба на услугите ни. След излизане от приложението, затваряне на браузъра или мобилното устройство, данните се трият. Използваме бисквитки, за да предоставим опцията „Запомни Ме“, която Ви позволява да използвате нашите услуги без да предоставяте потребителско име и парола. Допълнително е възможно да използваме бисквитки за да съхраняваме различни малки настройки, като избор на езика, позиции на менюта и персонализирано съдържание. Използваме бисквитки и за измерване на маркетинговите ни усилия.
Рекламни
Използваме бисквитки, за да измерваме маркетинг ефективността ни, броене на посещения, както и за проследяването дали дадено електронно писмо е било отворено.