Files
RubyAlgebra/lib/ruby_algebra/polynomial.rb

300 lines
7.2 KiB
Ruby
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 = '0'
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(variable)
Polynomial.new(@terms.map { |term| term.diff(variable) })
end
def substitute(values)
Polynomial.new(@terms.map { |term| term.substitute(values) })
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)
raise StandardError, "cannot divide by zero" if other.zero?
return [Polynomial.new, Polynomial.new] if self.zero?
# Сначала проверить, что оба многочлена от одной и той же переменной
t = if @terms.empty? || @terms[-1].variables.empty? then other.terms[-1] else @terms[-1] end
unless t.variables.empty?
only_variable = t.variables.keys[0]
@terms.each do |term|
next if term.variables.empty?
unless term.variables.length == 1 && term.variables.keys[0] == only_variable
raise StandardError, "cannot divide polynomials with different variables"
end
end
other.terms.each do |term|
next if term.variables.empty?
unless term.variables.length == 1 && term.variables.keys[0] == only_variable
raise StandardError, "cannot divide polynomials with different variables"
end
end
end
quotient = Polynomial.new
remainder = self
divisor_power = unless other.terms.last.variables.empty? then other.terms.last.variables.first[1] else 0 end
loop do
break if remainder.zero?
dividend_power = unless remainder.terms.last.variables.empty? then remainder.terms.last.variables.first[1] else 0 end
break if dividend_power < divisor_power
result_power = dividend_power - divisor_power
result_coeff = remainder.terms.last.coeff.to_f / other.terms.last.coeff
if result_power > 0
q = Term.new(result_coeff, {only_variable => result_power})
else
q = Term.new(result_coeff, {})
end
quotient += q
remainder -= other * q
end
[quotient, remainder]
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
annihilated = false
while j < new_terms.length
if new_terms[i].similar_to?(new_terms[j])
new_terms[i] += new_terms[j]
new_terms.delete_at(j)
if new_terms[i].zero?
new_terms.delete_at(i)
annihilated = true
break
end
else
j += 1
end
end
i += 1 unless annihilated
end
new_terms.sort_by { |t| t.total_power }
end
def simplify
Polynomial.new(_simplify)
end
def simplify!
@terms = _simplify
end
def zero?
@terms.all? { |t| t.zero? }
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(variable)
return Term.new if @variables[variable].nil?
new_coeff = @coeff
new_variables = @variables.filter { |var, power| var != variable }
unless @variables[variable] == 1
new_coeff *= @variables[variable]
new_variables[variable] = @variables[variable] - 1
end
Term.new(new_coeff, new_variables)
end
def substitute(values)
new_coeff = @coeff
new_variables = {}
@variables.each do |var, pow|
if values[var].nil?
new_variables[var] = pow
else
new_coeff *= values[var] ** pow
end
end
Term.new(new_coeff, new_variables)
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 zero? || other.zero?
return Term.new(coeff * other, variables) unless other.is_a? Term
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 /(other)
raise StandardError, "cannot divide by zero" if other.zero?
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
if @coeff == 1 && !@variables.empty?
result = ''
elsif @coeff == -1 && !@variables.empty?
result = '-'
else
result = @coeff.to_s
end
unless @variables.empty?
first = true
@variables.each do |var, power|
if first
unless power == 1
result += "#{var}^#{power}"
else
result += "#{var}"
end
else
unless power == 1
result += " * #{var}^#{power}"
else
result += " * #{var}"
end
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