From afb81b475af2fe4fe5d243cf38315ec6e542ac98 Mon Sep 17 00:00:00 2001 From: Slavasil Date: Tue, 31 Mar 2026 04:03:44 +0300 Subject: [PATCH] polynomials --- lib/ruby_algebra.rb | 7 +- lib/ruby_algebra/expression.rb | 43 +++++- lib/ruby_algebra/polynomial.rb | 232 +++++++++++++++++++++++++++++++++ 3 files changed, 272 insertions(+), 10 deletions(-) create mode 100644 lib/ruby_algebra/polynomial.rb diff --git a/lib/ruby_algebra.rb b/lib/ruby_algebra.rb index b9b54c4..d017ed9 100644 --- a/lib/ruby_algebra.rb +++ b/lib/ruby_algebra.rb @@ -1,8 +1,9 @@ # frozen_string_literal: true -require_relative "ruby_algebra/version" -require_relative "ruby_algebra/expression" -require_relative "ruby_algebra/parser" +require_relative 'ruby_algebra/version' +require_relative 'ruby_algebra/expression' +require_relative 'ruby_algebra/polynomial' +require_relative 'ruby_algebra/parser' module RubyAlgebra end diff --git a/lib/ruby_algebra/expression.rb b/lib/ruby_algebra/expression.rb index 1871ccd..4307a1f 100644 --- a/lib/ruby_algebra/expression.rb +++ b/lib/ruby_algebra/expression.rb @@ -17,12 +17,15 @@ module RubyAlgebra def op_assoc_type raise NotImplementedError end + def diff(v) raise NotImplementedError, "#{self.class}#diff not implemented" end + def evaluate raise NotImplementedError, "#{self.class}#evaluate not implemented" end + def constant? raise NotImplementedError, "#{self.class}#constant? not implemented" end @@ -33,6 +36,7 @@ module RubyAlgebra def initialize(lhs, rhs) raise ArgumentError unless lhs.is_a?(Expression) && rhs.is_a?(Expression) + @lhs = lhs @rhs = rhs end @@ -59,18 +63,22 @@ module RubyAlgebra def ==(other) return false if other.type != type + lhs == other.lhs && rhs == other.rhs end def diff(v) Addition.new(@lhs.diff(v), @rhs.diff(v)) end + def evaluate lhs_val = @lhs.evaluate rhs_val = @rhs.evaluate return nil if lhs_val.nil? || rhs_val.nil? + lhs_val + rhs_val end + def constant? @lhs.constant? && @rhs.constant? end @@ -94,12 +102,15 @@ module RubyAlgebra def diff(v) Subtraction.new(@lhs.diff(v), @rhs.diff(v)) end + def evaluate lhs_val = @lhs.evaluate rhs_val = @rhs.evaluate return nil if lhs_val.nil? || rhs_val.nil? + lhs_val - rhs_val end + def constant? @lhs.constant? && @rhs.constant? end @@ -110,6 +121,7 @@ module RubyAlgebra def initialize(lhs, rhs) raise ArgumentError unless lhs.is_a?(Expression) && rhs.is_a?(Expression) + @lhs = lhs @rhs = rhs end @@ -121,12 +133,10 @@ module RubyAlgebra if lhs.type == :constant && lhs.value == 1 need_parentheses_right ? "(#{rhs})" : rhs.to_s elsif lhs.type == :constant && lhs.value == -1 - need_parentheses_right ? "-(#{rhs})" : "-#{rhs.to_s}" + need_parentheses_right ? "-(#{rhs})" : "-#{rhs}" elsif (need_parentheses_right || rhs.type == :variable) && (need_parentheses_left || lhs.type == :mult || lhs.type == :constant) result = need_parentheses_left ? "(#{lhs})" : lhs.to_s - if lhs.type == :variable && rhs.type == :variable && lhs.single_letter? && rhs.single_letter? - result += " " - end + result += ' ' if lhs.type == :variable && rhs.type == :variable && lhs.single_letter? && rhs.single_letter? result + (need_parentheses_right ? "(#{rhs})" : rhs.to_s) else result = need_parentheses_left ? "(#{lhs}) * " : "#{lhs} * " @@ -148,6 +158,7 @@ module RubyAlgebra def ==(other) return false if other.type != type + lhs == other.lhs && rhs == other.rhs end @@ -158,12 +169,15 @@ module RubyAlgebra term2 = Multiplication.new(@lhs, v_prime) Addition.new(term1, term2) end + def evaluate lhs_val = @lhs.evaluate rhs_val = @rhs.evaluate return nil if lhs_val.nil? || rhs_val.nil? + lhs_val * rhs_val end + def constant? @lhs.constant? && @rhs.constant? end @@ -181,6 +195,7 @@ module RubyAlgebra def type :div end + def diff(v) u_prime = @lhs.diff(v) v_prime = @rhs.diff(v) @@ -192,13 +207,16 @@ module RubyAlgebra denominator = Power.new(@rhs, Constant.new(2)) Division.new(numerator, denominator) end + def evaluate lhs_val = @lhs.evaluate rhs_val = @rhs.evaluate return nil if lhs_val.nil? || rhs_val.nil? return nil if rhs_val == 0 - lhs_val / rhs_val + + lhs_val.to_f / rhs_val end + def constant? @lhs.constant? && @rhs.constant? end @@ -209,6 +227,7 @@ module RubyAlgebra def initialize(base, exponent) raise ArgumentError unless base.is_a?(Expression) && exponent.is_a?(Expression) + @base = base @exponent = exponent end @@ -235,12 +254,13 @@ module RubyAlgebra def ==(other) return false if other.type != type + base == other.base && exponent == other.exponent end def diff(v) unless @exponent.is_a?(Constant) - raise NotImplementedError, "Дифференцирование степени с неконстантным показателем не реализовано" + raise NotImplementedError, 'Дифференцирование степени с неконстантным показателем не реализовано' end n = @exponent.value @@ -250,12 +270,15 @@ module RubyAlgebra coeff = Constant.new(n) Multiplication.new(coeff, Multiplication.new(new_power, base_diff)) end + def evaluate base_val = @base.evaluate exp_val = @exponent.evaluate return nil if base_val.nil? || exp_val.nil? - base_val ** exp_val + + base_val**exp_val end + def constant? @base.constant? && @exponent.constant? end @@ -286,15 +309,18 @@ module RubyAlgebra def ==(other) return false if other.type != type + value == other.value end def diff(v) Constant.new(0) end + def evaluate @value end + def constant? true end @@ -330,15 +356,18 @@ module RubyAlgebra def ==(other) return false if other.type != type + symbol == other.symbol end def diff(v) @symbol == v ? Constant.new(1) : Constant.new(0) end + def evaluate nil end + def constant? false end diff --git a/lib/ruby_algebra/polynomial.rb b/lib/ruby_algebra/polynomial.rb new file mode 100644 index 0000000..007b1e3 --- /dev/null +++ b/lib/ruby_algebra/polynomial.rb @@ -0,0 +1,232 @@ +# frozen_string_literal: true + +module RubyAlgebra + # Многочлен - сумма одночленов + class Polynomial + # одночлены + attr_reader :terms + + def initialize(terms = []) + @terms = if terms.is_a? Term + [terms] + else + terms + end + simplify! + end + + def to_s + if @terms.empty? + result = '' + else + result = @terms[-1].to_s + i = @terms.length - 2 + while i >= 0 + result += if @terms[i].coeff > 0 + " + #{@terms[i]}" + else + " - #{-@terms[i]}" + end + i -= 1 + end + end + result + end + + def diff + raise NotImplementedError + end + + def evaluate(values) + result = 0 + @terms.each do |term| + # ... + end + result + end + + def +(other) + if other.is_a? Polynomial + Polynomial.new(@terms + other.terms).simplify + elsif other.is_a? Term + Polynomial.new(@terms + [other]).simplify + else + Polynomial.new(@terms + [Term.new(other)]).simplify + end + end + + def *(other) + if other.is_a? Polynomial + result = Polynomial.new + other.terms.each do |term| + result += self * term + end + result + else + Polynomial.new(@terms.map { |term| term * other }) + end + end + + def **(other) + result = Polynomial.new([Term.new(1)]) + other.times do + result *= self + end + result + end + + def -(other) + self + -other + end + + def +@ + self + end + + def -@ + self * -1 + end + + def ==(other) + @terms == other.terms + end + + def simplify + i = 0 + new_terms = @terms.filter { |t| !t.zero? } + while i < new_terms.length + j = i + 1 + while j < new_terms.length + if new_terms[i].similar_to?(new_terms[j]) + new_terms[i] += new_terms[j] + if new_terms[i].zero? + new_terms.delete_at(i) + i -= 1 + j -= 1 + end + new_terms.delete_at(j) + j -= 1 + end + j += 1 + end + i += 1 + end + Polynomial.new(new_terms.sort_by { |t| t.total_power }) + end + + def simplify! + i = 0 + new_terms = @terms.filter { |t| !t.zero? } + while i < new_terms.length + j = i + 1 + while j < new_terms.length + if new_terms[i].similar_to?(new_terms[j]) + new_terms[i] += new_terms[j] + if new_terms[i].zero? + new_terms.delete_at(i) + i -= 1 + j -= 1 + end + new_terms.delete_at(j) + j -= 1 + end + j += 1 + end + i += 1 + end + @terms = new_terms.sort_by { |t| t.total_power } + end + end + + # Одночлен в форме a_n * x_1^k_1 * ... * x_n^k_n + class Term + attr_reader :coeff, :variables + + def initialize(coeff = 0, variables = {}) + @coeff = coeff + @variables = {} + variables.each do |var, power| + next if power.zero? + + if @variables[var].nil? + @variables[var] = power + else + @variables[var] += power + end + end + end + + def diff(var) + raise NotImplementedError + end + + def evaluate(values) + raise NotImplementedError + end + + def +(other) + raise NotImplementedError, 'addition of non-similar terms is not implemented' unless similar_to?(other) + + Term.new(@coeff + other.coeff, @variables) + end + + def *(other) + return Term.new if @coeff.zero? || other.coeff.zero? + + new_variables = @variables.clone + other.variables.each do |symbol, power| + if new_variables[symbol].nil? + new_variables[symbol] = power + else + new_variables[symbol] += power + end + end + Term.new(@coeff * other.coeff, new_variables) + end + + def +@ + Term.new(@coeff, @variables) + end + + def -@ + Term.new(-@coeff, @variables) + end + + def ==(other) + @coeff == other.coeff && @variables == other.variables + end + + def total_power + @variables.values.sum + end + + def to_s + result = coeff.to_s + unless @variables.empty? + first = true + @variables.each do |var, power| + result += if first + "#{var}^#{power}" + else + " * #{var}^#{power}" + end + first = false + end + end + result + end + + def similar_to?(other) + return false if @variables.length != other.variables.length + + other.variables.each do |var, power| + return false if @variables[var] != power + end + true + end + + def zero? + @coeff.zero? + end + end +end