new to ruby, seeking advice


E

en.eric.m

I'll try to keep this as brief as possible. I heard about Ruby on
Rails and thought it was interesting, however, it was a bit hard to
work with without knowing ruby itself. So I've started learning ruby.

I had written a simple java (it was actually so simple it might as well
have been written in c) program to brute force solution to a series of
equations in 3 variables. I decided that recrating the program in ruby
would be a good test of what I had learned so far.

The exact equations aren't really the important point, and I've hard
coded the initial conditions more than would be desireable in a general
purpose solution. However, In terms of producing an accurate solution,
the program does quite well. I've got comments in the code that point
out where I think things are going well and where I wasn't as happy
with the solution I came up with.

I'll take any advice on how to make this program take full advantage of
ruby.

Thanks

# From Java program
# x= 8.48550
# y=-4.42140
# z=-2.90655

# From Ruby program
# x= 8.485481
# y=-4.421231
# z=-2.906773

#This class I'm pretty happy with, but there might be a better way to
get it initialized
class Eq3Var
def initialize(xCo, xPow, yCo, yPow, zCo, zPow, const)
@xCo = xCo.to_f
@yCo = yCo.to_f
@zCo = zCo.to_f
@xPow = xPow.to_f
@yPow = yPow.to_f
@zPow = zPow.to_f
@const = const.to_f
end

def eval (x, y, z)
@xCo * x**@xPow + @yCo * y**@yPow + @zCo * z**@zPow + @const
end
end

class Solver
# I think this method does as good a job of providing the initial
conditions as hard coding them gets
# These starting values are actually strangely lucky. Trying to give
the program a better place to start
# often produces worse results.
def initialize()
@eqs = []
@eqs[0] = Eq3Var.new(1, 1, -1, 1, 1, 1, -10)
@eqs[1] = Eq3Var.new(1, 2, 1, 2, 1, 2, -100)
@eqs[2] = Eq3Var.new(1, 3, 1, 3, 1, 3, -500)

@bestSol = [0.0, 0.0, 0.0]
@range = 10.0
@step = 0.5
@factor = 5.0
end

#this is where things get clunky
#even if you aren't at all interested in math you can see the repeated
code
def force()
puts "Starting best solution: #{@bestSol.join(', ')}"
a = @eqs[0].eval(@bestSol[0], @bestSol[1], @bestSol[2])
b = @eqs[1].eval(@bestSol[0], @bestSol[1], @bestSol[2])
c = @eqs[2].eval(@bestSol[0], @bestSol[1], @bestSol[2])
min = a**2.0 + b**2.0 + c**2.0

xInit = @bestSol[0]
yInit = @bestSol[1]
zInit = @bestSol[2]

i = xInit - @range
j = yInit - @range
k = zInit - @range

while i < xInit + @range
while j < yInit + @range
while k < zInit + @range

a = @eqs[0].eval(i, j, k)
b = @eqs[1].eval(i, j, k)
c = @eqs[2].eval(i, j, k)
t = a**2.0 + b**2.0 + c**2.0

if (t < min)
min = t
@bestSol[0] = i
@bestSol[1] = j
@bestSol[2] = k
#puts "new min #{t}"
end
k = k + @step
end
j = j + @step
k = zInit - @range
end
i = i + @step
j = yInit - @range
k = zInit - @range
end
@range = @range / @factor
@step = @step / @factor
puts "a is #{@eqs[0].eval(@bestSol[0], @bestSol[1], @bestSol[2])} - b
is #{@eqs[1].eval(@bestSol[0], @bestSol[1], @bestSol[2])} - c is
#{@eqs[2].eval(@bestSol[0], @bestSol[1], @bestSol[2])}"
return @bestSol
end

def getRange()
return @range
end

def getStep()
return @step
end
end

mySolver = Solver.new()
# there has got to be a way to itterate over this without having to
create a variable like this
# but the class is taking care of itself without any input from this
loop except for how many times to
# keep looking for a better solution
itr = 0
10.times do
guess = mySolver.force()
r = mySolver.getRange()
s = mySolver.getStep()
itr = itr + 1
puts "In #{itr} itterations, the best solution so far: #{guess.join(',
')}. Refine with range of #{r} and step of #{s}"
end
 
Ad

Advertisements

D

Dmitry Buzdin

Hi! Yeah, your post is "as brief as possible" :)
It is preferred to define getter methods by attr_reader command. No
more Java style getThis() setThat().

class Solver
attr_reader :range, :step
....
end
solver = Solver.new()
solver.range # Calling getter method here
 
W

William James

class Eq3Var
def initialize(xCo, xPow, yCo, yPow, zCo, zPow, const)
@xCo = xCo.to_f
@yCo = yCo.to_f
@zCo = zCo.to_f
@xPow = xPow.to_f
@yPow = yPow.to_f
@zPow = zPow.to_f
@const = const.to_f
end

class Eq3Var
def initialize( *args )
x_co, x_pow, y_co, y_pow, z_co, z_pow, const =
args.map{ |x| x.to_f }
 
E

en.eric.m

Thanks all for the advice. This post will be shorter. :)

One thing I noticed when making these changes was that every time I
made the code more compact, it seemed to run slower. Is this normal
for ruby? (I doubt this would be nearly as much of an issue if I
weren't number crunching.)

# From Java program
# x= 8.48550
# y=-4.42140
# z=-2.90655

# From Ruby program
# x= 8.485481
# y=-4.421231
# z=-2.906773


class Eq3Var
def initialize( *args )
@xCo, @xPow, @yCo, @yPow, @zCo, @zPow, @const = args.map{ |x|
x.to_f }
end

def eval (x, y, z)
@xCo * x**@xPow + @yCo * y**@yPow + @zCo * z**@zPow + @const
end
end

class Solver
attr_reader :range, :step, :bestSol

def initialize()
*@eqs = Eq3Var.new(1, 1, -1, 1, 1, 1, -10), Eq3Var.new(1, 2, 1, 2,
1, 2, -100), Eq3Var.new(1, 3, 1, 3, 1, 3, -500)

@range, @step, @factor, *@bestSol = 10.0, 0.5, 5.0, 0.0, 0.0, 0.0
end

def sumSquare( *args )
s = 0
args.each do |x|
s += x**2.0
end
return s
end


def force()
puts "Starting best solution: #{@bestSol.join(', ')}"
min = sumSquare(@eqs[0].eval(*@bestSol), @eqs[1].eval(*@bestSol),
@eqs[2].eval(*@bestSol))

*init = *@bestSol
i, j, k = init[0] - @range, init[1] - @range, init[2] - @range

while i < init[0] + @range
while j < init[1] + @range
while k < init[2] + @range
t = sumSquare(@eqs[0].eval(i, j, k), @eqs[1].eval(i, j, k),
@eqs[2].eval(i, j, k))

if (t < min)
min = t
@bestSol = i, j, k
#puts "new min #{t}"
end

k = k + @step
end
j, k = j + @step, init[2] - @range
end
i, j, k = i + @step, init[1] - @range, init[2] - @range
#puts "#{@bestSol[0]} #{@bestSol[1]} #{@bestSol[2]} a is
#{@eqs[0].eval(@bestSol[0], @bestSol[1], @bestSol[2])} - b is
#{@eqs[1].eval(@bestSol[0], @bestSol[1], @bestSol[2])} - c is
#{@eqs[2].eval(@bestSol[0], @bestSol[1], @bestSol[2])}"
end

@range, @step = @range / @factor, @step / @factor
puts "a is #{@eqs[0].eval(@bestSol[0], @bestSol[1], @bestSol[2])} - b
is #{@eqs[1].eval(@bestSol[0], @bestSol[1], @bestSol[2])} - c is
#{@eqs[2].eval(@bestSol[0], @bestSol[1], @bestSol[2])}"
end
end

mySolver = Solver.new()
n = ' '
itr = 0
10.times do
mySolver.force()
itr = itr + 1
puts "In #{itr} itterations, the best solution so far:
#{mySolver.bestSol.join(', ')}. Refine with range of #{mySolver.range}
and step of #{ mySolver.step}"
end

# The code looks prettier, but it runs slower :(
 
Ad

Advertisements

S

s0nspark

Thanks all for the advice. This post will be shorter. :)

One thing I noticed when making these changes was that every time I
made the code more compact, it seemed to run slower. Is this normal
for ruby? (I doubt this would be nearly as much of an issue if I
weren't number crunching.)

Well, that may simply be part of the cost of cleaner code, but I'd
suggest you profile the different versions and see what differences
appear... you can do this by running the scripts with the -rprofile
argument (i.e. "ruby -rptofile script.rb") ... From that you can see
where most of the time is being spent (which methods take the longest
to run and/or have the most calls) and you may be able to tune it from
there...

HTH,
Tim
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top