```Here is my solution (it's optimized for statistics not for speed).
I don't use things like someone tried all odd numbers up to 10**18
because doesn't change the algorithm.. I'll use that in my speed
optimized version..

#############

=begin

References:

http://en.wikipedia.org/wiki/Divisor_function
http://en.wikipedia.org/wiki/Weird_number
Eric W. Weisstein. "Weird Number." From MathWorld--A Wolfram Web
Resource. http://mathworld.wolfram.com/WeirdNumber.html
Eric W. Weisstein. "Semiperfect Number." From MathWorld--A Wolfram
Web Resource. http://mathworld.wolfram.com/SemiperfectNumber.html
Eric W. Weisstein. "Perfect Number." From MathWorld--A Wolfram Web
Resource. http://mathworld.wolfram.com/PerfectNumber.html
Eric W. Weisstein. "Divisor Function." From MathWorld--A Wolfram Web
Resource. http://mathworld.wolfram.com/DivisorFunction.html
Eric W. Weisstein. "Mersenne Prime." From MathWorld--A Wolfram Web
Resource. http://mathworld.wolfram.com/MersennePrime.html
Eric W. Weisstein. "Abundance." From MathWorld--A Wolfram Web
Resource. http://mathworld.wolfram.com/Abundance.html

=end

class Integer
\$prime_factors = Hash.new # we cache prime factors...
def prime_factors position = -1
if cached = \$prime_factors[self] # cached?
return cached # yes
end

if self == 1 # we have 1 we are done
return \$prime_factors[self]=[] # return no factors
elsif position<0 # we havn't reached factor 5 yet
if self&1 == 0 # test factor 2
return \$prime_factors[self]=[2,*(self>>1).prime_factors]
elsif self%3 == 0 # and factor 3
return \$prime_factors[self]=[3,*(self/3).prime_factors]
end
end

loop do
position+=6 # increment position by 6
if position*position > self # we have a prime number return it
return \$prime_factors[self]=[self]
elsif (quo,rem = divmod(position))   and rem.zero? # test 6n-1
return \$prime_factors[self]=[position,*quo.prime_factors
(position-6)]
elsif (quo,rem = divmod(position+2)) and rem.zero? # and 6n+1
return \$prime_factors[self]=[position+2,*quo.prime_factors
(position-6)]
end
end
end

def distinct_prime_factors # rle encode the prime factors ;)
distinct_prime_fac = Hash.new{0} # setup the hash
prime_factors.each do |prime_factor| # fill it
distinct_prime_fac[prime_factor]+=1
end
distinct_prime_fac.to_a.sort_by{|(fac,count)|fac} # and return
it as sorted array
end

def divisors # get the divisors (not needed for divisor sum)
divs = [] # store divisors here
n = 1 # start with 1
loop do
break if n*n > self # done
if (qua,rem = divmod(n)) and rem.zero? # test for division
divs << qua # add divisors
divs << n
end
n+=1
end
divs.uniq.sort[0..-2] # we don't want self
end

def semi_perfect? deficient=false # final test
cached_abundance = abundance
return deficient if cached_abundance < 0 # deficient return the
argument
return true if cached_abundance == 0 # perfect => semi perfect too

possible_values = {0=>true} # store all possible values in a hash
divs = self.divisors # get the divisors

div_sum_left = divs.inject(0){|a,b|a+b} # get the divisor sum

pos_2 = div_sum_left - self # this is a possibility too

divs.reverse.each do |div| # for each divisor
possible_values.keys.each do |value| # and each possible value
if value+div_sum_left < self # check wether it can reach the
number with the divisors left
possible_values.delete(value) # if not delete the number
(huge speedup)
end

new_value = value+div # we create a new possible value
including the divisor

if new_value == self or new_value == pos_2 # if it is the
number it's semi perfect
return true
elsif new_value < self # if it's less than the number it
could be semi perfect
possible_values[new_value]=true # add it to the possiblities
end # if it's more than the value we can ignore it
end
div_sum_left-=div # the current divisor isn't left anymore
end
false # we found no way to compose the number using the divisors
end

def restricted_divisor_sum # uses the formular from wikipedia
distinct_prime_factors.map do |(fac,count)|
comm_sum = 1
comm_mul = 1
count.times do
comm_sum += (comm_mul*=fac)
end
comm_sum
end.inject(1){|a,b|a*b}-self
end

def perfect? # perfect numbers have the form 2**(n-1)*(2**n-1)
where n is prime (and small ;) )
return false if self&1 == 1 # it isn't known weather there are
odd perfect numbers.. but it's irrelevant for my algorithm
doubled = self * 2 # the perfect number is a triangular number
of the form (n*(n+1))/2
doubled_root = Math.sqrt(doubled).floor # if we double it and
take the floored square root we get n
return false unless doubled == doubled_root*(doubled_root+1) #
if we don't get n it isn't perfect
doubled_root_string = (doubled_root+1).to_s(2) # from the first
line we know n+1 has to be of the form 2**n
return false unless doubled_root_string.count('1')==1 # we check
that here
return false unless
(doubled_root_string.size-1).prime_factors.size == 1 # and n ha to be
a prime
return false unless self.abundance == 0 # if it passes all the
earlier test we check it using the abundance
true # and if it passes it's perfect
end

def abundance
self.restricted_divisor_sum-self
end
end

require 'benchmark'

max_num = Integer(ARGV.shift||'1000') rescue 1000 # read a number
from the command line

new_semi_perfects = [] # store semi perfect numbers that can't be
constructed using other semi perfect numbers

STDOUT.sync = true
puts "Weird Numbers Up To #{max_num}:"

#begin_stat
perfect_count = 0
composed_semi_perfect_count = 0
new_semi_perfect_count = 0
weird_count = 0
deficient_count = 0

record_nums = (1..80).map{|n|max_num*n/80}

record_nums_left = record_nums.dup
next_record = record_nums_left.shift

recorded_times = []

init_time = [Benchmark.times,Time.now]

min_odd = 10**17

#end_stat

(1..max_num).each do |test_num| # counting loop

if test_num == next_record #stat
recorded_times << [Benchmark.times,Time.now] #stat
next_record = record_nums_left.shift #stat
end #stat

if test_num.perfect? # it's perfect
new_semi_perfects << test_num
perfect_count += 1 #stat
next
end

do_next = false
new_semi_perfects.each do |semi_per|
if test_num % semi_per == 0 # is it possible to compose the
current number using a semi-perfect number?
do_next = true # yes
composed_semi_perfect_count += 1 #stat
break
end
end
next if do_next
# no

case test_num.semi_perfect? nil # we don't care about deficient
numbers
when true # but we care about semi perfects
new_semi_perfects << test_num
new_semi_perfect_count += 1 #stat
when false # and even more about abundand non semi perfects
puts test_num
weird_count += 1 #stat
else #stat
deficient_count += 1 #stat
end

end

#end

#begin_stat

final_time = [Benchmark.times,Time.now]

digit_length = max_num.to_s.size

def form_float(num)
"%12s" % ("%im%#{7}ss" % [(num/60).floor,("%.4f" % (num %60))]).tr
(" ","0")
end

def rel(x,y)
"%#{y.to_s.size}i (%6s%%)" % [x,"%3.2f" % (x/y.to_f*100)]
end

puts "Stats"
puts "Time:"
puts "- User:   "+form_float(ut = final_time.first.utime -
init_time.first.utime)
puts "- System: "+form_float(st = final_time.first.stime -
init_time.first.stime)
puts "- U+S:    "+form_float(ut+st)
puts "- Real:   "+form_float(final_time.last - init_time.last)
puts
puts "Numbers:"
puts "- Total        "+rel(max_num,max_num)
puts "- Weird        "+rel(weird_count,max_num)
puts "- Perfect      "+rel(perfect_count,max_num)
puts "- Deficient    "+rel(deficient_count,max_num)
puts "- Abundand     "+rel(abundand_count = max_num-perfect_count-
deficient_count,max_num)
puts "- Semi-Perfect "+rel(abundand_count-weird_count,max_num)
puts "   (w/o perfects)"
puts ""
puts "- Passed 1st   "+rel(max_num-perfect_count,max_num)
puts "   (perfect test)"
puts "- Passed 2nd   "+rel(new_semi_perfect_count+weird_count
+deficient_count,max_num)
puts "   (composed test)"
puts "- Passed 3rd   "+rel(new_semi_perfect_count+weird_count,max_num)
puts "   (deficient test)"
puts "- Uncomposed   "+rel(new_semi_perfects.size,max_num)
puts "   (semi-perfects that arn't a multiply of another semi-perfect)"
puts

if recorded_times.size >= 80
puts "Graphs:"
puts

process_plot = []
real_plot = []

first_ustime = init_time.first.utime+init_time.first.stime
first_realtime = init_time.last

recorded_times.each do |(process_time,realtime)|
process_plot << process_time.utime+process_time.stime -
first_ustime
real_plot << realtime - first_realtime
end

max_process = process_plot.last
step_process = max_process / 22

max_real = real_plot.last
step_real = max_real / 22

def plot(plot_data,max,step)
22.downto(0) do |k|
res = ""
res = form_float(max) if k == 22
while res.size != plot_data.size
val = plot_data[res.size]
lower_range = k*step
res << ( val >= lower_range ? (val < lower_range+step ?
'#' : ':') : ' ' )
end
puts res
end
end

puts "Y: Realtime X: Number"
plot(real_plot,max_real,step_real)
puts "%-40s%40s"% [1,max_num]
puts "Y: System+Usertime X: Number"
plot(process_plot,max_process,step_process)
puts "%-40s%40s"% [1,max_num]
puts
end
#end_stat

__END__
--
Jannis Harder

```