Weird numbers: Difference between revisions

Content added Content deleted
No edit summary
(Ada!)
Line 87: Line 87:
70 836 4030 5830 7192 7912 9272 10430 10570 10792 10990 11410 11690 12110 12530 12670 13370 13510 13790 13930 14770 15610 15890 16030 16310
70 836 4030 5830 7192 7912 9272 10430 10570 10792 10990 11410 11690 12110 12530 12670 13370 13510 13790 13930 14770 15610 15890 16030 16310
</pre>
</pre>

=={{header|Ada}}==
This is not quite a translation of another version, in that:
* I first implemented it without looking at other implementations. This works, but is significantly slower than what others were reporting.
* I then compared to other implementations, and adapted what they had.

To be honest, I prefer the original implementation, even though it is much slower than many others, since:
* As far as I can tell, it's fairly consistent with timings, and doesn't much depend on the order of the factors.
* The "fast" approach is _highly_ dependent on the ordering of the factors. The implementers seem to have lucked into a good ordering that looks unnatural.

{{{out}}}
When run on my machine, both versions produce the following output.
<pre>
The first 25 weird numbers are 70 836 4030 5830 7192 7912 9272 10430 10570 10792 10990 11410 11690 12110 12530 12670 13370 13510 13790 13930 14770 15610 15890 16030 16310
</pre>

===Version 1 (slower; but fairly consistent)===

<syntaxhighlight lang="ada">
pragma Ada_2022;

with Ada.Text_IO;
with Ada.Containers.Vectors;
with Ada.Containers.Ordered_Sets;

procedure Weird_Numbers is
package IO renames Ada.Text_IO;
-- allows us to type "IO.Put_Line" instead of "Ada.Text_IO.Put_Line"

package IntVecs is new Ada.Containers.Vectors
(Index_Type => Positive, Element_Type => Positive);
subtype IntVec is IntVecs.Vector;
use all type IntVec;

Factor_Cache : IntVec;
-- used to keep track of the factors on each iteration

package Intsets is new Ada.Containers.Ordered_Sets
(Element_Type => Positive);
subtype Intset is Intsets.Set;
Semiperfect_Cache : Intset;
Not_Abundant_Cache : Intset;

function Proper_Divisors (Value : Positive) return IntVec is
Cofactors : IntVec;
begin
Factor_Cache.Clear;
begin
Factor_Cache.Append (1);
for Factor in 2 .. Value / 2 loop
if Value rem Factor = 0 then
Factor_Cache.Append (Factor);
end if;
end loop;
return Factor_Cache;
end;
end Proper_Divisors;

function Is_Abundant (Value : Positive) return Boolean is
begin
if Not_Abundant_Cache.Contains (Value)
or else Semiperfect_Cache.Contains (Value)
then
return False;
end if;
if Proper_Divisors (Value)'Reduce ("+", 0) > Value then
return True;
end if;
Not_Abundant_Cache.Insert (Value);
return False;
end Is_Abundant;

function Subset_Of (Factors : IntVec; Ith : Positive) return IntVec is
-- returns the Ith subset of Factors
-- if Factors has N elements, then there are 2**N possibilities
-- this maps Ith to one of these possibilities using its binary representation
Result : IntVec;
Index : Natural := 1;
Ith_Remainder : Natural := Ith;
begin
while Ith_Remainder /= 0 loop
if Ith_Remainder rem 2 = 1 then
Result.Append (Factors (Index));
Ith_Remainder := @ - 1;
end if;
Ith_Remainder := @ / 2;
Index := @ + 1;
end loop;
return Result;
end Subset_Of;

function Is_Semiperfect (Value : Positive) return Boolean is
Factors : constant IntVec := Factor_Cache;
Subset : IntVec;
Sum : Natural;
begin
if (for some Previous of Semiperfect_Cache => Value rem Previous = 0)
then
return True;
end if;
for Ith in reverse 1 .. (2**Positive (Factors.Length) - 1) loop
Subset := Subset_Of (Factors, Positive (Ith));
Sum := Subset'Reduce ("+", 0);
if Sum = Value then
Semiperfect_Cache.Insert (Value);
return True;
end if;
end loop;
return False;
end Is_Semiperfect;

function Is_Weird (Value : Positive) return Boolean is
(Is_Abundant (Value) and then not Is_Semiperfect (Value));

Current : Positive := 2;
Number_Found : Natural := 0;

begin
IO.Put ("The first 25 weird numbers are");
while Number_Found < 25 loop
if Is_Weird (Current * 2) then
IO.Put (Integer'Image (Current * 2));
Number_Found := @ + 1;
end if;
Current := @ + 1;
end loop;
IO.New_Line;
end Weird_Numbers;

</syntaxhighlight>

===Version 2 (what most people are using; faster if you're lucky with the ordering of factors)===

<syntaxhighlight lang="ada">
pragma Ada_2022;

with Ada.Text_IO;
with Ada.Containers.Vectors;
with Ada.Containers.Ordered_Sets;

procedure Weird_Numbers is
package IO renames Ada.Text_IO;

package IntVecs is new Ada.Containers.Vectors
(Index_Type => Positive, Element_Type => Positive);
subtype IntVec is IntVecs.Vector;
use all type IntVec;

Factor_Cache : IntVec;

package Intsets is new Ada.Containers.Ordered_Sets
(Element_Type => Positive);
subtype Intset is Intsets.Set;
Semiperfect_Cache : Intset;
Not_Abundant_Cache : Intset;

function Proper_Divisors (Value : Positive) return IntVec is
Cofactors : IntVec;
begin
Factor_Cache.Clear;
begin
Factor_Cache.Append (1);
for Factor in 2 .. Value / 2 loop
if Factor * Factor > Value then
exit;
end if;
if Value rem Factor = 0 then
Factor_Cache.Append (Factor);
if Factor * Factor /= Value then
Cofactors.Append (Value / Factor);
end if;
end if;
end loop;
Factor_Cache.Prepend_Vector (Cofactors);
-- this ordering is REALLY curious
-- for instance, the factors of 12 are ordered as 6, 4, 1, 2, 3
-- AND THIS MATTERS
-- both strictly ascending and strictly descending orders are much slower
return Factor_Cache;
end;
end Proper_Divisors;

function Is_Abundant (Value : Positive) return Boolean is
begin
if Not_Abundant_Cache.Contains (Value)
or else Semiperfect_Cache.Contains (Value)
then
return False;
end if;
if Proper_Divisors (Value)'Reduce ("+", 0) > Value then
return True;
end if;
Not_Abundant_Cache.Insert (Value);
return False;
end Is_Abundant;

function Alternate_Semiperfect (Value : Positive) return Boolean is
function Reroute
(Value : Positive; First : Positive) return Boolean
is
Head : Positive;
begin
if Factor_Cache.Last_Index >= First then
Head := Factor_Cache (First);
if Value < Head then
return Reroute (Value, First + 1);
else
return
Value = Head
or else Reroute (Value - Head, First + 1)
or else Reroute (Value, First + 1);
end if;
else
return False;
end if;
end Reroute;
begin
return
Reroute
(Value, Factor_Cache.First_Index);
end Alternate_Semiperfect;

function Is_Weird (Value : Positive) return Boolean is
(Is_Abundant (Value) and then not Alternate_Semiperfect (Value));

Current : Positive := 2;
Number_Found : Natural := 0;

begin
IO.Put ("The first 25 weird numbers are");
while Number_Found < 25 loop
if Is_Weird (Current * 2) then
IO.Put (Integer'Image (Current * 2));
Number_Found := @ + 1;
end if;
Current := @ + 1;
end loop;
IO.New_Line;
end Weird_Numbers;
</syntaxhighlight>


=={{header|ALGOL 68}}==
=={{header|ALGOL 68}}==