From: serialhex Date: 2011-03-24T12:04:26+09:00 Subject: Re: Nil can't be coerced into Fixnum (TypeError) --001636c5a9edea9e2c049f31bbb0 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable hey all, so while Jesus's implementation works, unless you want to implement a prime number generator yourself you're probably better off using Rubys built-in Prime class (doc here: http://rdoc.info/stdlib/prime/1.9.2/frames = ) to do this. not only does it give you cleaner code, it is SIGNIFICANTLY faster. # the spiffified code require 'prime' # you're gonna need this require 'mathn' # you're gonna want this class PrimeFactor def initialize(number) @number =3D number end def primeFactors @factors =3D Prime.prime_division(@number, Prime::TrialDivisionGenerator.new).flatten.uniq.sort.join(', ') end end #prime =3D PrimeFactor.new(13195) prime =3D PrimeFactor.new(ARGV.shift.to_i) puts "#{prime.primeFactors}" in fact, benchmarking the two thusly: # the benchmark code: require 'prime' # you're gonna need this require 'mathn' # you're gonna want this require 'benchmark' class PrimeFactor_Long def initialize(number) @number =3D number end def primeFactors factors =3D Array.new (2..@number).each do |num| if ( @number % num =3D=3D 0 ) factors.insert(factors.length,num) end end # p factors factors.each do |factor| next if factor.nil? factors.each_with_index do |candidate,i| next if candidate =3D=3D factor next if candidate.nil? factors[i] =3D nil if candidate % factor =3D=3D 0 end end factors.compact.join(',') end end class PrimeFactor_Shrt def initialize(number) @number =3D number end def primeFactors factors =3D Prime.prime_division(@number, Prime::TrialDivisionGenerator.new).flatten.uniq.sort.join(', ') end end lots =3D 13195 bunches_of =3D 1_000 Benchmark.bmbm do |x| x.report('Long') do bunches_of.times do long =3D PrimeFactor_Long.new lots long.primeFactors end end x.report('Shrt') do bunches_of.times do shrt =3D PrimeFactor_Shrt.new lots shrt.primeFactors end end end puts long =3D PrimeFactor_Long.new lots puts "long factors: #{long.primeFactors}" shrt =3D PrimeFactor_Shrt.new lots puts "shrt factors: #{long.primeFactors}" gives this: serialhex@livecd:~/src/test> ruby bench_prime.rb Rehearsal ---------------------------------------- Long 3.160000 0.070000 3.230000 ( 3.523138) Shrt 0.050000 0.000000 0.050000 ( 0.070737) ------------------------------- total: 3.280000sec user system total real Long 3.010000 0.030000 3.040000 ( 3.227173) Shrt 0.060000 0.000000 0.060000 ( 0.070470) long factors: 5,7,13,29 shrt factors: 5,7,13,29 no joke... it's more than 50x faster :P so yeah, unless this is a project for a class or something, use the included Prime class! hex 2011/3/23 Jes=FAs Gabriel y Gal=E1n > On Wed, Mar 23, 2011 at 10:57 AM, Mayank K. > wrote: > > I am trying to execute the following code and it seems like the array i= s > > going out of bounds. Let me know where am I going wrong. > > > > class PrimeFactor > > def initialize(number) > > @number =3D number > > end > > > > def primeFactors > > factors =3D Array.new > > (2..Math.sqrt(@number).ceil).each do |num| > > if ( @number % num =3D=3D 0 ) > > factors.insert(factors.length,num) > > end > > end > > (0...factors.length).each do |i| > > prime =3D factors[i] > > factors =3D factors.select { |x| x =3D=3D prime || x % prime !=3D0= } > > end > > > > factors.compact.join(',') > > end > > end > > > > prime =3D PrimeFactor.new(13195) > > puts "#{prime.primeFactors}" > > > > C:\Temp\Study\Ruby>ruby --version > > ruby 1.8.7 (2011-02-18 patchlevel 334) [i386-mingw32] > > > > C:\Temp\Study\Ruby>ruby prime.rb > > prime.rb:16:in `%': nil can't be coerced into Fixnum (TypeError) > > from prime.rb:16:in `primeFactors' > > from prime.rb:16:in `select' > > from prime.rb:16:in `primeFactors' > > from prime.rb:14:in `each' > > from prime.rb:14:in `primeFactors' > > from prime.rb:24 > > The problem with your code is that you are modifying the factors array > inside a precalculated iteration. Try adding some print statements in > the last loop you'll see what's going on: > > [... snip...] > p factors.length > (0...factors.length).each do |i| > p factors > p i > prime =3D factors[i] > factors =3D factors.select { |x| x =3D=3D prime || x % prime !=3D0 } > end > > As you'll see, you are modifying the array, but the each loop is still > going from 0 to 7. Another approach could be to set to nil the > multiples of each factor, and then compact: > > factors.each do |factor| > next if factor.nil? > factors.each_with_index do |candidate,i| > next if candidate =3D=3D factor > factors[i] =3D nil if candidate % factor =3D=3D 0 > end > end > > BTW, your logic about the Math.sqrt being the top possible factor is > wrong (that's used to know if a number is prime). For example, for 15, > Math.sqrt(15) is less than 4, and 5 is a factor of 15 which your logic > will skip. All in all, this works: > > class PrimeFactor > def initialize(number) > @number =3D number > end > > def primeFactors > factors =3D Array.new > (2..@number).each do |num| > if ( @number % num =3D=3D 0 ) > factors.insert(factors.length,num) > end > end > p factors > factors.each do |factor| > next if factor.nil? > factors.each_with_index do |candidate,i| > next if candidate =3D=3D factor > next if candidate.nil? > factors[i] =3D nil if candidate % factor = =3D=3D 0 > end > end > factors.compact.join(',') > end > end > > #prime =3D PrimeFactor.new(13195) > prime =3D PrimeFactor.new(ARGV.shift.to_i) > puts "#{prime.primeFactors}" > > Maybe it can be further optimized, but you can start from here. This > is the ouput for 13195: > > $ ruby prime_factors.rb 13195 > [5, 7, 13, 29, 35, 65, 91, 145, 203, 377, 455, 1015, 1885, 2639, 13195] > 5,7,13,29 > > Jesus. > > --001636c5a9edea9e2c049f31bbb0--