From: Jesse Merriman Date: 2008-02-10T23:39:39+09:00 Subject: [SOLUTION][QUIZ] Internal Rate of Return (#156) --Boundary-00=_xywrHn/OeCBxkgG Content-Type: Multipart/Mixed; boundary="Boundary-00=_xywrHn/OeCBxkgG" --Boundary-00=_xywrHn/OeCBxkgG Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Content-Disposition: inline Here's my first solution, attached and pastied here: http://pastie.caboo.se/149976 Its basically a binary search, repeatedly calculating the NVP and adjusting lower and upper bounds. Not too complicated, so let me just note a couple things: * Line 12 could have been a mirror of line 10, but I wanted to emphasize the symmetry. * Rationals are used throughout to ensure accuracy. * The "+ 1" on line 10 prevents 0 from being a fixed point. * The given accuracy guarantees the result will be within 1 / 10 ** [accuracy] of the true answer. This is not the same thing as having [accuracy] number of correct digits. (Though the program could be made to do that if the guesses fell on negative powers of ten). I haven't done all that many quizzes, but those I have were very enjoyable. Thank you James. -Jesse Merriman --Boundary-00=_xywrHn/OeCBxkgG Content-Type: application/x-ruby; name="irr.rb" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="irr.rb" #!/usr/bin/env ruby # Ruby Quiz 156: Internal Rate of Return # Jesse Merriman require 'rational' # Guess a new number between a lower and upper bound. def guess lower, upper if upper == 1.0/0.0 lower < 0 ? 0.to_r : (lower + 1) * 2 elsif lower == 1.0/0.0 - guess(-upper, -lower) else (lower + upper) / 2 end end # Calculate the IRR to within 1 / 10**accuracy def irr cash_flows, accuracy = 4 nvp = lambda do |irr| t = 0 cash_flows.inject { |sum, c| t += 1; sum + c / (1 + irr) ** t } end lower, upper = -1.to_r, 1.0/0.0 while (lower - upper).abs > Rational(1, 10 ** accuracy) g = guess lower, upper nvp[g] > 0 ? lower = g : upper = g end (lower + upper) / 2 end class String def to_r if m = /^(\-?\d+)$/.match(self) m[1].to_i.to_r elsif m = /^(\-?\d+)\.(\d+)$/.match(self) m[1].to_i + Rational(m[2].to_i, 10 ** m[2].length) elsif m = /^(\-?\d+)\s*\/\s*(\d+)$/.match(self) Rational m[1].to_i, m[2].to_i else raise StandardError, "Can't parse #{self} to a Rational" end end end if __FILE__ == $0 cash_flows = ARGV[0..-2].map { |x| x.to_r } accuracy = ARGV[-1].to_i if cash_flows.inject { |sum, x| sum + x } < 0 puts 'Cannot solve with net negative.' else i = irr cash_flows, accuracy puts "irr: #{i} (#{i.to_f})" end end --Boundary-00=_xywrHn/OeCBxkgG-- --Boundary-00=_xywrHn/OeCBxkgG--