Hallo everybody. Please go easy on me, this quiz solution is my third
ruby program ;) This language is a new discovery and it fascinates me,
but I learnt to program in the old-fashioned imperative world in high
school and I'm sure that it shows pretty clearly in the code (I made a
couple of "shout-outs" in the code to all fellow former users of the one
and only Borland Turbo Pascal 7 compiler :)
I decided to enter the "contest" hoping that somebody will show how
these "translated Pascal" constructs of mine can be rephrased in a "more
Ruby" way, maybe ;) Then, along the way, i discovered a really strange
thing that I'd like to discuss with you.
The first class adheres to the quiz specifications, but I will be frank,
anyway: I cheated. ;) The class simply stores the already generated
numbers... after all, I tought, the tradeoff beween a few kilobytes of
memory and the millions of cycles and butchering of mathematics caused
by continuously reseeding the main PRNG should be acceptable. If
somebody wants to pull hundreds of thousands of numbers in different
sequences from such a thing, he is doing the wrong thing anyway....
class Rseq
@@Instances = 0
@@Previous = 0
@@Randomize = rand 2**29
@@Randomize.freeze
def getID
@@Instances = @@Instances.succ
return ( @@Instances * 43589 + @@Randomize )
end
def initialize(arg_to_rand=0)
@ID = getID
@ID.freeze
@arg = arg_to_rand
@@Previous = @ID
srand(@ID)
@cache = [ rand( @arg ) ]
@pos = 0
end
def next
@pos = @pos.succ
if @pos <= @cache.length
return @cache[(@pos-1)]
else
if @@Previous != @ID
@@Previous = @ID
srand ( @ID + @pos )
end
@cache.push rand(@arg)
return @cache.last
end
end
def reset
@pos = 0
end
end
A better way to answer the basic quiz specifications for arbitrarily
large outputs could be to hash a counter, I realized, so I wrote another
class that works that way. Just for fun. A literal counter should work
fine with a good hash function (maybe with some feedback), but I decided
to use a string as "counter" because I believed that the Ruby hash
function was optimized for those.
class HRand
MAXINT = (2**(1.size*8 - 2) - 1).to_f
def initialize(arg_to_rand=0)
@arg = arg_to_rand.to_i
if @arg.is_a? Bignum
@FIXNUM = false
bytes = @arg.size
@D = (0x7F<< ((bytes-1)*8))
(bytes-2).times {|k| @D = (@D && (0xFF << (k*8))) }
else
@FIXNUM = true
end
@FIXNUM.freeze
@s = ''
10.times { @s = @s + rand(255).chr }
@s.freeze
@index = 0
end
def next
@index = @index.succ
if @FIXNUM
h = (@s + @index.to_s).hash.abs
return (h / MAXINT * @arg).to_i
else
num = 0
words = @arg.size/1.size
words.times {|k| n = n + ((@s + k.chr + @index.to_s).hash.abs <<
k*1.size*8) }
num[bytes*8-1] = 0
fraction = Rational(n,@D)
return (@arg * fraction).to_i
end
end
def reset
@index = 0
end
end
And it doesn't work.
Not surprising, in itself.
But it's totally unexpected and surprising that the fault, as far as I
can tell, doesn't lie in my code but in core Ruby o___O
The HRand objects output long runs of the same numbers because the
string.hash method returns SEQUENTIAL numbers for alphabetically
sequential strings!
I was taught that a hash function should strive to distribute the inputs
in its keyspace with maximal randomness, and that it should mask any
normally possible input pattern (those that are not a knowledgeable
attack at the function, I mean)..... That's not a hash function, that's
a checksum!
Does Ruby use such a dreadful function for its internal hash tables? If
so, filling a hash with previously ordered data is going to (over)fill
the hash buckets one by one... @___@
--
Posted via http://www.ruby-forum.com/.