Square Number Digit Density

Answering the bonus: the best score for numbers <1e9 is 5/3=1.666..., generated by 144411449 (and maybe others?).

But you can do better with larger numbers. Generally if n has a score of x, then you can concatenate two copies of n and get the same score x. If you're lucky and n has the same first and last digit, then you can drop one of those digits in the concatenation and improve your score slightly (one less than double the number of squares and one less than double the number of digits).

n=11449441 has a score of 1.625 and has the same first & last digit. Using that fact, we get the following sequence of scores:

1.625 for 11449441
1.666 for 114494411449441
1.682 for 1144944114494411449441
1.690 for 11449441144944114494411449441
1.694 for 114494411449441144944114494411449441

which gives an infinite sequence of numbers which are strictly (although decreasingly) better than previous numbers, and all but the first 2 better than the best score for numbers < 1e9.

This sequence may not be the best overall, though. It converges to a finite score (12/7=1.714) and there may be other numbers with better scores than the limit.

Edit: a better sequence, converges to 1.75

1.600 14441
1.667 144414441
1.692 1444144414441
1.706 14441444144414441
1.714 144414441444144414441

Windows PowerShell, 153 154 155 164 174

$a,$b=$args
@($a..$b|sort{-(0..($l=($s="$_").length)|%{($c=$_)..$l|%{-join$s[$c..$_]}}|?{$_[0]-48-and($x=[math]::sqrt($_))-eq[int]$x}).Count/$l},{$_})[0]

Thanks to Ventero for a one-byte reduction I was too stupid to find myself.

154-byte version explained:

$a,$b=$args   # get the two numbers. We expect only two arguments, so that
              # assignment will neither assign $null nor an array to $b.

@(   # @() here since we might iterate over a single number as well
    $a..$b |  # iterate over the range
        sort {   # sort
            (   # figure out all substrings of the number
                0..($l=($s="$_").length) | %{  # iterate to the length of the
                                               # string – this will run off
                                               # end, but that doesn't matter

                    ($c=$_)..$l | %{       # iterate from the current position
                                           # to the end

                        -join$s[$c..$_]    # grab a range of characters and
                                           # make them into a string again
                    }
                } | ?{                     # filter the list
                    $_[0]-48 -and          # must not begin with 0
                    ($x=[math]::sqrt($_))-eq[int]$x  # and the square root
                                                     # must be an integer
                }
            
            ).Count `  # we're only interested in the count of square numbers
            / $l       # divided by the length of the number
        },
        {-$_}  # tie-breaker
)[-1]  # select the last element which is the smallest number with the
       # largest SNDD

Ruby 1.9, 142 characters

$><<($*[0]..$*[1]).map{|a|n=0.0;(1..s=a.size).map{|i|n+=a.chars.each_cons(i).count{|x|x[0]>?0&&(r=x.join.to_i**0.5)==r.to_i}};[-n/s,a]}.min[1]
  • (139 -> 143): Fixed output in case of a tie.