Python Practice Exercises – Part 6: Fractions, GCD, and Functional Programming

Python Practice Exercises – Part 6

Try to solve each exercise on your own first.
After the exercise list, scroll down to the “Solutions” section to compare your approach.

Exercises

Exercise 1 – Digit-Cancelling Fractions

Normally, you may not just “cancel digits” in a fraction.
For example, 18/12 is obviously not the same as 8/2 (if you just remove the 1).

However, there are rare two-digit fractions where this wrong-looking “digit cancelling” coincidentally gives the correct result.
For example, 65/26 has the same value as 5/2 if you cancel the digit 6 from numerator and denominator.

Write a program that finds all pairs of two-digit numbers (numerator and denominator) where this kind of digit cancelling works.

Requirements:

  • Only use two-digit numbers (10–99).
  • Only consider values less than 1 (numerator < denominator).
  • Exclude trivial cases such as fractions with zeros like 10/70 or obviously reducible ones that just cancel a trailing zero.

Exercise 2 – Greatest Common Divisor (GCD) – Recursive

Write a recursive function that computes the greatest common divisor (GCD) of two numbers, using the Euclidean algorithm:

  • Divide the larger number by the smaller one.
  • If the remainder is 0, the smaller number is the GCD.
  • Otherwise, repeat the process with the smaller number and the remainder.

Example:

  • 80 / 65 → remainder 15 → continue with (65, 15)
  • 65 / 15 → remainder 5 → continue with (15, 5)
  • 15 / 5 → remainder 0 → GCD is 5

Exercise 3 – Character Frequencies

Write a function char_frequencies() that, for a given string, returns a dictionary mapping each character to the number of times it appears.

Example:

print(char_frequencies('Strawberry'))
# Example output (order may vary):
# {'S': 1, 't': 1, 'r': 3, 'a': 1, 'w': 1, 'b': 1, 'e': 1, 'y': 1}

Exercise 4 – Straight-Line Distance (Luftlinie)

Write a function that computes the distance between two points in the plane.

  • A point is given as (x, y), for example P1 = (x1, y1), P2 = (x2, y2).
  • Use it to compute distances like:

Verification examples:

  • P1 = (-4, 2); P2 = (-1, 6) → distance = 5
  • P1 = (-1, 6); P2 = (4, 18) → distance = 13

Example with Swiss coordinates (CH1903):

  • Zell = (704.4, 256.2)
  • Neuburg = (693.3, 261.4)
    They should be about 12.26 km apart.

Exercise 5 – Steganography

Steganography is the technique of hiding secret information inside another text.

Write a function hide(text, n=1) that obfuscates a plaintext string text as follows:

  • Convert the string to uppercase.
  • After each letter, insert n random uppercase letters.
  • Spaces should remain spaces.
  • n is optional and defaults to 1.

Example calls (your random output will differ):

print(hide('Meet at noon'))
# e.g.: MAFEEZTB AT QFXNTOJONZ

print(hide('Meet at noon', 2))
# e.g.: MABXEEJTTK AT HQLXOOFONZQ

Exercise 6 – Extract Vowels

From a given string like "Magermilchjoghurt", extract all vowels and join them into a new string.

Solve this in two ways:

  1. Using a classic loop.
  2. Using a higher-order function (e.g. filter() with lambda).

Exercise 7 – Bookstore Orders with map() and lambda

You have the following list of orders:

orders = [
    ["34587", "Learning Python, Mark Lutz", 4, 40.95],
    ["98762", "Programming Python, Mark Lutz", 5, 56.80],
    ["77226", "Head First Python, Paul Barry", 3, 32.95]
]

Each sublist has:
[order_id, title_and_author, quantity, unit_price]

Write code using map() and lambda to produce a list of 2-tuples:

(order_id, total_price)

Where:

  • total_price = quantity * unit_price
  • If this product is less than 100, add a surcharge of 10.

So for small orders, total price = product + 10.
You are allowed to nest map() calls if you want.

Exercise 8 – Maximum Word Length

Given the string:

s = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor"
s += " invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et"
s += " accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata"
s += " sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing"
s += " elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat,"
s += " sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd"
s += " gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet."

Determine the maximum word length (number of letters of the longest word).
You may ignore punctuation marks when counting letters.

Exercise 9 – Sum of Numbers from 1 to 1000

Find four different ways to compute the sum of the numbers from 1 to 1000.
For example:

  • Using a for loop
  • Using a while loop
  • Using reduce() with lambda
  • Using sum() with a comprehension or range()

Exercise 10 – Multiples of Seven

Find four different ways to build a list of all multiples of seven that are less than 100:

The final list should be:

[7, 14, 21, 28, 35, 42, 49, 56, 63, 70, 77, 84, 91, 98]

Do this in four different ways, for example:

  • Using range() with three parameters
  • Using a list comprehension
  • Using filter() and lambda
  • Using map() and lambda

Solutions and Explanations

Solution 1 – Digit-Cancelling Fractions

def digit_cancelling_fractions():
    results = []
    for num in range(10, 100):
        for den in range(10, 100):
            if num >= den:
                continue  # we only want fractions < 1

            # Exclude anything with zeros (trivial or invalid digit cancelling)
            if num % 10 == 0 or den % 10 == 0:
                continue

            num_str = str(num)
            den_str = str(den)

            # Look for a common digit to "cancel"
            for d in num_str:
                if d in den_str:
                    # Build "simplified" numerator and denominator
                    new_num_str = num_str.replace(d, "", 1)
                    new_den_str = den_str.replace(d, "", 1)

                    # Skip if this removes all digits
                    if not new_num_str or not new_den_str:
                        continue

                    new_num = int(new_num_str)
                    new_den = int(new_den_str)
                    if new_den == 0:
                        continue

                    original_value = num / den
                    cancelled_value = new_num / new_den

                    if abs(original_value - cancelled_value) < 1e-9:
                        results.append((num, den, new_num, new_den))

    return results


fractions = digit_cancelling_fractions()
for num, den, new_num, new_den in fractions:
    print(f"{num}/{den} == {new_num}/{new_den}")

Explanation:
We try all two-digit numerator/denominator pairs, avoid zeros (to exclude trivial 30/50 cases), and check if cancelling a common digit gives the same numeric value.

Solution 2 – Recursive GCD (Euclidean Algorithm)

def gcd(a, b):
    if b == 0:
        return abs(a)
    return gcd(b, a % b)

print(gcd(80, 65))  # 5
print(gcd(15, 5))   # 5
print(gcd(42, 56))  # 14

Explanation:
The Euclidean algorithm repeatedly replaces the pair (a, b) with (b, a % b) until the remainder is 0. The last non-zero value is the GCD

Solution 3 – Character Frequencies

def char_frequencies(text):
    freq = {}
    for ch in text:
        if ch in freq:
            freq[ch] += 1
        else:
            freq[ch] = 1
    return freq

print(char_frequencies("Erdbeere"))

Explanation:
We maintain a dictionary where each key is a character, and the value is the count. For every character, we increment its count.

Solution 4 – Distance Between Two Points

import math

def distance(p1, p2):
    x1, y1 = p1
    x2, y2 = p2
    return math.hypot(x2 - x1, y2 - y1)

print(distance((-4, 2), (-1, 6)))        # 5.0
print(distance((-1, 6), (4, 18)))        # 13.0

zell = (704.4, 256.2)
neuburg = (693.3, 261.4)
print(distance(zell, neuburg))           # ~12.26

Explanation:
The distance formula is:
√((x2 - x1)² + (y2 - y1)²)
math.hypot(dx, dy) computes exactly this.

Solution 5 – Steganography

import random
import string

def hide(text, n=1):
    text = text.upper()
    result_parts = []
    for ch in text:
        if ch == " ":
            result_parts.append(" ")
        else:
            random_letters = "".join(random.choice(string.ascii_uppercase) for _ in range(n))
            result_parts.append(ch + random_letters)
    return "".join(result_parts)

print(hide("Um acht an der Uhr"))
print(hide("Um acht an der Uhr", 2))

Explanation:

  • Convert to uppercase.
  • For each letter, add n random uppercase letters.
  • Keep spaces as spaces to preserve word boundaries.

Solution 6 – Extract Vowels

vowels = "aeiouAEIOU"

# Classic loop
def extract_vowels_loop(text):
    result = ""
    for ch in text:
        if ch in vowels:
            result += ch
    return result

# Higher-order function (filter + lambda)
def extract_vowels_filter(text):
    return "".join(filter(lambda ch: ch in vowels, text))

print(extract_vowels_loop("Magermilchjoghurt"))
print(extract_vowels_filter("Magermilchjoghurt"))

Explanation:
Both functions return the string of vowels only.
The second one uses filter() with a lambda function as requested.

Solution 7 – Bookstore Orders with map() and lambda

orders = [
    ["34587", "Learning Python, Mark Lutz", 4, 40.95],
    ["98762", "Programming Python, Mark Lutz", 5, 56.80],
    ["77226", "Head First Python, Paul Barry", 3, 32.95]
]

def compute_totals(orders_list):
    return list(
        map(
            lambda order: (
                order[0],
                order[2] * order[3] if order[2] * order[3] >= 100
                else order[2] * order[3] + 10
            ),
            orders_list
        )
    )

print(compute_totals(orders))

Explanation:

  • For each order, calculate quantity * unit_price.
  • If it’s less than 100, add 10.
  • Return (order_id, total_price) as a tuple in a list.

Solution 8 – Maximum Word Length

import string

def max_word_length(text):
    words = text.split()
    max_len = 0
    for w in words:
        cleaned = w.strip(string.punctuation)
        if len(cleaned) > max_len:
            max_len = len(cleaned)
    return max_len

print(max_word_length(s))

Explanation:
We split into words, remove punctuation from the start and end of each word, and track the maximum length.

Solution 9 – Sum of Numbers from 1 to 1000

from functools import reduce

# 1. For loop
def sum_for():
    total = 0
    for i in range(1, 1001):
        total += i
    return total

# 2. While loop
def sum_while():
    total = 0
    i = 1
    while i <= 1000:
        total += i
        i += 1
    return total

# 3. reduce + lambda
def sum_reduce():
    return reduce(lambda x, y: x + y, range(1, 1001))

# 4. sum() + range
def sum_builtin():
    return sum(range(1, 1001))

print(sum_for())
print(sum_while())
print(sum_reduce())
print(sum_builtin())

Explanation:
All four methods produce the same result (500500) but with different styles: imperative loops, functional reduce, and the built-in sum().

Solution 10 – Multiples of Seven

# 1. range() with step
multiples1 = list(range(7, 100, 7))

# 2. List comprehension
multiples2 = [x for x in range(1, 100) if x % 7 == 0]

# 3. filter() + lambda
multiples3 = list(filter(lambda x: x % 7 == 0, range(1, 100)))

# 4. map() + lambda
multiples4 = list(map(lambda k: 7 * k, range(1, 100 // 7 + 1)))

print(multiples1)
print(multiples2)
print(multiples3)
print(multiples4)

Explanation:
Each approach generates the same list of multiples in a different style (step-based range, comprehension, filter, and map).

Leave a Reply

Your email address will not be published. Required fields are marked *