Abundant, deficient and perfect number classifications

From Rosetta Code
Revision as of 00:06, 18 December 2014 by Rdm (talk | contribs) (J: Remove the warning message, since it is no longer accurate.)
Abundant, deficient and perfect number classifications is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

These define three classifications of positive integers based on their proper divisors.

Let P(n) be the sum of the proper divisors of n, where the proper divisors of n are all positive divisors of n other than n itself.

Example: 6 has proper divisors 1, 2, and 3. 1 + 2 + 3 = 6 so 6 is classed as a perfect number.

Task

Calculate how many of the integers 1 to 20,000 inclusive are in each of the three classes and show the result here.

Cf.

D

<lang d>void main() /*@safe*/ {

   import std.stdio, std.algorithm, std.range;
   static immutable properDivs = (in uint n) pure nothrow @safe /*@nogc*/ =>
       iota(1, (n + 1) / 2 + 1).filter!(x => n % x == 0 && n != x);
   enum Class { deficient, perfect, abundant }
   static Class classify(in uint n) pure nothrow @safe /*@nogc*/ {
       immutable p = properDivs(n).sum;
       with (Class)
           return (p < n) ? deficient : ((p == n) ? perfect : abundant);
   }
   enum rangeMax = 20_000;
   //iota(1, 1 + rangeMax).map!classify.hashGroup.writeln;
   iota(1, 1 + rangeMax).map!classify.array.sort().group.writeln;

}</lang>

Output:
[Tuple!(Class, uint)(deficient, 15043), Tuple!(Class, uint)(perfect, 4), Tuple!(Class, uint)(abundant, 4953)]

J

Supporting implementation:

<lang J>factors=: [: /:~@, */&>@{@((^ i.@>:)&.>/)@q:~&__ properDivisors=: factors -. ]</lang>

We can subtract the sum of a number's proper divisors from itself to classify the number:

<lang J> (- +/@properDivisors&>) 1+i.10 1 1 2 1 4 0 6 1 5 2</lang>

Except, we are only concerned with the sign of this difference:

<lang J> *(- +/@properDivisors&>) 1+i.30 1 1 1 1 1 0 1 1 1 1 1 _1 1 1 1 1 1 _1 1 _1 1 1 1 _1 1 1 1 0 1 _1</lang>

Also, we do not care about the individual classification but only about how many numbers fall in each category:

<lang J> #/.~ *(- +/@properDivisors&>) 1+i.20000 15043 4 4953</lang>

So: 15043 deficient, 4 perfect and 4953 abundant numbers in this range.

How do we know which is which? We look at the unique values (which are arranged by their first appearance, scanning the list left to right):

<lang J> ~. *(- +/@properDivisors&>) 1+i.20000 1 0 _1</lang>

The sign of the difference is negative for the abundant case - where the sum is greater than the number. And we rely on order being preserved in sequences (this happens to be a fundamental property of computer memory, also).

Perl

Using a module

Library: ntheory

We can use the <=> operator to return a comparison of -1, 0, or 1, which classifies the results. Let's look at the values from 1 to 30: <lang perl>use ntheory qw/divisor_sum/; say join " ", map { divisor_sum($_)-$_ <=> $_ } 1..30;</lang>

Output:
-1 -1 -1 -1 -1 0 -1 -1 -1 -1 -1 1 -1 -1 -1 -1 -1 1 -1 1 -1 -1 -1 1 -1 -1 -1 0 -1 1

We can see 6 is the first perfect number, 12 is the first abundant number, and 1 is classified as a deficient number.

Showing the totals for the first 20k numbers: <lang perl>use ntheory qw/divisor_sum/; my %h; $h{divisor_sum($_)-$_ <=> $_}++ for 1..20000; say "Perfect: $h{0} Deficient: $h{-1} Abundant: $h{1}";</lang>

Output:
Perfect: 4    Deficient: 15043    Abundant: 4953

Python

Importing Proper divisors from prime factors: <lang python>>>> from proper_divisors import proper_divs >>> from collections import Counter >>> >>> rangemax = 20000 >>> >>> def pdsum(n): ... return sum(proper_divs(n)) ... >>> def classify(n, p): ... return 'perfect' if n == p else 'abundant' if p > n else 'deficient' ... >>> classes = Counter(classify(n, pdsum(n)) for n in range(1, 1 + rangemax)) >>> classes.most_common() [('deficient', 15043), ('abundant', 4953), ('perfect', 4)] >>> </lang>