Ruby Code-Cutting (few lines as possible) - ruby
So the code below is what I'm trying to cut down to as few lines as possible. Any other ruby tricks out there I could use to shorten it? I appreciate all help anyone can offer.
Article Class:
class Article
attr_accessor :id, :price, :quantity
def initialize(id, price, quantity)
#id, #price, #quantity = id, Float(price), quantity.to_i
end
end
Order Class:
class Order
def initialize(name)
#a, i = [], 0
input = File.open(name, "r")
while(id = input.gets.chomp)
j, price = 0, input.gets.chomp
while(j<#a.length)
if(#a[j].id.eql?(id.to_i))
#a[j].quantity += 1
end
end
else
#a[i] = new Article(id,price,1)
i+=1
end
end
end
def orderCost
sum = 0
#a.each { |e| sum+=(e.price * e.quantity)}
sum = ((sum*1.07) + 2.99)
end
def displaySelectArticles
min, max = #a[0], #a[0]
#a.each do |e|
if(min.cost > e.cost)
min = e
end
if(max.cost < e.cost)
max = e
end
sum += e.cost*e.quantity and q += e.quantity
end
puts "Min: #{min.cost} | Max: #{max.cost} | Avg: #{Float(sum)/q}"
end
end
I did my best here but your initialize method didnt make any logical sense to me. Hopefully this can at least guide you into the right direction. Disclaimer: None of this was tested and I wrote it off of what methods I could remember.
class Order
def initialize(name)
#a, i = [], 0
File.readlines(name) do |line|
# This while loop makes no sense to me
# Seems like a surefire infiniteloop because if id = 3, it is always true
# Maybe you meant to do an operator like == for comparison
while(id = line)
j, price = 0, line
while j < #a.length
#a[j].quantity += 1 if(#a[j].id.eql?(id.to_i))
end
else
#a[i] = new Article(id, price, 1)
i += 1
end
end
end
def orderCost
# I would probably make this two lines because its unreadable
(#a.inject(0) { |accum, e| accum + (e.price * e.quantity) } * 1.07) + 2.99
end
def displaySelectArticles
min, max = #a[0], #a[0]
#a.each do |e|
min = e if min.cost > e.cost
max = e if max.cost < e.cost
sum += e.cost * e.quantity
q += e.quantity # I have no idea how you got a q
end
puts "Min: #{min.cost} | Max: #{max.cost} | Avg: #{Float(sum)/q}"
end
end
The Article class needs serious attention because of that jumble of junk where
three variables are assigned at the same time. Split that out into three very
simple assignments:
class Article
attr_accessor :id, :price, :quantity
def initialize(id, price, quantity)
#id = id
#price = price.to_f
#quantity = quantity.to_i
end
end
Using x.to_f is preferable to Float(x), it's a gentler approach to
converting. If you're doing financial calculations I'd stronly encourage
you to use something like BigDecimal
as floating point numbers are notoriously problematic. $1.99 has a way of
becoming $1.989999423489 which when rounded can appear to "lose" a cent here
or there. The BigDecimal class avoids this by representing values as exact.
The rest is basically attacking the problem of not using Enumerable
effectively and instead writing your own routines to do what should be simple:
class Order
def initialize(name)
#items = [ ]
File.open(name, "r") do |input|
while(id = input.gets.chomp)
price = input.gets.chomp
# find locates the first matching thing in the array, if any.
existing = #items.find do |item|
item.id == id
end
if (existing)
existing.quantity += 1
else
items << Article.new(id, price, 1)
end
end
end
def item_cost
# Inject is good at iterating and accumulating
#items.inject(0.0) do |sum, item|
sum + item.price * item.quantity
end
end
def item_count
#items.inject(0) do |sum, item|
sum + item.quantity
end
end
def order_cost
item_cost * 1.07 + 2.99
end
def display_select_articles
# minmax_by finds min and max entries based on arbitrary criteria
min, max = #items.minmax_by { |item| item.price }
sum = item_cost
count = item_count
puts "Min: %f | Max: %f | Avg: %f" % [
min ? min.cost : 0.0,
max ? max.cost : 0.0,
count > 0.0 ? (sum / count) : 0.0
]
end
end
Whenever possible go with the grain, and that means using Ruby's structures and their native methods. The more you deviate from this the more code you'll have to write, and the uglier your code will get. Sometimes you're solving a difficult problem and you have no choice, but this example is not one of those cases. Everything here is simpler if you just do it the Ruby way.
Related
ruby packing variables and monkey patching
Okay so this is the code: class Board attr_reader :size def initialize n #grid = Array.new(n) {Array.new(n,:N)} #size = n * n end def [] position row, col = position #grid[row][col] end def []= position, val row, col = position #grid[row][col] = val end def num_ships #grid.flatten.count(:S) end def attack position if self[position] == :S self[position] = :H puts "you sunk my battleship" return true else self[position] = :X return false end end def place_random_ships max_ships = #size * 0.25 while self.num_ships < max_ships row = rand(0...#grid.length) col = rand(0...#grid.length) position = [row,col] self[position] = :S end end end But, def place_random_ships max_ships = #size * 0.25 while self.num_ships < max_ships row = rand(0...#grid.length) col = rand(0...#grid.length) position = [row,col] self[position] = :S end end this works, and does what it's suppose to, but when I avoid packing [row,col] and add it directly it does not work. def place_random_ships max_ships = #size * 0.25 while self.num_ships < max_ships row = rand(0...#grid.length) col = rand(0...#grid.length) self[row,col] = :S end end I'm still new to programming, so please try to explain the issue to where I can understand it, or tell me the problem, so I can google it to get a better understanding please.
The issue is that you defined []= to take 2 argument, and array and a value. def []= position, val row, col = position #grid[row][col] = val end With your current implementation you would need to call it like this foo[[row,col]] = :S What you might want to to is define []= like this: def []= row, col, val #grid[row][col] = val end then when you want to pass the array position you can use the array spread operator. With this implementation both of these calls will work. position = [1,2] foo[1,2] = :S foo[*position] = :S if you do that you probably would want to define [] the same way. def [] row,col #grid[row][col] end
How to implement Java's Comparable module in Ruby
I'm currently going over Robert Sedgewick's Algorithms book. In the book for the implementation of a Priority Queue there is the use of the Comparable module. While going over the top k frequent elements leetcode problem I noticed that there would be an error in my Ruby implementation. def top_k_frequent(nums, k) ans = [] h = Hash.new(0) nums.each do |num| h[num] += 1 end heap = Heap.new h.each do |k,v| heap.insert({k => v}) end k.times do a = heap.del_max ans.push(a.keys[0]) end ans end class Heap def initialize #n = 0 #pq = [] end def insert(v) #pq[#n += 1] = v swim(#n) end def swim(k) while k > 1 && less((k / 2).floor, k) swap((k / 2).floor, k) k = k/2 end end def swap(i, j) temp = #pq[i] #pq[i] = #pq[j] #pq[j] = temp end def less(i, j) #pq[i].values[0] < #pq[j].values[0] end def del_max max = #pq[1] swap(1, #n) #n -= 1 #pq[#n + 1] = nil sink(1) max end def sink(k) while 2 * k <= #n j = 2 * k if !#pq[j + 1].nil? j += 1 if j > 1 && #pq[j].values[0] < #pq[j + 1].values[0] end break if !less(k, j) swap(k, j) k = j end end end Above is the Java Priority Queue implementation.
Ruby's comparable operator is <=> which will return one of -1, 0, 1 and nil (nil mean could not compare). In order to compare two objects , both need to implement a method def <=>(other). This is not on Object, so is not available on any objects that don't implement it or extend from a class that does implement it. Numbers and Strings, for example, do have an implementation. Hashes do not. I think in your case, the issue is slightly different. When you call queue.insert(my_hash) what you're expecting is for the algorithm to break up my_hash and build from that. Instead, the algorithm takes the hash as a single, atomic object and inserts that. If you add something like: class Tuple attr_accessor :key, :value def initialize(key, value) #key = key #value = value end def <=>(other) return nil unless other.is_a?(Tuple) value <=> other.value end end then this will allow you to do something like: hsh = { 1 => 3, 2 => 2, 3 => 1} tuples = hsh.map { |k, v| Tuple.new(k, v) } tuples.each { |tuple| my_heap.insert(tuple) } you will have all of your data in the heap. When you retrieve an item, it will be a tuple, so you can just call item.key and item.value to access the data.
How do I fix a problem to call a function in Ruby?
I'm trying to use some ruby code that I've found in Github. I've downloaded the code and did the necessary imports the "requires" and tried to run it as it is described in the readme file on github repository. The code is the following: In the file pcset_test.rb the code is the following: require './pcset.rb' require 'test/unit' # # When possible, test cases are adapted from # Introduction to Post-Tonal Theory by Joseph N. Straus, # unless obvious or otherwise noted. # class PCSetTest < Test::Unit::TestCase def test_init #assert_raise(ArgumentError) {PCSet.new []} assert_raise(ArgumentError) {PCSet.new [1, 2, 3, 'string']} assert_raise(ArgumentError) {PCSet.new "string"} assert_raise(ArgumentError) {PCSet.new [1, 2, 3.6, 4]} assert_equal([0, 1, 2, 9], PCSet.new([0, 1, 2, 33, 13]).pitches) assert_equal([3, 2, 1, 11, 10, 0], PCSet.new_from_string('321bac').pitches) assert_equal([0,2,4,5,7,11,9], PCSet.new([12,2,4,5,7,11,9]).pitches) assert_nothing_raised() {PCSet.new []} end def test_inversion end def test_transposition end def test_multiplication end # # set normal prime forte # # 0,2,4,7,8,11 7,8,11,0,2,4 0,1,4,5,7,9 6-31 # 0,1,2,4,5,7,11 11,0,1,2,4,5,7 0,1,2,3,5,6,8 7-Z36 # 0,1,3,5,6,7,9,10,11 5,6,7,9,10,11,0,1,3 0,1,2,3,4,6,7,8,10 9-8 # def test_normal_form testPC = PCSet.new [0,4,8,9,11] assert_kind_of(PCSet, testPC.normal_form) assert_equal([8,9,11,0,4], testPC.normal_form.pitches) assert_equal([10,1,4,6], PCSet.new([1,6,4,10]).normal_form.pitches) assert_equal([2,4,8,10], PCSet.new([10,8,4,2]).normal_form.pitches) assert_equal([7,8,11,0,2,4], PCSet.new([0,2,4,7,8,11]).normal_form.pitches) assert_equal([11,0,1,2,4,5,7], PCSet.new([0,1,2,4,5,7,11]).normal_form.pitches) assert_equal([5,6,7,9,10,11,0,1,3], PCSet.new([0,1,3,5,6,7,9,10,11]).normal_form.pitches) end def test_prime_form assert_equal([0,1,2,6], PCSet.new([5,6,1,7]).prime.pitches) assert_equal([0,1,4], PCSet.new([2,5,6]).prime.pitches) assert_equal([0,1,4,5,7,9], PCSet.new([0,2,4,7,8,11]).prime.pitches) assert_equal([0,1,2,3,5,6,8], PCSet.new([0,1,2,4,5,7,11]).prime.pitches) assert_equal([0,1,2,3,4,6,7,8,10], PCSet.new([0,1,3,5,6,7,9,10,11]).prime.pitches) end def test_set_class testPcs = PCSet.new([2,5,6]) testPrime = testPcs.prime assert_equal([ [2,5,6], [3,6,7], [4,7,8], [5,8,9], [6,9,10], [7,10,11], [8,11,0],[9,0,1], [10,1,2],[11,2,3],[0,3,4], [1,4,5], [6,7,10],[7,8,11],[8,9,0], [9,10,1],[10,11,2],[11,0,3], [0,1,4], [1,2,5], [2,3,6], [3,4,7], [4,5,8], [5,6,9] ].sort, PCSet.new([2,5,6]).set_class.map{|x| x.pitches}) assert_equal(testPcs.set_class.map{|x| x.pitches}, testPrime.set_class.map{|x| x.pitches}) end def test_interval_vector assert_equal([2,1,2,1,0,0], PCSet.new([0,1,3,4]).interval_vector) assert_equal([2,5,4,3,6,1], PCSet.new([0,1,3,5,6,8,10]).interval_vector) assert_equal([0,6,0,6,0,3], PCSet.new([0,2,4,6,8,10]).interval_vector) end def test_complement assert_equal([6,7,8,9,10,11], PCSet.new([0,1,2,3,4,5]).complement.pitches) assert_equal([3,4,5], PCSet.new([0,1,2], 6).complement.pitches) end # # Test values from (Morris 1991), pages 105-111 # Citation: # Morris. Class Notes for Atonal Music Theory # Lebanon, NH. Frog Peak Music, 1991. # def test_invariance_vector assert_equal([1,0,0,0,5,6,5,5],PCSet.new([0,2,5]).invariance_vector) assert_equal([2,2,2,2,6,6,6,6],PCSet.new([0,1,6,7]).invariance_vector) assert_equal([6,6,6,6,6,6,6,6],PCSet.new([0,2,4,6,8,10]).invariance_vector) assert_equal([1,0,0,0,0,0,0,0],PCSet.new([0,1,2,3,4,5,8]).invariance_vector) assert_equal([1,0,0,1,0,0,0,0],PCSet.new([0,1,2,3,5,6,8]).invariance_vector) assert_equal([12,12,12,12,0,0,0,0],PCSet.new([0,1,2,3,4,5,6,7,8,9,10,11]).invariance_vector) end # # Test values from (Huron 1994). Huron rounds, thus the 0.01 margin of error. # Citation: # Huron. Interval-Class Content in Equally Tempered Pitch-Class Sets: # Common Scales Exhibit Optimum Tonal Consonance. # Music Perception (1994) vol. 11 (3) pp. 289-305 # def test_huron h1 = PCSet.new([0,1,2,3,4,5,6,7,8,9,10,11]).huron assert_in_delta(-0.2, h1[0], 0.01) assert_in_delta(0.21, h1[1], 0.01) h2 = PCSet.new([0,2,4,5,7,9,11]).huron assert_in_delta(4.76, h2[0], 0.01) assert_in_delta(0.62, h2[1], 0.01) end def test_coherence end end And in the file pcset.rb the folloing code: # # => PCSet Class for Ruby # => Beau Sievers # => Hanover, Fall 2008. # # # TODO: Make this a module to avoid namespace collisions. # Lilypond and MusicXML output # include Math def choose(n, k) return [[]] if n.nil? || n.empty? && k == 0 return [] if n.nil? || n.empty? && k > 0 return [[]] if n.size > 0 && k == 0 c2 = n.clone c2.pop new_element = n.clone.pop choose(c2, k) + append_all(choose(c2, k-1), new_element) end def append_all(lists, element) lists.map { |l| l << element } end def array_to_binary(array) array.inject(0) {|sum, n| sum + 2**n} end # the following method is horrifically inelegant # but avoids monkey-patching. # TODO: do this right, incl. error checking def pearsons(x, y) if !x.is_a?(Array) || !y.is_a?(Array) then raise StandardError, "x and y must be arrays", caller end if x.size != y.size then raise StandardError, "x and y must be same size", caller end sum_x = x.inject(0) {|sum, n| sum + n} sum_y = y.inject(0) {|sum, n| sum + n} sum_square_x = x.inject(0) {|sum, n| sum + n * n} sum_square_y = y.inject(0) {|sum, n| sum + n * n} xy = [] x.zip(y) {|a, b| xy.push(a * b)} sum_xy = xy.inject(0) {|sum, n| sum + n} num = sum_xy - ((sum_x * sum_y)/x.size) den = Math.sqrt((sum_square_x - ((sum_x*sum_x)/x.size)) * (sum_square_y - ((sum_y*sum_y)/x.size))) (num/den) end class PCSet include Comparable attr_reader :pitches, :base, :input def initialize(pcarray, base = 12) if pcarray.instance_of?(Array) && pcarray.all?{|pc| pc.instance_of?(Fixnum)} #base, #input = base, pcarray #pitches = pcarray.map{ |x| x % #base }.uniq else raise ArgumentError, "Improperly formatted PC array", caller end end def PCSet.new_from_string(pcstring, base = 12) if base > 36 then raise StandardError, "Use PCSet.new to create pcsets with a base larger than 36", caller end pcarray = [] pcstring.downcase.split(//).each do |c| if c <= 'z' and c >= '0' then pcarray.push(c.to_i(36)) end end PCSet.new pcarray, base end def <=>(pcs) #pitches <=> pcs.pitches end def [](index) #pitches[index] end # Intersection def &(other) PCSet.new #pitches & other.pitches end # Union def |(other) PCSet.new #pitches | other.pitches end def inspect #pitches.inspect end def length #pitches.length end def invert(axis = 0) PCSet.new #pitches.map {|x| (axis-x) % #base} end def invert!(axis = 0) #pitches.map! {|x| (axis-x) % #base} end def transpose(interval) PCSet.new #pitches.map {|x| (x + interval) % #base} end def transpose!(interval) #pitches.map! {|x| (x + interval) % #base} end def multiply(m = 5) PCSet.new #pitches.map {|x| (x * m) % #base} end def multiply!(m = 5) #pitches.map! {|x| (x * m) % #base} end def zero transpose(-1 * #pitches[0]) end def zero! transpose!(-1 * #pitches[0]) end def transpositions (0..(#base-1)).to_a.map{|x| #pitches.map {|y| (y + x) % #base}}.sort.map {|x| PCSet.new x} end def transpositions_and_inversions(axis = 0) transpositions + invert(axis).transpositions end # # Normal form after Straus. Morris and AthenaCL do this differently. # def normal_form tempar = #pitches.sort arar = [] # [[1,4,7,8,10],[4,7,8,10,1], etc.] get each cyclic variation tempar.each {arar.push PCSet.new(tempar.unshift(tempar.pop))} most_left_compact(arar) end def normal_form! #pitches = normal_form.pitches end def is_normal_form? self.pitches == self.normal_form.pitches end def set_class transpositions_and_inversions.map{|pcs| pcs.normal_form}.sort end def prime most_left_compact([normal_form.zero, invert.normal_form.zero]) end def prime! self.pitches = self.prime.pitches end def is_prime? self.pitches == self.prime.pitches end def complement new_pitches = [] #base.times do |p| if !#pitches.include? p then new_pitches.push p end end PCSet.new new_pitches end def full_interval_vector pairs = choose(#pitches, 2) # choose every pc pair intervals = pairs.map {|x| (x[1] - x[0]) % #base} # calculate every interval i_vector = Array.new(#base-1).fill(0) intervals.each {|x| i_vector[x-1] += 1} # count the intervals i_vector end def interval_vector i_vector = full_interval_vector (0..((#base-1)/2)-1).each {|x| i_vector[x] += i_vector.pop} i_vector end # # Morris's invariance vector # def invariance_vector(m = 5) t = transpositions.map!{|pcs| self & pcs} ti = invert.transpositions.map!{|pcs| self & pcs} tm = multiply(m).transpositions.map!{|pcs| self & pcs} tmi = invert.multiply(m).transpositions.map!{|pcs| self & pcs} tc = complement.transpositions.map!{|pcs| self & pcs} tic = complement.invert.transpositions.map!{|pcs| self & pcs} tmc = complement.multiply(m).transpositions.map!{|pcs| self & pcs} tmic = complement.invert.multiply(m).transpositions.map!{|pcs| self & pcs} [t, ti, tm, tmi, tc, tic, tmc, tmic].map{|x| x.reject{|pcs| pcs.pitches != #pitches}.length} end # Huron's aggregate dyadic consonance measure. # Huron. Interval-Class Content in Equally Tempered Pitch-Class Sets: # Common Scales Exhibit Optimum Tonal Consonance. # Music Perception (1994) vol. 11 (3) pp. 289-305 def huron if #base != 12 then raise StandardError, "PCSet.huron only makes sense for mod 12 pcsets", caller end # m2/M7 M2/m7 m3/M6 M3/m6 P4/P5 A4/d5 huron_table = [-1.428, -0.582, 0.594, 0.386, 1.240, -0.453] interval_consonance = [] interval_vector.zip(huron_table) {|x, y| interval_consonance.push(x * y) } aggregate_dyadic_consonance = interval_consonance.inject {|sum, n| sum + n} [aggregate_dyadic_consonance, pearsons(interval_vector, huron_table)] end # # Balzano's vector of relations. Citation for all Balzano methods: # # Balzano. "The Pitch Set as a Level of Description for Studying Musical # Pitch Perception" in Music, Mind, and Brain ed. Clynes. Plenum Press. 1982. # def vector_of_relations (0..length-1).to_a.map do |i| (0..length-1).to_a.map do |j| (#pitches[(i + j) % length] - #pitches[i]) % #base end end end # # Checks if the set satisfies Balzano's uniqueness. # def is_unique? vector_of_relations.uniq.size == vector_of_relations.size end # # Checks if the set satisfies Balzano's scalestep-semitone coherence. # For all s[i] and s[i1]: # j < k => v[i][j] < v[i1][k] # Where j and k are scalestep-counting indices. # And unless v[i][j] == 6 (a tritone), in which case the strict inequality is relaxed. # def is_coherent? v = vector_of_relations truth_array = [] all_pair_indices = choose((0..length-1).to_a, 2) all_pair_indices.each do |i, i1| all_pair_indices.each do |j, k| if v[i][j] == 6 truth_array.push(v[i][j] <= v[i1][k]) else truth_array.push(v[i][j] < v[i1][k]) end if v[i1][j] == 6 truth_array.push(v[i1][j] <= v[i][k]) else truth_array.push(v[i1][j] < v[i][k]) end end end !truth_array.include?(false) end # # Strict Balzano coherence, no inequality relaxation for tritones. # def is_strictly_coherent? v = vector_of_relations truth_array = [] all_pair_indices = choose((0..length-1).to_a, 2) all_pair_indices.each do |i, i1| all_pair_indices.each do |j, k| truth_array.push(v[i][j] < v[i1][k]) truth_array.push(v[i1][j] < v[i][k]) end end !truth_array.include?(false) end def notes(middle_c = 0) noteArray = ['C','C#','D','D#','E','F','F#','G','G#','A','A#','B'] if #base != 12 then raise StandardError, "PCSet.notes only makes sense for mod 12 pcsets", caller end out_string = String.new transpose(-middle_c).pitches.each do |p| out_string += noteArray[p] + ", " end out_string.chop.chop end def info print "modulo: #{#base}\n" print "raw input: #{#input.inspect}\n" print "pitch set: #{#pitches.inspect}\n" print "notes: #{notes}\n" print "normal: #{normal_form.inspect}\n" print "prime: #{prime.inspect}\n" print "interval vector: #{interval_vector.inspect}\n" print "invariance vector: #{invariance_vector.inspect}\n" print "huron ADC: #{huron[0]} pearsons: #{huron[1]}\n" print "balzano coherence: " if is_strictly_coherent? print "strictly coherent\n" elsif is_coherent? print "coherent\n" else print "false\n" end end # def lilypond # # end # # def musicXML # # end ############################################################################### private # # Convert every pitch array to a binary representation, e.g.: # [0,2,4,8,10] -> 010100010101 # 2^n: BA9876543210 # The smallest binary number is the most left-compact. # def most_left_compact(pcset_array) if !pcset_array.all? {|pcs| pcs.length == pcset_array[0].length} raise ArgumentError, "PCSet.most_left_compact: All PCSets must be of same cardinality", caller end zeroed_pitch_arrays = pcset_array.map {|pcs| pcs.zero.pitches} binaries = zeroed_pitch_arrays.map {|array| array_to_binary(array)} winners = [] binaries.each_with_index do |num, i| if num == binaries.min then winners.push(pcset_array[i]) end end winners.sort[0] end end I'm calling them as follows: > my_pcset = PCSet.new([0,2,4,6,8,10]) > my_pcset2 = PCSet.new([1,5,9]) It shoud return: > my_pcset = PCSet.new([0,2,4,6,8,10]) => [0, 2, 4, 6, 8, 10] > my_pcset2 = PCSet.new([1,5,9]) => [1, 5, 9] But is returning nothing. The code is available on github Thanks
Try this in terminal: irb -r ./path_to_directory/pcset.rb and then initialize the objects.
I think the documentation for the repo is bad as it does not explain how you should be running this. The result of my_pcset = PCSet.new([0,2,4,6,8,10]) should set my_pcset to an instance of a PCSet not an array, so these lines from the README file are confusing at best. 3. How to use it Make new PCSets: my_pcset = PCSet.new([0,2,4,6,8,10]) => [0, 2, 4, 6, 8, 10] my_pcset2 = PCSet.new([1,5,9]) => [1, 5, 9] Looking at the code, I see inspect has been delegated to #pitches def inspect #pitches.inspect end I think if you inspect my_pcset you will get the expected result. my_pcset = PCSet.new([0,2,4,6,8,10]) p my_pcset # will print [0, 2, 4, 6, 8, 10] or `my_pcset.inspect` will return what you are expecting.
Euler 23 in Ruby
All right. I think I have the right idea to find the solution to Euler #23 (The one about finding the sum of all numbers that can't be expressed as the sum of two abundant numbers). However, it is clear that one of my methods is too damn brutal. How do you un-brute force this and make it work? sum_of_two_abunds?(num, array) is the problematic method. I've tried pre-excluding certain numbers and it's still taking forever and I'm not even sure that it's giving the right answer. def divsum(number) divsum = 1 (2..Math.sqrt(number)).each {|i| divsum += i + number/i if number % i == 0} divsum -= Math.sqrt(number) if Math.sqrt(number).integer? divsum end def is_abundant?(num) return true if divsum(num) > num return false end def get_abundants(uptonum) abundants = (12..uptonum).select {|int| is_abundant?(int)} end def sum_of_two_abunds?(num, array) #abundant, and can be made from adding two abundant numbers. array.each do |abun1| array.each do |abun2| current = abun1+abun2 break if current > num return true if current == num end end return false end def non_abundant_sum ceiling = 28123 sum = (1..23).inject(:+) + (24..ceiling).select{|i| i < 945 && i % 2 != 0}.inject(:+) numeri = (24..ceiling).to_a numeri.delete_if {|i| i < 945 && i % 2 != 0} numeri.delete_if {|i| i % 100 == 0} abundants = get_abundants(ceiling) numeri.each {|numerus| sum += numerus if sum_of_two_abunds?(numerus, abundants) == false} return sum end start_time = Time.now puts non_abundant_sum #Not enough numbers getting excluded from the total. duration = Time.now - start_time puts "Took #{duration} s "
Solution 1 A simple way to make it a lot faster is to speed up your sum_of_two_abunds? method: def sum_of_two_abunds?(num, array) array.each do |abun1| array.each do |abun2| current = abun1+abun2 break if current > num return true if current == num end end return false end Instead of that inner loop, just ask the array whether it contains num - abun1: def sum_of_two_abunds?(num, array) array.each do |abun1| return true if array.include?(num - abun1) end false end That's already faster than your Ruby code, since it's simpler and running faster C code. Also, now that that idea is clear, you can take advantage of the fact that the array is sorted and search num - abun1 with binary search: def sum_of_two_abunds?(num, array) array.each do |abun1| return true if array.bsearch { |x| num - abun1 <=> x } end false end And making that Rubyish: def sum_of_two_abunds?(num, array) array.any? do |abun1| array.bsearch { |x| num - abun1 <=> x } end end Now you can get rid of your own special case optimizations and fix your incorrect divsum (which for example claims that divsum(4) is 5 ... you should really compare against a naive implementation that doesn't try any square root optimizations). And then it should finish in well under a minute (about 11 seconds on my PC). Solution 2 Or you could instead ditch sum_of_two_abunds? entirely and just create all sums of two abundants and nullify their contribution to the sum: def non_abundant_sum ceiling = 28123 abundants = get_abundants(ceiling) numeri = (0..ceiling).to_a abundants.each { |a| abundants.each { |b| numeri[a + b] = 0 } } numeri.compact.sum end That runs on my PC in about 3 seconds.
more ruby way of doing project euler #2
I'm trying to learn Ruby, and am going through some of the Project Euler problems. I solved problem number two as such: def fib(n) return n if n < 2 vals = [0, 1] n.times do vals.push(vals[-1]+vals[-2]) end return vals.last end i = 1 s = 0 while((v = fib(i)) < 4_000_000) s+=v if v%2==0 i+=1 end puts s While that works, it seems not very ruby-ish—I couldn't come up with any good purely Ruby answer like I could with the first one ( puts (0..999).inject{ |sum, n| n%3==0||n%5==0 ? sum : sum+n }).
For a nice solution, why don't you create a Fibonacci number generator, like Prime or the Triangular example I gave here. From this, you can use the nice Enumerable methods to handle the problem. You might want to wonder if there is any pattern to the even Fibonacci numbers too. Edit your question to post your solution... Note: there are more efficient ways than enumerating them, but they require more math, won't be as clear as this and would only shine if the 4 million was much higher. As demas' has posted a solution, here's a cleaned up version: class Fibo class << self include Enumerable def each return to_enum unless block_given? a = 0; b = 1 loop do a, b = b, a + b yield a end end end end puts Fibo.take_while { |i| i < 4000000 }. select(&:even?). inject(:+)
My version based on Marc-André Lafortune's answer: class Some #a = 1 #b = 2 class << self include Enumerable def each 1.upto(Float::INFINITY) do |i| #a, #b = #b, #a + #b yield #b end end end end puts Some.take_while { |i| i < 4000000 }.select { |n| n%2 ==0 } .inject(0) { |sum, item| sum + item } + 2
def fib first, second, sum = 1,2,0 while second < 4000000 sum += second if second.even? first, second = second, first + second end puts sum end
You don't need return vals.last. You can just do vals.last, because Ruby will return the last expression (I think that's the correct term) by default.
fibs = [0,1] begin fibs.push(fibs[-1]+fibs[-2]) end while not fibs[-1]+fibs[-2]>4000000 puts fibs.inject{ |sum, n| n%2==0 ? sum+n : sum }
Here's what I got. I really don't see a need to wrap this in a class. You could in a larger program surely, but in a single small script I find that to just create additional instructions for the interpreter. You could select even, instead of rejecting odd but its pretty much the same thing. fib = Enumerator.new do |y| a = b = 1 loop do y << a a, b = b, a + b end end puts fib.take_while{|i| i < 4000000} .reject{|x| x.odd?} .inject(:+)
That's my approach. I know it can be less lines of code, but maybe you can take something from it. class Fib def first #p0 = 0 #p1 = 1 1 end def next r = if #p1 == 1 2 else #p0 + #p1 end #p0 = #p1 #p1 = r r end end c = Fib.new f = c.first r = 0 while (f=c.next) < 4_000_000 r += f if f%2==0 end puts r
I am new to Ruby, but here is the answer I came up with. x=1 y=2 array = [1,2] dar = [] begin z = x + y if z % 2 == 0 a = z dar << a end x = y y = z array << z end while z < 4000000 dar.inject {:+} puts "#{dar.sum}"
def fib_nums(num) array = [1, 2] sum = 0 until array[-2] > num array.push(array[-1] + array[-2]) end array.each{|x| sum += x if x.even?} sum end