# ETOOBUSY ðŸš€ minimal blogging for the impatient

# PWC082 - Common Factors

**TL;DR**

Here we are with TASK #1 from the Perl Weekly Challenge #082. Enjoy!

# The challenge

You are given 2 positive numbers

`$M`

and`$N`

. Write a script to list all common factors of the given numbers.

# The questions

One question that popped in my mind was: *is this stemming from the recent
challenge Common Base String?* My solution required finding all possible
factors at a certain point, soâ€¦

Rite questions would be about what to do in corner cases: lack or wrong inputs (e.g. strings or floating point values, negative values, etc.)

Last, a question might be about whether the output should comply to a specific sorting or not. Apart, of course, confirmation about the output interface (the round parentheses, distancing, â€¦).

# The solution

It might be possible to address this with a totally brute force approach butâ€¦ thereâ€™s an evident property that can be used in this case.

Whatever factor the two inputs might have in common, it will also have
to be a factor of their *greatest common divisor* (which is the higher
factor that they have in common, by the way).

Hence, instead of comparing factors of the two inputs, itâ€™s easier to
find their greatest common divisor and find all of *its* factors.

To address the first issue, Euclidâ€™s algorithm is the perfect tool (we already saw it in The extended Euclidâ€™s algorithm) and can fit in a single line:

```
sub gcd ($A, $B) { ($A, $B) = ($B % $A, $A) while $A; return $B }
```

Finding all of its factor can now be addressed with a O(N) linear approach, somehow brute-force-ish:

```
sub common_factors ($A, $B) {
my $gcd = gcd($A, $B);
grep { !($gcd % $_) } 1 .. int($gcd / 2), $gcd;
}
```

We go through all *possible* candidates, keeping only those that are
really divisors of the greatest common divisor. These candidates are
`1`

, the greatest common divisor itself, as well as any value between
`1`

up to one half of the greatest common divisor (there cannot be a
factor that is greater than this).

An interesting side effect of the range up to the one half of the
greatest common divisor is that when this value is `1`

, the integer
rounding `int($gcd / 2)`

is equal to `0`

and the range is empty, leaving
only `$gcd`

(i.e. `1`

) in the list fed to `grep`

. This means that `1`

will only appear once in the output, which is good!

The full solution, should you want to play with it, is the following:

```
#!/usr/bin/env perl
use 5.024;
use warnings;
use English qw< -no_match_vars >;
use experimental qw< postderef signatures >;
no warnings qw< experimental::postderef experimental::signatures >;
sub gcd ($A, $B) { ($A, $B) = ($B % $A, $A) while $A; return $B }
sub common_factors ($A, $B) {
my $gcd = gcd($A, $B);
grep { !($gcd % $_) } 1 .. int($gcd / 2), $gcd;
}
my $M = shift || 12;
my $N = shift || 18;
say '(', join(', ', common_factors($M, $N)), ')';
```

This is it for today, stay tuned for the other task!

*Comments? Octodon, Twitter, GitHub, Reddit, or drop me a line!*