Обучение Python: задачи и их решения

Раздел: Обучение Python -> Задачи по программированию

Основные задачи по программированию на Python

Как подсчитать количество вхождений каждого элемента в списке наиболее эффективно?

Основным решением является использование класса Counter из модуля collections. Он создает словарь, где ключами выступают элементы списка, а значениями – их количество. Этот подход оптимален по времени и лаконичен.

from collections import Counter

nums = [1, 2, 2, 3, 3, 3, 4]
counter = Counter(nums)
print(dict(counter))  # {1: 1, 2: 2, 3: 3, 4: 1}

Python programming tasks (задачи по программированию на python)

Как решить задачу подсчета без импорта дополнительных модулей?

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

nums = [1, 2, 2, 3, 3, 3, 4]
counts = {}
for num in nums:
    counts[num] = counts.get(num, 0) + 1
print(counts)

задачи на python c решением (задачи на python с решением)

Как выполнить подсчет с помощью метода count() для каждого уникального элемента?

Этот вариант удобен, когда список небольшой, так как каждый вызов count() проходит по всему списку.

nums = [1, 2, 2, 3, 3, 3, 4]
unique = set(nums)
counts = {item: nums.count(item) for item in unique}
print(counts)

Как получить счетчик с сохранением порядка появления элементов?

Для сохранения порядка (Python 3.7+) можно использовать dict.fromkeys или OrderedDict.

from collections import OrderedDict

nums = [1, 2, 2, 3, 3, 3, 4]
counts = OrderedDict()
for num in nums:
    counts[num] = counts.get(num, 0) + 1
print(list(counts.items()))

Типичные ошибки и проблемы:

  • Использование изменяемых типов (список, словарь) в качестве ключей для Counter вызовет TypeError. Ключи должны быть хешируемыми (числа, строки, кортежи).
  • При использовании метода count() в цикле по уникальным элементам возникает квадратичная сложность O(n*m), что неприемлемо для больших данных.
  • При ручном подсчете через словарь часто забывают обработать первый вход (использовать get или условие).

Цели и случаи использования: метод Counter подходит для быстрого анализа частотности в больших наборах данных. Ручной словарь – для обучения. count() – для очень малых списков, когда импорт нежелателен.


Как найти все простые числа до заданного N с наилучшей производительностью?

Самым эффективным решением является решето Эратосфена. Оно работает за O(n log log n) и требует памяти O(n).

def sieve(n):
    is_prime = [True] * (n+1)
    is_prime[0] = is_prime[1] = False
    for p in range(2, int(n**0.5)+1):
        if is_prime[p]:
            for multiple in range(p*p, n+1, p):
                is_prime[multiple] = False
    return [i for i, prime in enumerate(is_prime) if prime]

print(sieve(30))

Как проверить, является ли число простым, без построения решета?

Используется проверка делителей от 2 до квадратного корня числа. Этот метод подходит для одиночных проверок.

def is_prime(n):
    if n < 2:
        return False
    for i in range(2, int(n**0.5)+1):
        if n % i == 0:
            return False
    return True

# Проверим числа 17, 18
print(is_prime(17), is_prime(18))

Как можно модифицировать решето для работы с большими N, экономя память?

Используется сегментированное решето или хранение только нечётных чисел. Ниже пример с битовой маской через bytearray.

def sieve_bytearray(n):
    # Храним только нечётные числа
    size = (n+1)//2
    is_prime = bytearray(b'\x01') * size
    for i in range(3, int(n**0.5)+1, 2):
        if is_prime[i//2]:
            start = i*i
            step = i*2
            is_prime[start//2::step] = b'\x00' * ((n - start)//step + 1)
    primes = [2] + [i*2+1 for i, val in enumerate(is_prime) if val and i>0]
    return primes

print(sieve_bytearray(30))

Типичные ошибки и проблемы:

  • В решете Эратосфена часто ошибаются с начальным индексом для вычёркивания: нужно начинать с p*p, а не с 2*p.
  • При проверке делителей забывают про sqrt или проверяют все делители до n, что крайне неэффективно.
  • Для больших N решето может потребовать много памяти (список bool). Использование bytearray или array('b') снижает нагрузку.

Цели и случаи использования: решето Эратосфена – для массового поиска простых чисел (например, в задачах перебора). Проверка делителей – для одиночных проверок или когда N невелико. Экономичные версии – для ограниченной памяти.


Как отсортировать список словарей по значению определённого ключа?

Наиболее универсальное решение – функция sorted() с параметром key, в котором используется lambda-функция.

data = [{'name': 'Alice', 'age': 30},
        {'name': 'Bob', 'age': 25},
        {'name': 'Charlie', 'age': 35}]
sorted_data = sorted(data, key=lambda x: x['age'])
print(sorted_data)

Как отсортировать список на месте, не создавая новый объект?

Используется метод list.sort(), который изменяет исходный список.

data = [{'name': 'Alice', 'age': 30},
        {'name': 'Bob', 'age': 25},
        {'name': 'Charlie', 'age': 35}]
data.sort(key=lambda x: x['age'])
print(data)

Как упростить сортировку по атрибуту, если ключ – это строковый атрибут?

Можно использовать operator.attrgetter (для объектов) или itemgetter (для словарей).

from operator import itemgetter

data = [{'name': 'Alice', 'age': 30},
        {'name': 'Bob', 'age': 25},
        {'name': 'Charlie', 'age': 35}]
sorted_data = sorted(data, key=itemgetter('age'))
print(sorted_data)

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

В lambda или itemgetter можно указать кортеж ключей.

data = [{'name': 'Alice', 'age': 30},
        {'name': 'Bob', 'age': 25},
        {'name': 'Charlie', 'age': 25}]
sorted_data = sorted(data, key=lambda x: (x['age'], x['name']))
print(sorted_data)
# Сортировка по убыванию возраста: reverse=True

Типичные ошибки и проблемы:

  • Попытка сортировать словарь по ключу, которого нет в некоторых элементах, вызовет KeyError. Следует использовать .get(key, default).
  • Сортировка по строковому числу (например, '25') приведёт к лексикографическому порядку, а не числовому. Нужно привести к числу.
  • При использовании sort() с большим списком изменяется исходный объект, что может быть неожиданным.

Цели и случаи использования: sorted() – когда нужен новый отсортированный список. list.sort() – когда исходный список можно изменить. itemgetter – для читаемости и, возможно, небольшого прироста скорости на больших данных.


Как прочитать числа из текстового файла и вычислить их среднее арифметическое?

Базовое решение – открыть файл с помощью with, прочитать строки, преобразовать каждую в число и посчитать среднее. Если строки содержат лишние пробелы, их удаляют.

# Предположим, файл numbers.txt содержит:
# 10
# 20
# 30
# 40

with open('numbers.txt', 'r') as f:
    numbers = [int(line.strip()) for line in f if line.strip()]
average = sum(numbers) / len(numbers)
print(average)  # 25.0

Как обработать файл, в котором числа записаны в одну строку через пробел?

Можно прочитать всю строку и разделить по пробелам, затем преобразовать.

with open('numbers_line.txt', 'r') as f:
    line = f.read().strip()
    numbers = list(map(int, line.split()))
average = sum(numbers) / len(numbers) if numbers else 0
print(average)

Как учесть возможные ошибки преобразования (нечисловые строки)?

Следует использовать блок try-except или фильтрацию с str.isdigit.

def safe_int(s):
    try:
        return int(s.strip())
    except ValueError:
        return None

with open('messy.txt', 'r') as f:
    numbers = [val for line in f for val in [safe_int(line.strip())] if val is not None]
if numbers:
    average = sum(numbers) / len(numbers)
    print(average)
else:
    print('Нет корректных чисел')

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

Можно обрабатывать файл построчно, накапливая сумму и количество.

total = 0
count = 0
with open('huge.txt', 'r') as f:
    for line in f:
        if line.strip():
            try:
                total += float(line.strip())
                count += 1
            except ValueError:
                pass
average = total / count if count else 0
print(average)

Типичные ошибки и проблемы:

  • Файл может не существовать – нужно обрабатывать FileNotFoundError.
  • Пустые строки или строки с пробелами приводят к ошибке преобразования. Рекомендуется проверять line.strip() перед конвертацией.
  • Смешивание типов (целые и вещественные) лучше приводить к float для корректного среднего.
  • При использовании sum() для пустого списка возникает деление на ноль. Необходима проверка длины.

Цели и случаи использования: чтение из файла – стандартная задача для обработки данных. Метод с построчным чтением подходит для больших файлов. Обработка ошибок необходима для «грязных» данных.


Как сгенерировать пароль заданной длины, используя буквы, цифры и символы?

Безопасный способ – использование модуля secrets, предназначенного для криптостойкой генерации. Комбинация символов задается через string.

import secrets
import string

def generate_password(length=12):
    alphabet = string.ascii_letters + string.digits + string.punctuation
    return ''.join(secrets.choice(alphabet) for _ in range(length))

print(generate_password(16))

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

Можно сначала выбрать по одному символу из каждой категории, а затем заполнить остаток случайными.

import secrets
import string

def strong_password(length=12):
    if length < 4:
        raise ValueError('Длина пароля должна быть не менее 4')
    # Гарантированный минимум
    lower = secrets.choice(string.ascii_lowercase)
    upper = secrets.choice(string.ascii_uppercase)
    digit = secrets.choice(string.digits)
    punct = secrets.choice(string.punctuation)
    # Заполнение остатка
    other = ''.join(secrets.choice(string.ascii_letters + string.digits + string.punctuation) for _ in range(length-4))
    # Перемешиваем, чтобы порядок не был предсказуем
    lst = list(lower + upper + digit + punct + other)
    secrets.SystemRandom().shuffle(lst)
    return ''.join(lst)

print(strong_password(12))

Как использовать менее безопасный, но более простой модуль random?

Для нечувствительных приложений (например, генерация временного токена) можно применить random.choice, но он не криптостоек.

import random
import string

def simple_password(length=8):
    chars = string.ascii_letters + string.digits
    return ''.join(random.choice(chars) for _ in range(length))

print(simple_password(10))

Типичные ошибки и проблемы:

  • Использование random для паролей – небезопасно. Для аутентификации и токенов нужен secrets.
  • Если алфавит слишком мал (например, только цифры), энтропия пароля низкая.
  • При генерации с гарантированным включением символов легко забыть перемешать, и пароль будет начинаться с фиксированного набора.

Цели и случаи использования: secrets – для реальных паролей, токенов, ключей. random – для тестовых данных, где безопасность не важна. Гарантированное включение символов необходимо, когда политика паролей требует минимум по одному символу из каждой категории.

Расширенные примеры и нестандартные сценарии

Подсчёт частоты слов в тексте с очисткой пунктуации

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

Пример
import re
from collections import Counter

text = """Python! Это язык программирования. Python очень популярен; многие его изучают.
Программирование на Python – это интересно."""
# Убираем знаки препинания, оставляем только буквы и пробелы
clean_text = re.sub(r'[^\w\s]', '', text).lower()
words = clean_text.split()
word_counts = Counter(words)
print(word_counts.most_common(5))
[('python', 2), ('это', 2), ('программирования', 1), ('язык', 1), ('популярен', 1)]

Сравнение производительности решета Эратосфена и простой проверки делителей

Для больших N разница во времени становится колоссальной. Приведём код с замером времени.

Пример
import time

def simple_sieve(n):
    is_prime = [True]*(n+1)
    is_prime[0]=is_prime[1]=False
    for i in range(2, int(n**0.5)+1):
        if is_prime[i]:
            for j in range(i*i, n+1, i):
                is_prime[j]=False
    return [i for i, v in enumerate(is_prime) if v]

def naive_primes(n):
    def is_prime(x):
        if x<2: return False
        for i in range(2, int(x**0.5)+1):
            if x%i==0: return False
        return True
    return [i for i in range(2, n+1) if is_prime(i)]

N=100000
start=time.time()
s1=simple_sieve(N)
print('Sieve time:', time.time()-start)
start=time.time()
s2=naive_primes(N)
print('Naive time:', time.time()-start)
print('Результаты совпадают:', s1==s2)
Sieve time: 0.012
Naive time: 1.345
Результаты совпадают: True

Сортировка списка объектов класса с использованием attrgetter

Если данные представлены не словарями, а пользовательскими объектами, применяется attrgetter.

Пример
from operator import attrgetter

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def __repr__(self):
        return f'{self.name}({self.age})'

people = [Person('Alice',30), Person('Bob',25), Person('Charlie',35)]
sorted_people = sorted(people, key=attrgetter('age'), reverse=True)
print(sorted_people)  # [Charlie(35), Alice(30), Bob(25)]

Чтение CSV-файла с числами и вычисление среднего по столбцу

Файл data.csv содержит столбцы: name, age, salary. Нужно найти среднюю зарплату. Используем модуль csv.

Пример
import csv

total_salary = 0
count = 0
with open('data.csv', newline='', encoding='utf-8') as f:
    reader = csv.DictReader(f)
    for row in reader:
        salary = float(row['salary'])
        total_salary += salary
        count += 1
average_salary = total_salary / count if count else 0
print(f'Средняя зарплата: {average_salary:.2f}')
Средняя зарплата: 75000.50

Генерация пароля с исключением похожих символов (например, 0 и O, 1 и l)

Повышение читаемости пароля путём удаления двусмысленных символов.

Пример
import secrets
import string

def readable_password(length=12):
    # Исключаем символы, которые легко спутать
    alphabet = string.ascii_letters + string.digits
    ambiguous = '0O1lI'
    alphabet = ''.join(c for c in alphabet if c not in ambiguous)
    return ''.join(secrets.choice(alphabet) for _ in range(length))

print(readable_password(10))
Dk3pWx9yQv

Задачи по программированию на Python - comments

En
Python programming tasks (python)