DEV Community

Simon Green
Simon Green

Posted on

Weekly Challenge: The Rational Base

Weekly Challenge 386

Each week Mohammad S. Anwar sends out The Weekly Challenge, a chance for all of us to come up with solutions to two weekly tasks. My solutions are written in Python first, and then converted to Perl. Unless otherwise stated, Copilot (and other AI tools) have NOT been used to generate the solution. It's a great way for us all to practice some coding.

Challenge, My solutions

Task 1: Reverse Base

Task

You are given a string representing a number, and an integer specifying the base of that representation.

Write a function to convert this string to an integer. (For bases greater than 10, use characters A-Z, a-z, + and / in that order.)

My solution

This is the reserve of a previous challenge where we converted numbers to a specified base. In this challenge, I start by defining the variable alphabet which has the first base characters from 0-9A-Za-z+/

import string

def reverse_base(input_string: str, base: int) -> int:
    alphabet = "0123456789" + string.ascii_uppercase + string.ascii_lowercase + "+/"

    alphabet = alphabet[:base]
Enter fullscreen mode Exit fullscreen mode

I then convert each charter to its decimal representation. This uses list comprehension to find the index (position) of the character in the alphabet variable. If a value is invalid, a ValueError is raised, and I raise an Exception to state this.

    try:
        digits = [alphabet.index(digit) for digit in input_string]
    except ValueError:
        raise ValueError("Invalid input for the specified base")
Enter fullscreen mode Exit fullscreen mode

The last part is to calculate the integer value, and return the output variable.

    output = 0
    for digit in digits:
        output *= base
        output += digit

    return output
Enter fullscreen mode Exit fullscreen mode

The Perl solution follows the same logic.

Examples

$ ./ch-1.py 101010 2
42

$ ./ch-1.py EEADEE 16
15642094

$ ./ch-1.py 755 8
493

$ ./ch-1.py 1BRJB 36
2228519

$ ./ch-1.py 7MyqL 64
123456789
Enter fullscreen mode Exit fullscreen mode

Task 2: Rational Numbers

Task

You are given two strings representing non-negative rational numbers.

Write a script to return true if the two given rational numbers are same otherwise false.

My solution

For this challenge, I take two or more numbers. I start by split each rats (rational number) value into three parts: 1) The whole number (or 0 if not present), 2) The non-recurring fraction (or "" if not present), and 3) The recurring part of the fraction (or 0 if not present). This is stored in a list (array in Perl) called rat_parts.

def same_rational_numbers(*rats) -> bool:
    rat_parts = []
    for rat in rats:
        m = re.search(r"(\d+)(?:\.(\d*)(?:\((\d+)\))?)?$", rat)
        if not m:
            raise ValueError("The value {rat} is invalid!")
        rat_parts.append([m.group(1) or "0", m.group(2) or "", m.group(3) or "0"])
Enter fullscreen mode Exit fullscreen mode

The next thing I do is check if the whole part of the numbers are the same. By using a set, duplicate values are removed. If there is more than one unique whole number, it means the values in rat aren't the same. For the Perl solution, I use uniq from the List::Util module to check for uniqueness.

    if len(set(r[0] for r in rat_parts)) != 1:
        return False
Enter fullscreen mode Exit fullscreen mode

The last part to is check if the fraction part is the same. For this I set the part length to the maximum of length the non-recurring fraction and two times the recurring amount. This should cover all cases. The extend_part function takes the split parts, and extends the fractional part of part_length length.

def extend_part(rat_part, part_length) -> str:
    s = rat_part[1]
    while len(s) < part_length:
        s += rat_part[2]

    return s[:part_length]
Enter fullscreen mode Exit fullscreen mode

Like with the whole check, the fraction part also uses a set (or uniq in Perl) to check that all parts are the same.

    part_length = max(len(r[1]) + 2 * len(r[2]) for r in rat_parts)
    if len(set(extend_part(r, part_length) for r in rat_parts)) != 1:
        return False

    return True
Enter fullscreen mode Exit fullscreen mode

Like with the first task, the Perl solution follows the same logic.

Examples

In the challenge, the solution to the fourth example gives a different result. I believe this is an error in the example, not in my code.

$ ./ch-2.py "0.(12)" "0.(121)"
false

$ ./ch-2.py "0.1(23)" "0.12(32)"
true

$ ./ch-2.py "0.1(234)" "0.12(342)"
true

$ ./ch-2.py "12.99(99)" "13."
false

$ ./ch-2.py "0.(123)" "0.1(231)"
true
Enter fullscreen mode Exit fullscreen mode

Top comments (0)