Запись алгоритмов на языках программирования

Информатика 11 класс · тест: 5 вопросов

Погружаемся в мир языков программирования! Узнаем, как записывать алгоритмы на языках программирования Си, Паскаль и Python, работать со структурами данных и массивами. Научимся анализировать программы и решать практические задачи.

Язык программирования как мост между мыслью и машиной

§ 7. Запись алгоритмов на языках программирования

Введение: Язык как мост между мыслью и машиной

Представь: ты хочешь объяснить другу, как пройти к новому кафе. Ты используешь русский язык, жесты, может, карту. А теперь представь, что твой друг — это компьютер. Он не понимает ни слов, ни жестов. Ему нужен строгий, формальный язык, где каждое слово имеет одно значение, а каждая конструкция подчиняется железным правилам.

Язык программирования — это именно такой формальный язык. Это система знаков и правил, которая позволяет описать алгоритм так, чтобы компьютер мог его понять и выполнить.

💡 Интересный парадокс

Мы создаём языки программирования, чтобы общаться с машинами, но на самом деле мы общаемся друг с другом через код. Код читают другие программисты, его поддерживают, развивают. Машина лишь исполняет то, что мы описали.

Мост между человеком и компьютером через код

Язык программирования — мост между человеческой мыслью и машинным исполнением

В учебнике примеры записаны на языке Си — одном из фундаментальных языков программирования: на нём написаны операционные системы (Linux, части Windows), игровые движки и драйверы устройств. В разных школах программируют на разных языках, поэтому здесь у каждого примера есть вкладки Си / Паскаль / Python: выбери свой, и сайт запомнит выбор.

7.1. Структурная организация данных: от битов к смыслу

Компьютер на нижнем уровне оперирует битами — нулями и единицами. Это единственный "язык", который понимает процессор. Но мы, люди, не думаем битами. Мы думаем числами, словами, изображениями, музыкой.

📊 Определение

Данные — это информация, представленная в виде, пригодном для обработки компьютером.

🤔 Как ты думаешь?

Почему мы не пишем программы на языке битов? Потому что это невероятно сложно, долго и чревато ошибками. Вместо этого мы используем абстракции — слои, которые скрывают сложность и позволяют работать с более понятными концепциями.

Простые и сложные структуры данных

Простые типы

Базовые "кирпичики"

  • Целые числа (int, long) — для счёта, индексов, кодов
  • Вещественные числа (float, double) — для измерений, координат, научных расчётов
  • Символы (char) — буквы, цифры, знаки препинания
  • Логические значения (bool, int) — истина/ложь, да/нет
Сложные типы

Комбинации простых типов

  • Массивы — упорядоченные последовательности элементов одного типа
  • Строки — массивы символов, представляющие текст
  • Структуры (struct) — объединения разнородных данных
  • Графы, деревья, списки — более сложные структуры для специфических задач
От простых блоков к сложным конструкциям

Структуры данных: от простых элементов к сложным построениям

✨ Почему это важно?

Понимание структуры данных помогает:

  • Выбрать правильный тип для задачи (не использовать float там, где нужен int)
  • Оптимизировать память (int занимает меньше места, чем double)
  • Избегать ошибок (переполнение, потеря точности)
  • Понимать, как работают библиотеки и фреймворки

7.2. Основы языка программирования: инструменты для построения алгоритмов

Давайте разберём "анатомию" программы. Каждая программа — это текст, состоящий из элементов, организованных по строгим правилам.

Алфавит и базовые элементы

🔤 Алфавит

Латинские буквы (a-z, A-Z), цифры (0-9), специальные символы (;, {, }, (, ), +, -, *, /, и др.)

📚 Служебные слова

Слова с зарезервированным значением: int, if, while, return, for

🏷️ Идентификаторы

Названия переменных, функций: counter, summa, calculateAverage

📝 Правила именования

  • Начинается с буквы или подчёркивания _
  • Может содержать буквы, цифры, подчёркивания
  • Регистр имеет значение: Counter и counter — разные имена
  • Не должно совпадать со служебными словами

Практический совет: Имена должны быть говорящими. x — плохо. studentAge — хорошо. Код читается намного чаще, чем пишется.

Операции и выражения

➕ Арифметические операции

+   сложение
-   вычитание
*   умножение
/   деление
%   остаток от деления (только для целых)
+     сложение
-     вычитание
*     умножение
/     деление (результат всегда дробный)
div   целая часть от деления
mod   остаток от деления
+    сложение
-    вычитание
*    умножение
/    деление (результат всегда дробный)
//   целая часть от деления
%    остаток от деления
**   возведение в степень

⚖️ Операции сравнения

==  равно
!=  не равно
>   больше
<   меньше
>=  больше или равно
<=  меньше или равно
=   равно
<>  не равно
>   больше
<   меньше
>=  больше или равно
<=  меньше или равно
==  равно
!=  не равно
>   больше
<   меньше
>=  больше или равно
<=  меньше или равно

🔀 Логические операции

&&  логическое И (AND)
||  логическое ИЛИ (OR)
!   логическое НЕ (NOT)
and  логическое И
or   логическое ИЛИ
not  логическое НЕ
and  логическое И
or   логическое ИЛИ
not  логическое НЕ

🎯 Приоритет операций

От высшего к низшему:

  1. ! (отрицание)
  2. *, /, %
  3. +, -
  4. <, <=, >, >=
  5. ==, !=
  6. &&
  7. ||

💡 Пример

int result = 5 + 3 * 2;  // result = 11 (не 16!)
int logic = (5 > 3) && (2 < 4);  // logic = 1 (истина)
result := 5 + 3 * 2;             { result = 11 (не 16!) }
logic := (5 > 3) and (2 < 4);    { logic = True (истина) }
result = 5 + 3 * 2               # result = 11 (не 16!)
logic = (5 > 3) and (2 < 4)      # logic = True (истина)

Как ты думаешь, почему важен приоритет операций? Представь, что ты пишешь формулу для расчёта итоговой оценки: (homework * 0.3 + exam * 0.7). Без правильного порядка вычислений результат будет неверным.

Структура программы

#include <stdio.h>  // Подключение библиотеки для ввода-вывода

int main() {  // Главная функция - точка входа
    // Объявление переменных
    int age;
    float height;
    
    // Ввод данных
    printf("Введите возраст: ");
    scanf("%d", &age);
    
    // Обработка
    if (age >= 18) {
        printf("Вы совершеннолетний\n");
    } else {
        printf("Вы несовершеннолетний\n");
    }
    
    return 0;  // Успешное завершение
}
program Adult;                   { заголовок программы }
var                              { объявление переменных }
  age: integer;
  height: real;
begin                            { начало программы }
  { ввод данных }
  write('Введите возраст: ');
  readln(age);

  { обработка }
  if age >= 18 then
    writeln('Вы совершеннолетний')
  else
    writeln('Вы несовершеннолетний');
end.                             { конец программы — с точкой }
# переменные объявлять не нужно

# ввод данных
age = int(input("Введите возраст: "))

# обработка
if age >= 18:
    print("Вы совершеннолетний")
else:
    print("Вы несовершеннолетний")

🔑 Ключевые части программы

  • #include — директива препроцессора, подключает библиотеки
  • main() — обязательная функция, с неё начинается выполнение
  • Блок { } — группирует операторы
  • ; — завершает каждую инструкцию
  • return 0 — код возврата (0 = успех)
Программа как конвейер обработки данных

Программа работает как конвейер: данные → обработка → результат

Основные операторы

Оператор Назначение Пример
Присваивание Присвоить значение переменной x = 10;
Ввод Считать данные scanf("%d", &n);
Вывод Вывести данные printf("Результат: %d\n", sum);
Условный Ветвление if (x > 0) { ... } else { ... }
Цикл while Цикл с предусловием while (i < 10) { ... }
Цикл for Цикл со счётчиком for (i = 0; i < 10; i++) { ... }
Цикл do-while Цикл с постусловием do { ... } while (i < 10);

7.3. Анализ программ: трассировочные таблицы

Один из лучших способов понять, как работает программа — это "прогнать" её вручную, пошагово отслеживая изменения переменных. Этот процесс называется трассировкой.

🎯 Зачем нужна трассировка?

  • Поиск ошибок: Найти место, где программа делает не то, что мы ожидали
  • Понимание логики: Увидеть, как изменяются данные на каждом шаге
  • Обучение: Глубже понять работу циклов, условий, рекурсии
  • Подготовка к экзаменам: Многие задачи на ЕГЭ требуют именно ручной трассировки

Пример 1: Простой цикл

#include <stdio.h>

int main() {
    int a = 5;
    int b = 1;
    
    while (b <= a) {
        b = b + 1;
        a = a - 1;
    }
    
    printf("a = %d, b = %d\n", a, b);
    return 0;
}
program Trace1;
var
  a, b: integer;
begin
  a := 5;
  b := 1;
  while b <= a do
  begin
    b := b + 1;
    a := a - 1;
  end;
  writeln('a = ', a, ', b = ', b);
end.
a = 5
b = 1
while b <= a:
    b = b + 1
    a = a - 1
print(f"a = {a}, b = {b}")

Трассировочная таблица (каждая строка — один шаг):

Шаг Команда/Условие a b Комментарий
1 a = 5 5 — Инициализация
2 b = 1 5 1 Инициализация
3 b <= a (истина) 5 1 Входим в цикл
4 b = b + 1 5 2
5 a = a - 1 4 2
... ... ... ... Цикл продолжается
12 b <= a (ложь) 2 4 Выходим из цикла
13 printf(...) 2 4 Вывод

✅ Ответ

a = 2, b = 4

Пример 2: Вложенные циклы (компактная трассировка)

Для сложных программ полная трассировка может быть громоздкой. Можно использовать контрольные точки — отмечать состояние переменных в ключевых моментах.

#include <stdio.h>

int main() {
    int s = 0;
    int i, j;
    
    for (i = 1; i <= 3; i++) {
        for (j = 3; j >= i; j--) {
            s = s + i + j;
        }
    }
    
    printf("s = %d\n", s);
    return 0;
}
program Trace2;
var
  s, i, j: integer;
begin
  s := 0;
  for i := 1 to 3 do
    for j := 3 downto i do
      s := s + i + j;
  writeln('s = ', s);
end.
s = 0
for i in range(1, 4):
    for j in range(3, i - 1, -1):   # j от 3 вниз до i
        s = s + i + j
print("s =", s)

Контрольная точка: после каждого выполнения s = s + i + j

Итерация i j s (новое) Вычисление
Начало — — 0
1 1 3 4 0 + 1 + 3
2 1 2 7 4 + 1 + 2
3 1 1 9 7 + 1 + 1
4 2 3 14 9 + 2 + 3
5 2 2 18 14 + 2 + 2
6 3 3 24 18 + 3 + 3

✅ Ответ

s = 24

💡 Интересное наблюдение

Внутренний цикл выполняется разное количество раз на каждой итерации внешнего цикла. Это типичная ситуация для алгоритмов обработки треугольных матриц или пирамид.

Трассировка программы как исследование

Трассировка — это детальное исследование каждого шага программы

7.4. Продвинутые приёмы анализа: когда трассировки недостаточно

Иногда программа содержит сотни или тысячи итераций. Построить полную трассировочную таблицу невозможно. Нужны другие подходы.

Метод инвариантов

📐 Определение

Инвариант цикла — это утверждение, которое остаётся истинным перед каждой итерацией цикла.

Пример:

#include <stdio.h>

int main() {
    int n = 0;
    int s = 400;
    
    while (s < 2992) {
        s = s + 12;
        n = n + 2;
    }
    
    printf("n = %d\n", n);
    return 0;
}
program Invariant;
var
  n, s: integer;
begin
  n := 0;
  s := 400;
  while s < 2992 do
  begin
    s := s + 12;
    n := n + 2;
  end;
  writeln('n = ', n);
end.
n = 0
s = 400
while s < 2992:
    s = s + 12
    n = n + 2
print("n =", n)

🔍 Анализ:

  1. Что делает s? Начинается с 400, каждую итерацию увеличивается на 12
  2. Что делает n? Начинается с 0, каждую итерацию увеличивается на 2
  3. Инвариант: n = 2 * (количество итераций)
  4. Условие выхода: s >= 2992

📊 Вычисление:

  • Количество итераций: (2992 - 400) / 12 = 2592 / 12 = 216
  • Значит, n = 2 * 216 = 432

Проверка граничного случая:

  • После 216 итераций: s = 400 + 216 * 12 = 400 + 2592 = 2992 ✓
  • Условие s < 2992 станет ложным ✓

✅ Ответ

n = 432

Метод аналитического разбора

Пример:

#include <stdio.h>

int main() {
    int x, m, n;
    
    printf("Введите x: ");
    scanf("%d", &x);
    
    m = 0;
    n = 1;
    
    while (x > 0) {
        m = m + 1;
        n = n * (x % 10);
        x = x / 10;
    }
    
    printf("m = %d, n = %d\n", m, n);
    return 0;
}
program Digits;
var
  x, m, n: integer;
begin
  write('Введите x: ');
  readln(x);
  m := 0;
  n := 1;
  while x > 0 do
  begin
    m := m + 1;
    n := n * (x mod 10);
    x := x div 10;
  end;
  writeln('m = ', m, ', n = ', n);
end.
x = int(input("Введите x: "))
m = 0
n = 1
while x > 0:
    m = m + 1
    n = n * (x % 10)
    x = x // 10
print(f"m = {m}, n = {n}")

🔍 Что делает программа?

Разберём по шагам:

  1. m = m + 1 — счётчик, увеличивается на каждой итерации
  2. x % 10 — последняя цифра числа x
  3. n = n * (x % 10) — умножаем n на последнюю цифру
  4. x = x / 10 — "отрезаем" последнюю цифру (целочисленное деление)

💡 Вывод:

  • m — количество цифр в числе x
  • n — произведение всех цифр числа x

📝 Задача:

Найти все пятизначные числа, для которых m = 5 и n = 25.

Решение:

  • 25 = 5 × 5 × 1 × 1 × 1
  • Значит, число должно содержать две пятёрки и три единицы

Комбинаторика: Количество способов расставить 2 пятёрки и 3 единицы в 5 позициях:

C₅² = 5! / (2! · 3!) = (5 · 4) / 2 = 10

Все числа:

11155, 11515, 11551, 15115, 15151, 15511, 51115, 51151, 51511, 55111

Наименьшее: 11155
Наибольшее: 55111

Поиск закономерностей вместо перебора

Аналитический подход: ищем закономерности вместо слепого перебора

✨ Ключевые выводы по §7

Язык программирования — это формальная система, которая переводит наши идеи в инструкции для компьютера. Код пишется для людей, а исполняется машинами. Структура данных определяет эффективность. Правильный выбор типа данных влияет на скорость, память и корректность программы. Трассировка — мощный инструмент анализа. Для простых программ используем полную пошаговую трассировку, для сложных — контрольные точки и аналитические методы. Инварианты и паттерны помогают понимать алгоритмы. Вместо слепого перебора ищи закономерности в работе переменных.

🤔 Проверь себя (§7)

Вопрос 1: Почему в программировании важен приоритет операций? Приведи пример ошибки, которая может возникнуть из-за неправильного понимания приоритета.

Подумай о разнице между 2 + 3 * 4 и (2 + 3) * 4. Какой результат получится в каждом случае?

Вопрос 2: Определи значение переменной result после выполнения кода:
int a = 10, b = 3, result;
result = (a / b) * b + (a % b);
a := 10;
b := 3;
result := (a div b) * b + (a mod b);
a = 10
b = 3
result = (a // b) * b + (a % b)

Подсказка: вспомни про целочисленное деление и остаток от деления.

Вопрос 3: Программа выводит числа от 1 до N. Какой цикл ты бы выбрал — while или for? Почему?

Подумай о том, когда количество итераций известно заранее, а когда — нет.

Вопрос 4: Дана программа. Что хранится в переменной sum после выполнения? Опиши словами, что делает этот алгоритм.
int x = 123;
int sum = 0;
while (x > 0) {
    sum += x % 10;
    x /= 10;
}
x := 123;
sum := 0;
while x > 0 do
begin
  sum := sum + x mod 10;
  x := x div 10;
end;
x = 123
sum = 0
while x > 0:
    sum += x % 10
    x //= 10
Мини-кейс: Тебе нужно написать программу, которая определяет, является ли число простым. Опиши алгоритм словами (без кода). Какие оптимизации можно применить?

Подсказка: нужно ли проверять все делители от 2 до N-1?

§ 8. Структурированные типы данных. Массивы

Введение: От одиночных величин к коллекциям

Представь: ты разрабатываешь приложение для анализа результатов турнира по киберспорту. У тебя 100 участников, и нужно хранить очки каждого.

Плохое решение:

int player1, player2, player3, ..., player100;
var
  player1, player2, player3, ..., player100: integer;
player1 = player2 = player3 = ... = player100 = 0

Это абсурд! Код будет огромным, негибким, непригодным для обработки.

Хорошее решение:

int scores[100];  // Массив из 100 элементов
var
  scores: array[1..100] of integer;   { массив из 100 элементов }
scores = [0] * 100   # список из 100 элементов

📚 Определение

Массив — это структура данных, которая позволяет хранить множество элементов одного типа под одним именем, обращаясь к каждому по номеру (индексу).

🌍 Массивы повсюду

  • Пиксели изображения — двумерный массив цветов
  • Звуковая дорожка — массив амплитуд сигнала
  • Список друзей — массив ID пользователей
  • История чата — массив сообщений
Массив как организованное хранилище данных

Массив — упорядоченная коллекция данных с доступом по номеру

8.1. Объявление и инициализация массивов

Базовый синтаксис

тип_данных имя_массива[размер];
имя_массива: array[первый_индекс..последний_индекс] of тип;
имя_списка = [начальное_значение] * размер

📝 Примеры:

int numbers[10];          // Массив из 10 целых чисел
float temperatures[7];    // Массив из 7 вещественных чисел
char message[100];        // Массив из 100 символов (строка)
var
  numbers: array[1..10] of integer;    { 10 целых чисел }
  temperatures: array[1..7] of real;   { 7 вещественных чисел }
  message: string;                     { строка }
numbers = [0] * 10          # 10 целых чисел
temperatures = [0.0] * 7    # 7 вещественных чисел
message = ""                # строка

⚠️ Важно!

  • Размер массива должен быть известен на этапе компиляции (константа)
  • В Си и Python индексы начинаются с 0: массив из 10 элементов — это arr[0] … arr[9]. В Паскале границы задают при описании, обычно array[1..10] — элементы arr[1] … arr[10]
  • Обращение к arr[10] — выход за границы массива, это ошибка!

Инициализация массива

Способ 1

При объявлении

int scores[5] = {85, 92, 78, 95, 88};
const
  scores: array[1..5] of integer = (85, 92, 78, 95, 88);
scores = [85, 92, 78, 95, 88]
Способ 2

Частичная инициализация

int data[10] = {1, 2, 3};
// Остальные элементы = 0
{ в Паскале при описании перечисляют все элементы }
const
  data: array[1..10] of integer = (1, 2, 3, 0, 0, 0, 0, 0, 0, 0);
data = [1, 2, 3] + [0] * 7
# остальные элементы = 0
Способ 3

Без указания размера

int values[] = {10, 20, 30, 40};
// Размер = 4 (автоматически)
{ размер в Паскале всегда указывают явно }
const
  values: array[1..4] of integer = (10, 20, 30, 40);
values = [10, 20, 30, 40]
# len(values) = 4
Способ 4

В цикле

int arr[100];
for (int i = 0; i < 100; i++) {
    arr[i] = i * 2;
}
var
  arr: array[1..100] of integer;
  i: integer;
...
for i := 1 to 100 do
  arr[i] := (i - 1) * 2;   { те же значения: 0, 2, 4, … }
arr = [0] * 100
for i in range(100):
    arr[i] = i * 2

8.2. Работа с элементами массива

Доступ к элементам

#include <stdio.h>

int main() {
    int scores[5] = {85, 92, 78, 95, 88};
    
    // Чтение элемента
    printf("Первый элемент: %d\n", scores[0]);  // 85
    printf("Третий элемент: %d\n", scores[2]);  // 78
    
    // Изменение элемента
    scores[2] = 80;
    printf("Новый третий элемент: %d\n", scores[2]);  // 80
    
    return 0;
}
program Access;
var
  scores: array[1..5] of integer;
begin
  scores[1] := 85; scores[2] := 92; scores[3] := 78;
  scores[4] := 95; scores[5] := 88;

  { чтение элемента: в Паскале индексы с 1 }
  writeln('Первый элемент: ', scores[1]);   { 85 }
  writeln('Третий элемент: ', scores[3]);   { 78 }

  { изменение элемента }
  scores[3] := 80;
  writeln('Новый третий элемент: ', scores[3]);   { 80 }
end.
scores = [85, 92, 78, 95, 88]

# чтение элемента
print("Первый элемент:", scores[0])   # 85
print("Третий элемент:", scores[2])   # 78

# изменение элемента
scores[2] = 80
print("Новый третий элемент:", scores[2])   # 80

🤔 Как ты думаешь, почему индексация с нуля?

Исторически это связано с тем, что индекс — это смещение относительно начала массива. arr[0] — это "начало + 0 шагов", arr[3] — "начало + 3 шага".

Обход массива (итерация)

🔄 Классический способ:

int arr[5] = {10, 20, 30, 40, 50};
int sum = 0;

for (int i = 0; i < 5; i++) {
    sum += arr[i];
}

printf("Сумма: %d\n", sum);  // 150
const
  arr: array[1..5] of integer = (10, 20, 30, 40, 50);
var
  i, sum: integer;
...
sum := 0;
for i := 1 to 5 do
  sum := sum + arr[i];
writeln('Сумма: ', sum);   { 150 }
arr = [10, 20, 30, 40, 50]
total = 0
for i in range(5):
    total += arr[i]
print("Сумма:", total)   # 150

❌ Распространённая ошибка:

for (int i = 0; i <= 5; i++) {  // ОШИБКА! Выход за границы
    sum += arr[i];
}
for i := 1 to 6 do        { ОШИБКА! Элемента arr[6] нет }
  sum := sum + arr[i];
for i in range(6):        # ОШИБКА! arr[5] нет — IndexError
    total += arr[i]

Правило: условие должно быть i < размер, а не i <= размер.

8.3. Типовые задачи на массивы

Задача 1: Поиск максимального элемента

🎯 Алгоритм:

  1. Предположить, что первый элемент — максимальный
  2. Пройти по остальным элементам
  3. Если нашли элемент больше текущего максимума — обновить максимум
#include <stdio.h>

int main() {
    int arr[10] = {45, 78, 23, 91, 56, 12, 88, 34, 67, 50};
    int max = arr[0];  // Предполагаем, что первый — максимальный
    int maxIndex = 0;
    
    for (int i = 1; i < 10; i++) {
        if (arr[i] > max) {
            max = arr[i];
            maxIndex = i;
        }
    }
    
    printf("Максимальный элемент: %d (индекс %d)\n", max, maxIndex);
    return 0;
}
program MaxElement;
const
  arr: array[1..10] of integer = (45, 78, 23, 91, 56, 12, 88, 34, 67, 50);
var
  i, max, maxIndex: integer;
begin
  max := arr[1];            { предполагаем, что первый — максимальный }
  maxIndex := 1;
  for i := 2 to 10 do
    if arr[i] > max then
    begin
      max := arr[i];
      maxIndex := i;
    end;
  writeln('Максимальный элемент: ', max, ' (индекс ', maxIndex, ')');
end.
arr = [45, 78, 23, 91, 56, 12, 88, 34, 67, 50]
biggest = arr[0]          # предполагаем, что первый — максимальный
max_index = 0
for i in range(1, 10):
    if arr[i] > biggest:
        biggest = arr[i]
        max_index = i
print(f"Максимальный элемент: {biggest} (индекс {max_index})")

✅ Вывод:

Максимальный элемент: 91 (индекс 3)

Аналогично: поиск минимума, среднего арифметического, подсчёт элементов с заданным свойством.

Задача 2: Реверс массива (переворот)

💡 Идея:

Поменять местами первый и последний элементы, второй и предпоследний, и т.д.

#include <stdio.h>

int main() {
    int arr[8] = {1, 2, 3, 4, 5, 6, 7, 8};
    int n = 8;
    
    // Реверс
    for (int i = 0; i < n / 2; i++) {
        int temp = arr[i];
        arr[i] = arr[n - 1 - i];
        arr[n - 1 - i] = temp;
    }
    
    // Вывод
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    // Вывод: 8 7 6 5 4 3 2 1
    
    return 0;
}
program Reverse;
const
  n = 8;
var
  arr: array[1..n] of integer;
  i, temp: integer;
begin
  for i := 1 to n do
    arr[i] := i;              { 1 2 3 4 5 6 7 8 }

  { реверс }
  for i := 1 to n div 2 do
  begin
    temp := arr[i];
    arr[i] := arr[n + 1 - i];
    arr[n + 1 - i] := temp;
  end;

  { вывод }
  for i := 1 to n do
    write(arr[i], ' ');
  { вывод: 8 7 6 5 4 3 2 1 }
end.
arr = [1, 2, 3, 4, 5, 6, 7, 8]
n = 8

# реверс
for i in range(n // 2):
    temp = arr[i]
    arr[i] = arr[n - 1 - i]
    arr[n - 1 - i] = temp

# вывод
for i in range(n):
    print(arr[i], end=" ")
# вывод: 8 7 6 5 4 3 2 1

🤔 Почему i < n / 2?

Если пройти весь массив, то элементы поменяются местами дважды и вернутся в исходное положение!

Задача 3: Подсчёт элементов, удовлетворяющих условию

Пример: Сколько чётных чисел в массиве?

#include <stdio.h>

int main() {
    int arr[10] = {15, 22, 31, 44, 57, 60, 73, 86, 91, 100};
    int count = 0;
    
    for (int i = 0; i < 10; i++) {
        if (arr[i] % 2 == 0) {
            count++;
        }
    }
    
    printf("Количество чётных чисел: %d\n", count);
    return 0;
}
program EvenCount;
const
  arr: array[1..10] of integer = (15, 22, 31, 44, 57, 60, 73, 86, 91, 100);
var
  i, count: integer;
begin
  count := 0;
  for i := 1 to 10 do
    if arr[i] mod 2 = 0 then
      count := count + 1;
  writeln('Количество чётных чисел: ', count);
end.
arr = [15, 22, 31, 44, 57, 60, 73, 86, 91, 100]
count = 0
for i in range(10):
    if arr[i] % 2 == 0:
        count += 1
print("Количество чётных чисел:", count)

✅ Вывод:

Количество чётных чисел: 5

Анализ массива данных

Типовые операции с массивами: поиск, фильтрация, анализ

8.4. Практическое применение: от задач к реальности

Пример 1: Анализ температур за неделю

#include <stdio.h>

int main() {
    float temps[7];  // Температуры за 7 дней
    float sum = 0, avg, maxTemp, minTemp;
    int hotDays = 0;
    
    // Ввод данных
    printf("Введите температуры за 7 дней:\n");
    for (int i = 0; i < 7; i++) {
        printf("День %d: ", i + 1);
        scanf("%f", &temps[i]);
        sum += temps[i];
    }
    
    // Средняя температура
    avg = sum / 7;
    
    // Максимум и минимум
    maxTemp = minTemp = temps[0];
    for (int i = 1; i < 7; i++) {
        if (temps[i] > maxTemp) maxTemp = temps[i];
        if (temps[i] < minTemp) minTemp = temps[i];
    }
    
    // Подсчёт жарких дней (>25°C)
    for (int i = 0; i < 7; i++) {
        if (temps[i] > 25.0) hotDays++;
    }
    
    // Вывод результатов
    printf("\n--- Анализ погоды ---\n");
    printf("Средняя температура: %.1f°C\n", avg);
    printf("Максимум: %.1f°C\n", maxTemp);
    printf("Минимум: %.1f°C\n", minTemp);
    printf("Жарких дней: %d\n", hotDays);
    
    return 0;
}
program Weather;
var
  temps: array[1..7] of real;   { температуры за 7 дней }
  sum, avg, maxTemp, minTemp: real;
  hotDays, i: integer;
begin
  sum := 0;
  hotDays := 0;

  { ввод данных }
  writeln('Введите температуры за 7 дней:');
  for i := 1 to 7 do
  begin
    write('День ', i, ': ');
    readln(temps[i]);
    sum := sum + temps[i];
  end;

  { средняя температура }
  avg := sum / 7;

  { максимум и минимум }
  maxTemp := temps[1];
  minTemp := temps[1];
  for i := 2 to 7 do
  begin
    if temps[i] > maxTemp then maxTemp := temps[i];
    if temps[i] < minTemp then minTemp := temps[i];
  end;

  { подсчёт жарких дней (>25°C) }
  for i := 1 to 7 do
    if temps[i] > 25.0 then hotDays := hotDays + 1;

  { вывод результатов }
  writeln;
  writeln('--- Анализ погоды ---');
  writeln('Средняя температура: ', avg:0:1, '°C');
  writeln('Максимум: ', maxTemp:0:1, '°C');
  writeln('Минимум: ', minTemp:0:1, '°C');
  writeln('Жарких дней: ', hotDays);
end.
temps = [0.0] * 7   # температуры за 7 дней
total = 0
hot_days = 0

# ввод данных
print("Введите температуры за 7 дней:")
for i in range(7):
    temps[i] = float(input(f"День {i + 1}: "))
    total += temps[i]

# средняя температура
avg = total / 7

# максимум и минимум
max_temp = temps[0]
min_temp = temps[0]
for i in range(1, 7):
    if temps[i] > max_temp:
        max_temp = temps[i]
    if temps[i] < min_temp:
        min_temp = temps[i]

# подсчёт жарких дней (>25°C)
for i in range(7):
    if temps[i] > 25.0:
        hot_days += 1

# вывод результатов
print("\n--- Анализ погоды ---")
print(f"Средняя температура: {avg:.1f}°C")
print(f"Максимум: {max_temp:.1f}°C")
print(f"Минимум: {min_temp:.1f}°C")
print("Жарких дней:", hot_days)

🌍 Реальное применение

Метеостанции используют массивы для хранения тысяч измерений. Алгоритмы анализа — те же самые, только масштаб больше.

Пример 2: Фильтрация данных (простейший спам-фильтр)

Представим, что у нас есть массив "подозрительности" сообщений (оценка от 0 до 100). Сообщения с оценкой выше 70 — спам.

#include <stdio.h>

int main() {
    int scores[10] = {25, 85, 10, 92, 45, 78, 5, 88, 60, 95};
    int spamCount = 0;
    
    printf("Сообщения-спам (оценка > 70):\n");
    for (int i = 0; i < 10; i++) {
        if (scores[i] > 70) {
            printf("Сообщение #%d (оценка: %d)\n", i + 1, scores[i]);
            spamCount++;
        }
    }
    
    printf("\nВсего спама: %d из 10\n", spamCount);
    return 0;
}
program Spam;
const
  scores: array[1..10] of integer = (25, 85, 10, 92, 45, 78, 5, 88, 60, 95);
var
  i, spamCount: integer;
begin
  spamCount := 0;
  writeln('Сообщения-спам (оценка > 70):');
  for i := 1 to 10 do
    if scores[i] > 70 then
    begin
      writeln('Сообщение #', i, ' (оценка: ', scores[i], ')');
      spamCount := spamCount + 1;
    end;
  writeln;
  writeln('Всего спама: ', spamCount, ' из 10');
end.
scores = [25, 85, 10, 92, 45, 78, 5, 88, 60, 95]
spam_count = 0

print("Сообщения-спам (оценка > 70):")
for i in range(10):
    if scores[i] > 70:
        print(f"Сообщение #{i + 1} (оценка: {scores[i]})")
        spam_count += 1

print(f"\nВсего спама: {spam_count} из 10")

✅ Вывод:

Сообщения-спам (оценка > 70):
Сообщение #2 (оценка: 85)
Сообщение #4 (оценка: 92)
Сообщение #6 (оценка: 78)
Сообщение #8 (оценка: 88)
Сообщение #10 (оценка: 95)

Всего спама: 5 из 10

🔗 Связь с реальностью

Gmail, ВКонтакте, Telegram используют сложные алгоритмы машинного обучения, но базовая логика — та же: оценка каждого сообщения и фильтрация по порогу.

8.5. Двумерные массивы (матрицы) — краткий обзор

Одномерный массив — это последовательность. Двумерный массив — это таблица (матрица).

int matrix[3][4];  // 3 строки, 4 столбца
var
  matrix: array[1..3, 1..4] of integer;   { 3 строки, 4 столбца }
matrix = [[0] * 4 for i in range(3)]   # 3 строки, 4 столбца

📊 Визуализация:

matrix[0][0]  matrix[0][1]  matrix[0][2]  matrix[0][3]
matrix[1][0]  matrix[1][1]  matrix[1][2]  matrix[1][3]
matrix[2][0]  matrix[2][1]  matrix[2][2]  matrix[2][3]

📝 Инициализация:

int matrix[3][4] = {
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12}
};
const
  matrix: array[1..3, 1..4] of integer = (
    (1, 2, 3, 4),
    (5, 6, 7, 8),
    (9, 10, 11, 12)
  );
matrix = [
    [1, 2, 3, 4],
    [5, 6, 7, 8],
    [9, 10, 11, 12]
]

🔄 Обход (вложенные циклы):

for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 4; j++) {
        printf("%d ", matrix[i][j]);
    }
    printf("\n");
}
for i := 1 to 3 do
begin
  for j := 1 to 4 do
    write(matrix[i, j], ' ');
  writeln;
end;
for i in range(3):
    for j in range(4):
        print(matrix[i][j], end=" ")
    print()

🌍 Применение:

  • Изображения (двумерный массив пикселей)
  • Игровое поле (шахматная доска, тетрис)
  • Таблицы данных (расписание, оценки)
  • Научные расчёты (линейная алгебра)
Двумерный массив как таблица

Двумерный массив — организация данных в виде таблицы с двухуровневой адресацией

✨ Ключевые выводы по §8

Массивы — фундаментальная структура данных. Они позволяют эффективно хранить и обрабатывать большие объёмы однотипных данных. Индексация с нуля — стандарт в программировании. Массив из N элементов имеет индексы от 0 до N-1. Выход за границы — критическая ошибка. Типовые операции над массивами: поиск, подсчёт, преобразование, вычисление агрегатов. Массивы — основа для сложных структур. Строки, матрицы, изображения, звук — всё это массивы или их вариации.

🤔 Проверь себя (§8)

Вопрос 1: Какой индекс имеет последний элемент массива int data[50]? Почему?

Вспомни, что индексация начинается с нуля.

Вопрос 2: Что выведет этот код?
int arr[5] = {2, 4, 6, 8, 10};
int result = arr[1] + arr[3];
printf("%d\n", result);
const
  arr: array[1..5] of integer = (2, 4, 6, 8, 10);
var
  result: integer;
...
result := arr[2] + arr[4];   { в Паскале индексы с 1 }
writeln(result);
arr = [2, 4, 6, 8, 10]
result = arr[1] + arr[3]
print(result)
Вопрос 3: Напиши условие цикла для обхода массива float values[100] так, чтобы не было выхода за границы.

Подсказка: условие должно учитывать размер массива.

Вопрос 4: Дан массив оценок студентов. Как определить, сколько из них получили "отлично" (оценка >= 90)? Опиши алгоритм словами.

Какую переменную-счётчик нужно использовать? Какое условие проверять?

Мини-кейс: Ты разрабатываешь систему учёта посещаемости. У тебя 30 студентов и 5 занятий в неделю. Какую структуру данных ты выберешь для хранения информации о посещениях? Обоснуй свой выбор.

Подсказка: подумай о двумерных массивах.

🎯 Практические задания

✍️ Задание 1: Базовые операции

Напиши программу, которая:

  1. Создаёт массив из 10 элементов
  2. Заполняет его числами от 1 до 10
  3. Выводит все элементы
  4. Находит и выводит сумму всех элементов

🔍 Задание 2: Поиск элемента

Дан массив: {5, 12, 8, 19, 3, 15, 7}

Напиши программу, которая находит позицию (индекс) элемента со значением 19.

🎨 Задание 3: Анализ данных

Создай массив с оценками 10 студентов (от 0 до 100). Программа должна:

  • Найти среднюю оценку
  • Подсчитать, сколько студентов получили >= 60
  • Вывести максимальную и минимальную оценки

🔄 Задание 4: Преобразование

Напиши программу, которая удваивает все элементы массива.

Пример:
Было: {1, 2, 3, 4, 5}
Стало: {2, 4, 6, 8, 10}

💭 Итоговое размышление

Алгоритмы и структуры данных — это не просто абстрактные концепции. Это инструменты мышления, которые помогают:

  • Декомпозировать сложные задачи на простые шаги
  • Оптимизировать процессы (как в коде, так и в жизни)
  • Автоматизировать рутину и фокусироваться на творчестве
  • Понимать логику работы цифровых систем, которые нас окружают

Каждый раз, когда ты открываешь приложение, смотришь видео, отправляешь сообщение — за кулисами работают тысячи алгоритмов, оперирующих массивами, деревьями, графами. И теперь ты знаешь, как они устроены изнутри.

🚀 Следующий шаг

От простых массивов мы перейдём к более сложным структурам — спискам, стекам, очередям, деревьям. А затем — к алгоритмам поиска, сортировки, графов. Каждая новая концепция будет опираться на то, что ты уже понял.

Продолжай исследовать! 🚀

Программирование как путь познания

Программирование — это путешествие от простого к сложному, от концепции к реализации

🎓 Отличная работа! Вы освоили фундаментальные концепции программирования — на Си, Паскале или Python. В следующих уроках мы будем развивать эти знания, изучая более сложные алгоритмы и структуры данных.

Проверь себя

5 случайных вопросов по этому уроку — при следующем открытии будут другие. Ответы проверяются сразу, без регистрации.