Что значит сложность алгоритма и зачем нам ее знать?
Для составления эффективного и оптимизированного по времени и по памяти алгоритма.
Сложность алгоритма обычно вычисляют для больших объемов данных, например, массивы из N элементов.
Она бывает временная (по используемому времени выполнения) и емкостная (по размеру используемой памяти) и выражается как функция от N. Ее можно вычислить, зная сколько арифметических операций используется в алгоритме и сколько байт занимают данные.
Кроме этого на эффективность алгоритма (точнее программы) влияет:
Вот некоторые рекомендации:
var i,n,k,a:int64;//64 разрядный тип целых чисел
begin
read(n);
k:=0;
for i:=1 to n do begin
read(a);
if a>0 then k:=k+1;
end;
write(k);
end.
Рассмотрим еще одну задачу, где можно обойтись без массива:
Напишите программу, которая циклически сдвигает элементы массива влево (например, массив {3, 5, 7, 9} превращается в массив {5, 7, 9, 3}).
Решение:
var
n:integer;
a,i,p:longint;
begin
read(n);read(p);
for i:=1 to n do begin
read(a);
write(a,' ');
end;
write(p);
end.
Здесь сложность алгоритма, не учитывая время на ввод и вывод данных, равна 0.
Рассмотрим аналогичную задачу, где без массива не обойтись:
Напишите программу, которая циклически сдвигает элементы массива вправо (например, если элементы нумеруются, начиная с нуля, то 0-й элемент становится 1-м, 1-й становится 2-м, ..., последний становится 0-м, то есть массив {3, 5, 7, 9} превращается в массив {9, 3, 5, 7}). (см. здесь)
Решение:
1. Считываем сразу все элементы в нужном порядке и выводим:
program A1;
var
n:integer;
a:array[1..3200] of longint;
i:longint;
begin
read(n);
for i:=2 to n do read(a[i]);
read(a[1]);
for i:=1 to n do write(a[i],' ');
end.
Здесь сложность алгоритма, не учитывая время на ввод и вывод данных, равна 0.
2. После считывания всех элементов - делаем сдвиг с конца.
program A2;
var
n:integer;
a:array[1..3200] of longint;
i,p:longint;
begin
read(n);
for i:=1 to n do read(a[i]);
p:=a[n];
for i:=n downto 2 do a[i]:=a[i-1];
a[1]:=p;
for i:=1 to n do write(a[i],' ');
end.
По количеству присваиваний - сложность алгоритма линейно зависит от N+1.
3. После считывания всех элементов - делаем перестановки пары элементов с конца (как в сортировке пузырьком).
program A3;
var
n:integer;
a:array[1..3200] of longint;
i,p:longint;
begin
read(n);
for i:=1 to n do read(a[i]);
for i:=n downto 2 do begin
p:=a[i];a[i]:=a[i-1];a[1-1]:=p;
end;
for i:=1 to n do write(a[i],' ');
end.
По количеству присваиваний - сложность алгоритма линейно зависит от 3*(N-1).
Еще пример: Дан массив чисел. Переставить элементы массива в обратном порядке.
Решение: Если больше ничего с этим массивом делать не надо, то можно просто считать и вывести за один проход.
program A1;
var
n:integer;
a:array[1..3200] of longint;
i:longint;
begin
read(n);
for i:=1 to n do read(a[i]);
for i:=n downto 1 do write(a[i],' ');
end.
или так
program A2;
var
n:integer;
a:array[1..3200] of longint;
i:longint;
begin
read(n);
for i:=n downto 1 do read(a[i]);
for i:=1 to n do write(a[i],' ');
end.
Если действительно нужно перевернуть массив, то придется написать алгоритм:
program A3;
var
n:integer;
a:array[1..3200] of longint;
p,i:longint;
begin
read(n);
for i:=1 to n do read(a[i]);
for i:=1 to n div 2 do begin
p:=a[i]; a[i]:=a[n-i+1]; a[n-i+1]:=p;
end;
for i:=1 to n do write(a[i],' ');
end.
Для составления эффективного и оптимизированного по времени и по памяти алгоритма.
Сложность алгоритма обычно вычисляют для больших объемов данных, например, массивы из N элементов.
Она бывает временная (по используемому времени выполнения) и емкостная (по размеру используемой памяти) и выражается как функция от N. Ее можно вычислить, зная сколько арифметических операций используется в алгоритме и сколько байт занимают данные.
Кроме этого на эффективность алгоритма (точнее программы) влияет:
- работа процессора (количество операций выполняемых в секунду),
- его разрядность (сколько бит считывается сразу, как одно слово),
- устройство памяти,
- компилятор языка программирования (быстрее будет программа на Ассемблере).
Вот некоторые рекомендации:
- Разрядность процессора должна совпадать с размером данных. Например, 32-х разрядный процессор будет быстрее выполнять операции с типами данных, которые кратны 32 (в Паскале - longint). Это относится и к размеру массива.
- Цикл for работает быстрее while, так как не проверяет условие продолжения/окончания цикла каждый раз.
- В логическом выражении ставьте первым то условие, которое выполняется чаще других. То же относится ко вложенным циклам и циклам с условиями.
- Лучше использовать цикл вместо рекурсивной функции.
- Ввод и вывод на экран тратится больше времени, чем при работе с файлом. Лучше работать напрямую с памятью (лучше динамической).
- По возможности сохраняйте громоздкие вычисления в переменные или константы.
- Вызов функций и подпрограмм тоже требует времени.
- Если есть возможность - сразу считываем, обрабатываем и выводим результат. В некоторых задачах использование массива необязательно, например: в массиве, состоящий из целых чисел, подсчитать количество положительных чисел.
var i,n,k,a:int64;//64 разрядный тип целых чисел
begin
read(n);
k:=0;
for i:=1 to n do begin
read(a);
if a>0 then k:=k+1;
end;
write(k);
end.
Рассмотрим еще одну задачу, где можно обойтись без массива:
Напишите программу, которая циклически сдвигает элементы массива влево (например, массив {3, 5, 7, 9} превращается в массив {5, 7, 9, 3}).
Решение:
var
n:integer;
a,i,p:longint;
begin
read(n);read(p);
for i:=1 to n do begin
read(a);
write(a,' ');
end;
write(p);
end.
Здесь сложность алгоритма, не учитывая время на ввод и вывод данных, равна 0.
Рассмотрим аналогичную задачу, где без массива не обойтись:
Напишите программу, которая циклически сдвигает элементы массива вправо (например, если элементы нумеруются, начиная с нуля, то 0-й элемент становится 1-м, 1-й становится 2-м, ..., последний становится 0-м, то есть массив {3, 5, 7, 9} превращается в массив {9, 3, 5, 7}). (см. здесь)
Решение:
1. Считываем сразу все элементы в нужном порядке и выводим:
program A1;
var
n:integer;
a:array[1..3200] of longint;
i:longint;
begin
read(n);
for i:=2 to n do read(a[i]);
read(a[1]);
for i:=1 to n do write(a[i],' ');
end.
Здесь сложность алгоритма, не учитывая время на ввод и вывод данных, равна 0.
2. После считывания всех элементов - делаем сдвиг с конца.
program A2;
var
n:integer;
a:array[1..3200] of longint;
i,p:longint;
begin
read(n);
for i:=1 to n do read(a[i]);
p:=a[n];
for i:=n downto 2 do a[i]:=a[i-1];
a[1]:=p;
for i:=1 to n do write(a[i],' ');
end.
По количеству присваиваний - сложность алгоритма линейно зависит от N+1.
3. После считывания всех элементов - делаем перестановки пары элементов с конца (как в сортировке пузырьком).
program A3;
var
n:integer;
a:array[1..3200] of longint;
i,p:longint;
begin
read(n);
for i:=1 to n do read(a[i]);
for i:=n downto 2 do begin
p:=a[i];a[i]:=a[i-1];a[1-1]:=p;
end;
for i:=1 to n do write(a[i],' ');
end.
По количеству присваиваний - сложность алгоритма линейно зависит от 3*(N-1).
Еще пример: Дан массив чисел. Переставить элементы массива в обратном порядке.
Решение: Если больше ничего с этим массивом делать не надо, то можно просто считать и вывести за один проход.
program A1;
var
n:integer;
a:array[1..3200] of longint;
i:longint;
begin
read(n);
for i:=1 to n do read(a[i]);
for i:=n downto 1 do write(a[i],' ');
end.
или так
program A2;
var
n:integer;
a:array[1..3200] of longint;
i:longint;
begin
read(n);
for i:=n downto 1 do read(a[i]);
for i:=1 to n do write(a[i],' ');
end.
Если действительно нужно перевернуть массив, то придется написать алгоритм:
program A3;
var
n:integer;
a:array[1..3200] of longint;
p,i:longint;
begin
read(n);
for i:=1 to n do read(a[i]);
for i:=1 to n div 2 do begin
p:=a[i]; a[i]:=a[n-i+1]; a[n-i+1]:=p;
end;
for i:=1 to n do write(a[i],' ');
end.
Комментариев нет:
Отправить комментарий