Показаны сообщения с ярлыком циклы. Показать все сообщения
Показаны сообщения с ярлыком циклы. Показать все сообщения

понедельник, 21 февраля 2011 г.

Как быстро научиться программировать?

Риторический вопрос, так как требует много времени, устремленности и работоспособности, что не всегда есть у большинства начинающих. После первой же неудачи - бросают это дело.
Вот несколько советов начинающим:
1) Возьмите пример программы (даже скачанный с сайта) и проделайте алгоритмические шаги по этой программе в ручную. Конечно нужно знать хотя бы некоторые основные команды и операторы языка. Для этого возьмите справочник или учебник по языку программирования (иногда помогает решить непростую проблему). Например, дана простая программа (такие программы есть в установке среды Borland Pascal в папке Examples или Demo, в папке Doc - документация по языку и работе в среде):
Var S: real;
       i: integer;
begin
   S:=0;
   for i:=1 to 10 do
      S:=S+i;
  writeln(S:7:3);
end.
В этой программе 2 переменные: S и i. Как они меняются в процессе работы программы?
S=0 i=1;
S:=S+i (это значит берем значение из переменной S и прибавляем к ней значение переменной i, а затем присваиваем полученное значение переменной S, т.е. заменяем значение переменной S). Получим S=0+1=1.
i=2 (берем следующее значение параметра цикла)
S=1+2=3
i=3
S=3+3=6
и т.д.
Ответьте на вопрос: что делает этот алгоритм?
2) Для этого что-нибудь поменяйте  в программе, например, измените типы данных или параметры циклов или вывод.
3) После того, как определили как работает алгоритм, что вводит и выводит, попробуйте переписать алгоритм другим способом, например, применить другой оператор цикла While или Repeat. Так вы запомните быстрее их назначение и использование.
4) Через какое-то время попробуйте воспроизвести этот же алгоритм, написав программу заново без подсказки. Если в процессе работы выстроится одни и те же операторы, переменные, порядок этих операторов будет один и тот же, что и в предыдущей программе, то вы уже запомнили этот алгоритм и можно переходить к следующей программе. (У меня уже все алгоритмы выглядят почти одинаково: и структурно, и по именам переменных.)
Так, шаг за шагом, вы выучите все простые алгоритмы при работе с целыми числами, со строками, с вещественными числами, с массивами, с записями, с файлами, с объектами и т.д. Лучше идти от простого к сложному, не углубляясь в суть сложных олимпиадных задач. После наработки таких алгоритмов, можно попробовать решить олимпиадную задачу на одном из сайтов, посвященных этому, например:
acmp.ru
acm.timus.ru
и другие (можно найти по поисковой системе, введя ключевое слово "олимпиады по информатике, программированию").
На многих сайтах есть форумы: общайтесь, читайте советы знающих людей.
Удачи.

вторник, 15 февраля 2011 г.

Циклы

Алгоритм, включающий повторение команд до выполнения какого-то условия, называется циклическим.
Циклы в Pascal можно оформить тремя способами:
1) цикл с параметром (счетчик )
For i:=1 to n do ...;
или
 For i:=n downto 1 do ...;
2) цикл с предусловием
while true do ...;
3) цикл с постусловием
 Repeat ... until false;
Цикл с постусловием не использует операторные скобки begin ... end. 
Рассмотрим простой пример: вычислить сумму цифр в заданной строке S.
Если известна длина и она не превышает 255, то можно использовать цикл с параметром:
Sum:=0;
For i:=1 to length(S) do 
 if S[i] in ['0'..'9'] then Sum:=Sum+ord(S[i])-ord('0'); 
В этом примере функция определения длины length(S) будет вызвана только один раз (!) в начале формирования цикла. Выражение ord(S[i])- ord('0') вычислит разность между числами в таблице ASCII и получит нужную цифру. Очень удобно использовать вместо процедуры val. В среде Delphi/Lazarus для этого можно использовать функцию StrToInt(S[i]);
Если использовать цикл с предусловием, то каждый раз придется вызывать эту функцию:
Sum:=0;
i:=1;
While i<=length(S) do begin
   if S[i] in ['0'..'9'] then Sum:=Sum+ord(S[i])-ord('0'); 
   inc(i);
end;
Но можно предварительно запомнить длину строки во временной переменной L:
Sum:=0;
i:=1; L:=length(S);
While i<=L do begin
   if S[i] in ['0'..'9'] then Sum:=Sum+ord(S[i])-ord('0'); 
   inc(i);
end;
Ничего не изменилось, но если нужно это повторить много раз, то по времени цикл с параметром будет быстрее. Даже если применить 2-й вариант цикла с предусловием, то проверка условия тоже требует времени и будет идти медленнее цикла с параметром. Цикл For работает как счетчик и ему не надо делать лишние проверки, только задать начальный и конечный параметр.
Пример, когда же лучше использовать цикл For, нежели While уже обсуждался (см. Простые числа). Теперь рассмотрим случай, когда цикл For нельзя использовать.
Нужно удалить из строки S символ * и продублировать каждый символ, отличный от *. Приведем неправильный код:
For i:=1 to length(S) do begin
if S[i]='*' then delete(S, i, 1)
 else insert (S[i], S, i);
end; 
На первый взгляд, что здесь неверно? Но теперь,зная, что длина строки вычисляется только один раз в самом начале цикла, то ясно, что выполняться он будет ровно столько, сколько символов было в строке до удаления или добавления. Теперь представим, что в строке удалены половина символов. Что будет проверяться после этого? Скорее всего мусор в памяти компьютера, следующий после слова, или что-то важное. Поэтому может произойти сбой программы при ее выполнении. Как исправить положение? Менять на переменную и исправлять ее в теле цикла бесполезно. Вот в этом случае лучше использовать цикл While:
i:=1;
While i<=length(S) do 
if S[i]='*' then delete(S, i, 1)
 else begin insert (S[i], S, i); i:=i+2; end;

Отметим, что переход на следующий символ здесь нужен только при вставке символа и точно на 2 символа вперед, чтобы пропустить добавленный ранее и перейти к следующему.
Можно запомнить длину строки в переменной L и менять ее в теле цикла:

i:=1; L:=length(S);
While i<=L do 
if S[i]='*' then begin delete(S, i, 1); dec(L); end
 else begin insert (S[i], S, i); i:=i+2; inc(L); end;
Рекомендуется использовать функции inc и dec вместо оператора присваивания.
Решите следующие задачи:
1. Определите количество слов в строке, разделенных одним пробелом.
2. Тоже, но с несколькими пробелами.
3. Уберите лишние пробелы в строке.
4. Вставьте пробелы после запятой, точки, и других знаков препинания, и удалите лишние пробелы перед ними.

четверг, 21 октября 2010 г.

Как научиться программировать циклы?

Циклы - это структура достаточно сложная для понимания и программирования.
Рассмотрим пример составления программы на Паскале. Перед этим лучше дать шаблон описания циклических алгоритмов. Мы будем разбирать цикл с параметром (его очень сложно понять начинающим программистам).
Лучше использовать для понимания пример вычисления суммы ряда. Пусть это будет сумма 1+1/2+1/3+1/4+1/5+ ...+1/1000. Видно, что таких чисел нужно писать очень много. Нельзя ли это как нибудь автоматизировать? Для этого используем циклический алгоритм.
Пусть сумма хранится в переменной S (Важно понять, что переменная меняет свое значение после оператора присваивания). Итак, возникает вопрос: что тут будет телом цикла? и сколько раз выполнится цикл?
Пока не понятно что есть что, предположим, что каждый раз в теле цикла будет прибавляться (увеличиваться) к сумме (переменная S) следующее слагаемое. Выпишем все суммы по порядку увеличения количества слагаемых.
На начальном (нулевом) шаге считаем, что S=0 (ничего не прибавили, нет слагаемых).
1-й шаг S=1
2-й шаг S=1+1/2
3-й шаг S=1+1/2+1/3
4-й шаг S=1+1/2+1/3+1/4
и т.д.
Видно, что каждый раз в сумме повторяется некоторое количество слагаемых (они подчеркнуты). Оператор присваивания работает справа налево: сначала вычисляется выражение, а потом присваивается результат в переменную. Если мы запишем S=S+1 это будет означать, что берем значение из переменной S (оно равно на нулевом шаге 0) и прибавим 1, в результате получаем 1; и только после этого заменяем значение переменной S на 1, т.е. теперь S равно не 0, а 1.
Таким образом, следующим шагом будет S=S+1/2 - это будет означать, что берем значение из переменной S (оно равно на 1-м шаге 1) и прибавим 1/2, в результате получаем 1+1/2; и только после этого заменяем значение переменной S на 1+1/2, т.е. теперь S равно не 1, а 1+1/2.
Продолжая рассуждения получим следующий линейный алгоритм:

0-й шаг S=0
1-й шаг S=S+1
2-й шаг S=S+1/2
3-й шаг S=S+1/3
4-й шаг S=S+1/4
 и т.д.

Теперь видно, что какую-то часть можно считать постоянной частью и выделить в тело цикла. Но шаги и слагаемые меняются. Как решить эту проблему? Обозначим, номер шага за переменную K. Тогда линейный алгоритм запишется так:

0-й шаг K=0; S=0

1-й шаг K=1; S=S+1/K
2-й шаг K=2; S=S+1/K
3-й шаг K=3; S=S+1/K
4-й шаг K=4; S=S+1/K
и т.д.
Теперь видно, что S=S+1/K повторяется каждый раз для каждого следующего K. K меняется от 1 до 1000.  Для этой переменной можно организовать цикл с параметром FOR K:=1 TO1000 DO, а в теле цикла запишем S:=S+1/K. Получим следующий циклический алгоритм вместо линейного:
FOR K:=1 TO1000 DO 
         S:=S+1/K;
Видно, что эти две строчки ничто, по сравнению с 1000 строчками линейного.
Спросите, зачем огород городить? На самом деле такие задачи обычно решаются с заданным ограничением: количество слагаемых (обозначим за переменную N). И если записать для каждого числа такие суммы, то будет ОЧЕНЬ МНОГО строк в программе. Конечно, можно копировать и вставлять, но лучше записать так:

READLN(N);
FOR K:=1 TO N DO 
         S:=S+1/K;
 Все!
Я привела такой подробный текст, чтобы видно было ход моих рассуждений, как пришли к циклу. Если просто записать цикл и от него уже объяснять как работает цикл - никогда не научитесь программировать циклы.
Попробуйте самостоятельно составить циклы для сумм:
a) sin 1+ sin 2+sin 3 + ... + sin n
b) x+x^2+x^3+x^4+...+x^n (не используя функцию возведения в степень и лишних циклов)
с) 1+1*2+1*2*3+...+n! (n!=1*2*3*...*n - не используя лишних операторов цикла)
d) sin x+ sin sin x + sin sin sin x+ ... + sin ... sin x (количество вызовов функции sin будет n)