I solved the question of the happiness of 1 by simply giving 10 and
such a happiness of 2, and 7 a happiness of 5.
This doesn't matter for determining the happiest number anyway, and is
a bit more consistent.

For finding the happiest numbers it only searches numbers with non
decreasing digits (eg. 123 but not 312 and 321).
This speeds up the search a lot, it can handle searching up to 10^10 in seconds.

It can also search for happy bases, because of this my solution has to
work for any base and is a bit messy as to_s is useless for this and
recursion is impossible for bases above 250 or so (stack overflow and
such).
To speed up searches it doesn't clear the cache from previous bases,
so memory uses increases until 300Mb at base 3000 or so.

Anyway, the world is a better place now that we know there are no
happy bases under 3202 ;)

Usage:
happy.rb find [base]  - find the happiest number under base^7
happy.rb without parameters: search for happy bases


Code:


class Array
  def sum
    inject(0){|a,b| a+b}
  end
end

class Integer

  def to_digits(base)
    return [self] if self < base
    (self/base).to_digits(base) << self%base
  end

  def happy_function
    to_digits($base).map{|d| d ** 2}.sum
  end

  def happiness
    num = self
    chain = [num]
    until num == 1 || num.cached_happiness == -1
      num.cached_happiness = -1
      chain << num = num.happy_function
    end
    if num == 1
      chain.shift.cached_happiness = chain.size until chain.empty?
    end
    self.cached_happiness
  end

  def happy?
    happiness >= 0
  end

  protected
  def cached_happiness
    return 0 if self==1
    (@happiness||={})[$base]
  end

  def cached_happiness=(val)
    (@happiness||={})[$base] = val
  end
end

def nondec_nums(len,min=0,n=0,&b) # yields all numbers with
non-decreasing digit sequences of length <= len
  if len==0
    yield n
  else
    [*min...$base].each{|m| nondec_nums(len-1, m ,n * $base + m,&b) }
  end
end

maxn = maxh = 1
s = Time.now

if ARGV[0] == 'find'
  $base = (ARGV[1] || 10 ).to_i
  MAXLEN = 7
  puts "searching happiest number < #{$base**MAXLEN} in base #{$base}"
  max_n, max_h = 1,0
  nondec_nums(MAXLEN) {|n| # length 7 / up to 9,999,999
  if n.happiness > max_h
    max_n, max_h = n, n.happiness
    puts "n=#{n}\th=#{max_h}\tdigits=#{n.to_digits($base).inspect}"
  end
}
else
  puts "searching for happy bases, press ctrl-c to quit"
  (2..1_000_000).each {|$base|
    len = 3    #  len * (base-1)^2 < b^len - 1  for len >= 3 and any base
    max = $base**len - 1  # upper bound for when \forall n>=max
n.happy_function < n   =>  if all numbers of up to and including this
length are happy then all numbers are
    max -= $base**(len-1) while max.happy_function < max # further
decrease upper bound,  for base 10 this is : 999, 899, 799, etc
    max += $base**(len-1) # previous loop always does 1 step too much
    happy_base = true
    min_unhappy = nil
    nondec_nums(len) {|n|
      if !n.happy?  && n>0
        min_unhappy = n
        happy_base = false
        break
      end
      break if n > max
    }
    puts happy_base ? "base #{$base} is a happy base!" : "base
#{$base} is not a happy base because #{min_unhappy} is unhappy."
  }
end