Weekly Challenge 357
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. It's a great way for us all to practice some coding.
Task 1: Kaprekar Constant
Task
Write a function that takes a 4-digit integer and returns how many iterations are required to reach Kaprekar's constant (6174).
My solution
This is where GitHub Copilot really scars me somewhat. Without mentioning "kaprekar" at all, and after typing while number != 6174: it auto-completed the rest of the code.
For this solution, I start by checking that the number is between 1 and 9999, and that it is not divisible by 1111. If any of these are false, I return -1.
I create a variable called count, starting at 0. If number is not 6174, I take the following steps
- Set
digitsto be the individual digits, left padded with zeros if needed. - Set
asc_digitsto the abovedigitslist, sorted numerically (lowest first). Setdesc_digitsto the same, but in reversed sorted order. - Set
small_numto the integer that concatenatesasc_digits. - Set
large_numto the integer that concatenatesdesc_digits. - Set number to be
large_numminussmall_num. - If this is not 6174, repeat the loop again.
def kaprekar_constant(number: int) -> int:
if not 0 < number < 10000:
return -1
if number % 1111 == 0:
return -1
count = 0
while number != 6174:
digits = [int(d) for d in str(number).zfill(4)]
asc_digits = sorted(digits)
desc_digits = sorted(digits, reverse=True)
small_num = int(''.join(map(str, asc_digits)))
large_num = int(''.join(map(str, desc_digits)))
number = large_num - small_num
count += 1
return count
The Perl solution is a little shorter, as I don't need to worry about finicky things like converting integers to strings :P
sub main ($int) {
if ($int < 1 or $int > 9999 or $int % 1111 == 0) {
say -1;
return;
}
my $count = 0;
while ($int != 6174) {
my $small_number = join("", sort { $a <=> $b } (split //, sprintf("%04d", $int)));
my $big_number = reverse($small_number);
$int = $big_number - $small_number;
$count++
}
say $count;
}
Examples
$ ./ch-1.py 3524
3
$ ./ch-1.py 6174
0
$ ./ch-1.py 9998
5
$ ./ch-1.py 1001
4
$ ./ch-1.py 9000
4
$ ./ch-1.py 1111
-1
Task 2: Unique Fraction Generator
Task
Given a positive integer n, generate all unique fractions you can create using integers from 1 to n and follow the rules below:
- Use numbers
1throughnonly (no zero) - Create fractions like numerator/denominator
- List them in ascending order (from smallest to largest)
- If two fractions have the same value (like
1/2and2/4), only show the one with the smallest numerator
My solution
Python has the fractions module as standard, while Perl has a CPAN module called Numbers::Fraction.
Both exhibit the same behaviors:
- Takes the numerator (top part) and denominator (bottom part) as input variables
- Will automatically convert to have the lowest unique values. For example,
Fraction(2, 4)automatically isFraction(1, 2). - Annoyingly for this task, will print a whole number if the denominator is 1.
- Two fractions variables can be compared to be sorted correctly (by the number they represent). For example
¼ < ½.
With that in mind, this task involves having a double loop for n and d (both from 1 to number), removing the duplicates with the set function, and then sorting them numerically. Finally, I turn the Fraction object into strings, and return the list.
def unique_fraction_generator(number: int) -> list[str]:
fractions = sorted(set(
Fraction(n,d)
for d in range(1, number + 1)
for n in range(1, number+1)
))
return [f"{frac.numerator}/{frac.denominator}" for frac in fractions]
The Perl solution follows the same logic. It uses grep to check if a fraction is added previously, and pushes it to the fractions array if it is not.
use Number::Fraction;
sub main ($int) {
my @fractions = ();
foreach my $n (1 .. $int) {
foreach my $d (1 .. $int) {
my $fraction = Number::Fraction->new($n, $d);
push @fractions, $fraction if not grep { $_ == $fraction} @fractions;
}
}
my @sorted_fractions = sort { $a <=> $b } @fractions;
say join(", ", map({ "$_->{num}/$_->{den}" } @sorted_fractions));
}
Examples
$ ./ch-2.py 3
1/3, 1/2, 2/3, 1/1, 3/2, 2/1, 3/1
$ ./ch-2.py 4
1/4, 1/3, 1/2, 2/3, 3/4, 1/1, 4/3, 3/2, 2/1, 3/1, 4/1
$ ./ch-2.py 1
1/1
$ ./ch-2.py 6
1/6, 1/5, 1/4, 1/3, 2/5, 1/2, 3/5, 2/3, 3/4, 4/5, 5/6, 1/1, 6/5, 5/4, 4/3, 3/2, 5/3, 2/1, 5/2, 3/1, 4/1, 5/1, 6/1
$ ./ch-2.py 5
1/5, 1/4, 1/3, 2/5, 1/2, 3/5, 2/3, 3/4, 4/5, 1/1, 5/4, 4/3, 3/2, 5/3, 2/1, 5/2, 3/1, 4/1, 5/1
Top comments (0)