Python Programming Exercises: 25 Classic Problems with Solutions

Narcissistic Numbers

A narcissistic number (also known as an Armstrong number) is a three-digit number where the sum of each digit raised to the power of three equals the original number. For instance, 153 is narcissistic because 1³ + 5³ + 3³ = 153.

for num in range(100, 1000):
    hundreds = num // 100
    tens = (num // 10) % 10
    units = num % 10
    
    if hundreds ** 3 + tens ** 3 + units ** 3 == num:
        print(f"{num} is a narcissistic number")

Output: 153, 370, 371, 407

Four-Leaf Rose Numbers

A four-leaf rose number is the four-digit version of Armstrong numbers. It requires that the sum of each digit raised to the fourth power equals the number itself.

for num in range(1000, 10000):
    thousands = num // 1000
    hundreds = (num // 100) % 10
    tens = (num // 10) % 10
    units = num % 10
    
    if thousands ** 4 + hundreds ** 4 + tens ** 4 + units ** 4 == num:
        print(f"{num} is a four-leaf rose number")

Output: 1634, 8208, 9474

Reversing a String

Method 1: Slice Notation

text = input("Enter a string: ")
print(text[::-1])

Method 2: Loop with List Building

text = input("Enter a string: ")
result = []
for idx in range(len(text) - 1, -1, -1):
    result.append(text[idx])
print(''.join(result))

Number Guessing Game

Generate a random number between 0 and 100. The player gets 10 attempts to guess it, receiving hints whether their guess is higher or lower than the target.

import random

target = random.randint(0, 100)

for attempt in range(10):
    guess = int(input("Enter your guess: "))
    
    if guess > target:
        print("Too high")
    elif guess < target:
        print("Too low")
    else:
        print(f"Correct! You used {attempt + 1} attempts")
        break
    
    print(f"Remaining attempts: {9 - attempt}")
else:
    print("Game over - you didn't find the number")

Chicken Purchasing Problem

With 100 yuan, purchase exactly 100 chickens. Roosters cost 5 yuan each, hens cost 3 yuan each, and chicks cost 1 yuan for three. Find all possible combinations.

Mathematical constraints:

  • x + y + z = 100 (total chickens)
  • 5x + 3y + z/3 = 100 (total cost)
count = 0
for roosters in range(1, 20):
    for hens in range(1, 33):
        chicks = 100 - roosters - hens
        if chicks > 0 and 5 * roosters + 3 * hens + chicks / 3 == 100:
            count += 1
            print(f"Solution {count}: Roosters={roosters}, Hens={hens}, Chicks={chicks}")

Output: Three valid combinations exist for this problem.

Day of Year Calculator

Input a date (year, month, day) and determine whether it's a leap year, then calculate which day of the year it represents.

Leap year conditions:

  • Divisible by 4 but not by 100, OR
  • Divisible by 400
year = int(input("Year: "))
month = int(input("Month: "))
day = int(input("Day: "))

days_in_month = [31, 29 if (year % 4 == 0 and year % 100 != 0) or year % 400 == 0 else 28,
                 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]

total_days = day
for m in range(month - 1):
    total_days += days_in_month[m]

leap_status = "leap" if (year % 4 == 0 and year % 100 != 0) or year % 400 == 0 else "common"
print(f"{year} is a {leap_status} year")
print(f"{month}/{day} is day {total_days} of the year")

Monkey and Peaches

A monkey collects some peaches. Each morning, it eats half of the remaining peaches plus one additional peach. After 10 mornings, only 1 peach remains. Calculate the initial number of peaches.

Working backwards from day 10: if p peaches remain before eating on day 9, then p/2 - 1 = 1, giving p = 4.

remaining = 1
print(f"Morning 10: {remaining} peaches")

for d in range(9, 0, -1):
    remaining = (remaining + 1) * 2
    print(f"Morning {d}: {remaining} peaches")

print(f"Initial count: {remaining} peaches")

Output: The monkey initially collected 1534 peaches.

Bubble Sort

The algorithm repeatedly steps through the list, comparing adjacent elements and swapping them if they are in the wrong order. The pass through the list is repeated until the list is sorted.

import random

data = [random.randint(0, 99) for _ in range(6)]
n = len(data)

print(f"Unsorted: {data}")

for i in range(n - 1):
    for j in range(n - i - 1):
        if data[j] > data[j + 1]:
            data[j], data[j + 1] = data[j + 1], data[j]

print(f"Sorted: {data}")

Binary Search

Binary search efficiently finds a target in a sorted sequence by repeatedly dividing the search interval in half.

Iterative Approach

def binary_search(arr, target):
    low, high = 0, len(arr) - 1
    attempts = 0
    
    while low <= high:
        mid = (low + high) // 2
        attempts += 1
        
        if target > arr[mid]:
            low = mid + 1
        elif target < arr[mid]:
            high = mid - 1
        else:
            return mid, attempts
    
    return -1, attempts

sorted_array = [5, 7, 11, 22, 27, 33, 39, 52, 58]
index, count = binary_search(sorted_array, 11)
print(f"Found at index {index} in {count} attempts")

Recursive Approach

def binary_search_recursive(arr, target, low, high):
    if low <= high:
        mid = (low + high) // 2
        if target < arr[mid]:
            return binary_search_recursive(arr, target, low, mid - 1)
        elif target > arr[mid]:
            return binary_search_recursive(arr, target, mid + 1, high)
        else:
            return mid
    return -1

result = binary_search_recursive([5, 7, 11, 22, 27, 33, 39, 52, 58], 11, 0, 8)
print(f"Index: {result}")

Selection Sort

The algorithm divides the input list into two parts: sorted and unsorted. It repeatedly selects the minimum element from the unsorted portion and moves it to the sorted portion.

import random

items = [random.randint(1, 100) for _ in range(8)]
size = len(items)

print(f"Unsorted: {items}")

for i in range(size - 1):
    min_pos = i
    for j in range(i + 1, size):
        if items[min_pos] > items[j]:
            min_pos = j
    items[i], items[min_pos] = items[min_pos], items[i]
    print(f"Pass {i + 1}: {items}")

print(f"Final sorted: {items}")

Rock Paper Scissors Game

A game where both player and computer start with 100 points. Winning adds 10 points, losing subtracts 10. The game ends when either player reaches 200 (win) or 0 (lose).

import random

choices = {1: "Scissors", 2: "Rock", 3: "Paper"}
points = 100

print("=" * 40)
print("       ROCK PAPER SCISSORS")
print("1=Scissors  2=Rock  3=Paper")

while True:
    computer = random.randint(1, 3)
    player_input = input("Your choice: ")
    
    if player_input not in ('1', '2', '3'):
        print("Invalid input, try again")
        continue
    
    player = int(player_input)
    print(f"Computer: {choices[computer]} | You: {choices[player]}")
    
    if (player == 1 and computer == 3) or \
       (player == 2 and computer == 1) or \
       (player == 3 and computer == 2):
        points += 10
        print(f"You win! Score: {points}")
    elif player == computer:
        print(f"Draw! Score: {points}")
    else:
        points -= 10
        print(f"You lose! Score: {points}")
    
    if points >= 200:
        print("Congratulations - you win!")
        break
    elif points <= 0:
        print("Game over - you lose")
        break

Happy Numbers

A happy number is one where repeatedly replacing the number with the sum of squares of its digits eventually yields 1. If it never reaches 1, it's an unhappy number.

Example with 19: 19 → 82 → 68 → 100 → 1 (happy)

def digit_square_sum(n):
    total = 0
    for digit in str(n):
        total += int(digit) ** 2
    return total

def is_happy(n):
    seen = set()
    while digit_square_sum(n) not in seen:
        n = digit_square_sum(n)
        seen.add(n)
    return n == 1

num = int(input("Enter a number: "))
print("Happy number" if is_happy(num) else "Not a happy number")

Age Puzzle - Two Sisters

The product of two sisters' ages is 6 times their sum, and their age difference is at most 8 years. Find the younger sister's age.

for older in range(1, 100):
    for younger in range(1, older):
        if older * younger == 6 * (older + younger) and older - younger <= 8:
            print(f"Older: {older}, Younger: {younger}")

Solution: The sisters are 15 and 10 years old.

Age Puzzle - Mathematician Wiener

A mathematician's age has a cube that is 4 digits and a fourth power that is 6 digits. These 10 digits contain each digit 0-9 exactly once. Find his age.

for age in range(10, 30):
    cube = str(age ** 3)
    fourth = str(age ** 4)
    
    if len(cube) == 4 and len(fourth) == 6:
        if len(set(cube + fourth)) == 10:
            print(f"Age: {age}")
            print(f"Cube: {cube}, Fourth power: {fourth}")

The mathematician was 18 years old (18³=5832, 18⁴=104976).

Custom Split Function

Implement a string splitting function similar to Python's built-in split(), handling a separator and optional limit parameter.

def custom_split(text, delimiter="", limit=0):
    parts = []
    start = 0
    splits = 0
    
    for idx, char in enumerate(text):
        if limit > 0 and splits == limit:
            parts.append(text[start:])
            break
            
        if char == delimiter:
            parts.append(text[start:idx])
            start = idx + 1
            splits += 1
        elif idx == len(text) - 1:
            parts.append(text[start:idx + 1])
    
    return parts

print(custom_split("life-is-short-you-need-python", "-"))
# ['life', 'is', 'short', 'you', 'need', 'python']

print(custom_split("life-is-short-you-need-python", "-", 2))
# ['life', 'is', 'short-you-need-python']

Dayan Sequence

An ancient Chinese sequecne: 0, 2, 4, 8, 12, 18, 24, 32, 40, 50, ... The pattern: even positions are n²/2, odd positions are (n²-1)/2.

for n in range(1, 101):
    if n % 2 == 0:
        value = int((n ** 2) / 2)
    else:
        value = int((n ** 2 - 1) / 2)
    print(value)

Character Frequency Analysis

Given a string, find the character that appears most frequently and count its occurrences.

def most_frequent(text):
    frequencies = {}
    for ch in text:
        frequencies[ch] = frequencies.get(ch, 0) + 1
    
    max_char = max(frequencies, key=frequencies.get)
    return max_char, frequencies[max_char]

char, count = most_frequent("HelloWorld")
print(f"Most frequent: '{char}', count: {count}")

Output: l appears 3 times

Diamond Pattern Using Stack

Print a diamond of given side length using stack-based logic for the lower half.

def print_diamond(size):
    stack = []
    
    for row in range(1, 2 * size):
        if row <= size:
            pattern = ' ' * (size - row) + '*' * (2 * row - 1)
            if row != size:
                stack.append(pattern)
            print(pattern)
        else:
            print(stack.pop())

print_diamond(5)

Output forms a diamond shape with the upper half printed directly and lower half popped from stack.

Understanding Recursion

A recursive function calls itself. Every recursive call adds a new frame to the stack, and there must be a base case to terminate.

Three elements of recursion design:

  1. Define the function's purpose clearly
  2. Identify the termination condition
  3. Establish the recursive relationship
def recursive_demo(n):
    if n == 0:
        return
    print(f"Before recursive call: {n}")
    recursive_demo(n - 1)
    print(f"After recursive call: {n}")

recursive_demo(5)

Output demonstrates how function calls stack up before unwinding on return.

Fibonacci Sequence with Recursion

The Fibonacci sequence: 1, 1, 2, 3, 5, 8, 13, 21, 34, ... Each term (from the third onward) equals the sum of the two preceding terms.

Formula: f(n) = f(n-1) + f(n-2)

def fibonacci(n):
    if n <= 2:
        return 1
    return fibonacci(n - 1) + fibonacci(n - 2)

for i in range(1, 11):
    print(f"fib({i}) = {fibonacci(i)}")

Recursion and stack memory: Each function call pushes a frame onto the call stack. The first call returns last, following LIFO (Last In, First Out) principles.

Finding Maximum of Three Numbers

Determine the largest of three numbers using only conditional statements, without built-in functions or data structures.

a, b, c = 10, 6, 18

max_val = a if a > b else b
max_val = max_val if max_val > c else c

print(max_val)

Perfect Numbers

A perfect number equals the sum of its proper divisors (excluding itself). For example, 6 = 1 + 2 + 3.

def divisor_sum(n):
    total = 0
    for i in range(1, n):
        if n % i == 0:
            total += i
    return total

for num in range(1, 1000):
    if num == divisor_sum(num):
        print(f"{num} is perfect")

Output: 6, 28, 496

Factorial Sum

Calculate the sum of factorials: 1! + 2! + 3! + ... + 10!

Formula: f(n) = n × f(n-1)

def factorial(n):
    if n < 2:
        return 1
    return n * factorial(n - 1)

total = sum(factorial(i) for i in range(1, 11))
print(f"Sum of factorials: {total}")

Valid Parentheses

Determine if a string containing '(', ')', '{', '}', '[', ']' is valid. Valid means matching pairs in correct order.

Method 1: String Replacement

def is_valid_v1(s):
    if len(s) % 2 == 1:
        return False
    
    while '()' in s or '[]' in s or '{}' in s:
        s = s.replace('()', '').replace('[]', '').replace('{}', '')
    
    return len(s) == 0

print(is_valid_v1("()"))        # True
print(is_valid_v1("()[]{}"))    # True
print(is_valid_v1("([])"))      # True

Method 2: Stack-Based

def is_valid_v2(s):
    if len(s) % 2 == 1:
        return False
    
    stack = []
    pairs = {')': '(', '}': '{', ']': '['}
    
    for char in s:
        if char in pairs:
            if not stack or stack[-1] != pairs[char]:
                return False
            stack.pop()
        else:
            stack.append(char)
    
    return len(stack) == 0

print(is_valid_v2("(){}[({[]})]"))  # True
print(is_valid_v2("(){}[({[)}]"))    # False

Palindrome Numbers

A palindrome reads the same forwards and backwards. Examples: 121, 1221, 12321.

Method 1: String Reversal

def is_palindrome_str(n):
    if n < 0 or (n > 0 and n % 10 == 0):
        return False
    return str(n) == str(n)[::-1]

print(is_palindrome_str(121))   # True
print(is_palindrome_str(-121))  # False

Method 2: Reverse Half the Number

def is_palindrome_math(n):
    if n < 0 or (n > 0 and n % 10 == 0):
        return False
    
    reversed_half = 0
    while n > reversed_half:
        reversed_half = reversed_half * 10 + n % 10
        n //= 10
    
    return n == reversed_half or n == reversed_half // 10

print(is_palindrome_math(1221))    # True
print(is_palindrome_math(123321))  # True
print(is_palindrome_math(12321))   # True

Tags: python programming algorithms Practice Problems Data Structures

Posted on Fri, 26 Jun 2026 16:31:22 +0000 by Mateobus