Nil can't be coerced into Fixnum (TypeError)

Discussion in 'Ruby' started by Mayank K., Mar 23, 2011.

  1. Mayank K.

    Mayank K. Guest

    I am trying to execute the following code and it seems like the array is
    going out of bounds. Let me know where am I going wrong.

    class PrimeFactor
    def initialize(number)
    @number = number
    end

    def primeFactors
    factors = Array.new
    (2..Math.sqrt(@number).ceil).each do |num|
    if ( @number % num == 0 )
    factors.insert(factors.length,num)
    end
    end
    (0...factors.length).each do |i|
    prime = factors
    factors = factors.select { |x| x == prime || x % prime !=0 }
    end

    factors.compact.join(',')
    end
    end

    prime = 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

    --
    Posted via http://www.ruby-forum.com/.
    Mayank K., Mar 23, 2011
    #1
    1. Advertising

  2. On Wed, Mar 23, 2011 at 10:57 AM, Mayank K. <> wrot=
    e:
    > I am trying to execute the following code and it seems like the array is
    > going out of bounds. Let me know where am I going wrong.
    >
    > class PrimeFactor
    > =A0def initialize(number)
    > =A0 =A0@number =3D number
    > =A0end
    >
    > =A0def primeFactors
    > =A0 =A0factors =3D Array.new
    > =A0 =A0(2..Math.sqrt(@number).ceil).each do |num|
    > =A0 =A0 =A0if ( @number % num =3D=3D 0 )
    > =A0 =A0 =A0 =A0factors.insert(factors.length,num)
    > =A0 =A0 =A0end
    > =A0 =A0end
    > =A0 =A0(0...factors.length).each do |i|
    > =A0 =A0 =A0prime =3D factors
    > =A0 =A0 =A0factors =3D factors.select { |x| x =3D=3D prime || x % prime !=

    =3D0 }
    > =A0 =A0end
    >
    > =A0 =A0factors.compact.join(',')
    > =A0end
    > 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)
    > =A0 =A0 =A0 =A0from prime.rb:16:in `primeFactors'
    > =A0 =A0 =A0 =A0from prime.rb:16:in `select'
    > =A0 =A0 =A0 =A0from prime.rb:16:in `primeFactors'
    > =A0 =A0 =A0 =A0from prime.rb:14:in `each'
    > =A0 =A0 =A0 =A0from prime.rb:14:in `primeFactors'
    > =A0 =A0 =A0 =A0from 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
    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 =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 =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.
    Jesús Gabriel y Galán, Mar 23, 2011
    #2
    1. Advertising

  3. Mayank K.

    serialhex Guest

    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 =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
    > > 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
    > 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 =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 =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.
    >
    >
    serialhex, Mar 24, 2011
    #3
  4. Nice I dint know about the Prime class available in stdlib. Thanks a lot..
    :)

    --Mayank

    On Thu, Mar 24, 2011 at 8:34 AM, serialhex <> wrote:

    > 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/frame=

    s)
    > 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 =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 projec=

    t
    > 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

    > is
    > > > 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
    > > > 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
    > > 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 =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 =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.
    > >
    > >

    >




    --=20
    Mayank Kohaley
    Mayank Kohaley, Mar 24, 2011
    #4
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Christoffer Sawicki
    Replies:
    5
    Views:
    253
    Christoffer Sawicki
    Sep 2, 2006
  2. Ja Bo
    Replies:
    7
    Views:
    203
    Kenosis
    Dec 27, 2006
  3. Jason Lillywhite
    Replies:
    6
    Views:
    176
    Jason Lillywhite
    Jun 11, 2008
  4. Heesob Park

    Why Fixnum===Fixnum is false?

    Heesob Park, May 13, 2009, in forum: Ruby
    Replies:
    5
    Views:
    122
    Joel VanderWerf
    May 14, 2009
  5. Prateek Agarwal
    Replies:
    4
    Views:
    404
    Ken Bloom
    Jul 31, 2009
Loading...

Share This Page