Полукольцо

Полукольцо — это алгебраическая структура, которая обобщает понятие кольца, ослабляя некоторые из его свойств. Полукольцо обладает двумя операциями (обычно называемыми сложением и умножением), которые удовлетворяют определённым аксиомам, но при этом не требуется, чтобы сложение было обратимым (то есть не требуется существование обратных элементов относительно сложения).

Определение полукольца

Полукольцо — это множество \(S\), на котором определены две бинарные операции:

Эти операции должны удовлетворять следующим аксиомам:

Примеры полуколец

Зачем нужны полукольца?

Полукольца находят применение в различных областях:

Формальное определение

В то время как группа, моноид и полугруппа определяются множеством и одной операцией, полукольцо определяется множеством и двумя операциями. Учитывая множество R и операции + и *, мы говорим, что (R, +, *) - это полукольцо, если оно удовлетворяет следующим свойствам:

Для полугруппы должны соблюдаться законы коммутативного моноида по сложению:

и законы полугруппы по умножению:

, а также дистрибутивность и мультипликативное свойство нуля:

Связь с кольцом

Полукольцо — общеалгебраическая структура, похожая на кольцо, но без требования существования противоположного по сложению элемента. В кольце же для каждого элемента \(a\) должен существовать элемент \(-a\) такой, что \(a + (-a) = 0\).

Для кольца последнее соотношение (мультипликативное свойство нуля) не требуется, поскольку оно следует из других, для полукольца оно необходимо. Отличие полукольца от кольца состоит только в том, что по сложению полукольцо образует не коммутативную группу, а только коммутативный моноид.

Код

trait MultiplicativeSemigroup[A]:
  def times(x: A, y: A): A

trait Semiring[A] extends CMonoid[A], MultiplicativeSemigroup[A]    

Числа относительно сложения с 0 и умножения

(Z, +, *)

given Semiring[Int] with
  val empty                        = 0
  def combine(x: Int, y: Int): Int = x + y
  def times(x: Int, y: Int): Int   = x * y

Законы

... // Законы коммутативного моноида

def checkMultiplicativeAssociativity[A](x: A, y: A, z: A)(using
    ms: MultiplicativeSemigroup[A]
): ValidatedNel[String, Unit] =
  Either.cond[String, Unit](
    ms.times(ms.times(x, y), z) == ms.times(x, ms.times(y, z)),
    (),
    "Не соблюдается ассоциативность по умножению: (x * y) * z должно быть равно x * (y * z)"
  ).toValidatedNel
  
def checkRightDistributivity[A](x: A, y: A, z: A)(using
    sr: Semiring[A]
): ValidatedNel[String, Unit] =
  Either.cond[String, Unit](
    sr.times(sr.combine(x, y), z) == sr.combine(
      sr.times(x, z),
      sr.times(y, z)
    ),
    (),
    "Не соблюдается дистрибутивность справа: (x + y) * z = x * z + y * z"
  ).toValidatedNel

def checkLeftDistributivity[A](x: A, y: A, z: A)(using
    sr: Semiring[A]
): ValidatedNel[String, Unit] =
  Either.cond[String, Unit](
    sr.times(x, sr.combine(y, z)) == sr.combine(
      sr.times(x, y),
      sr.times(x, z)
    ),
    (),
    "Не соблюдается дистрибутивность слева: x * (y + z) = x * y + x * z"
  ).toValidatedNel
    
def checkRightMultiplicativePropertyOfZero[A](x: A)(using
    sr: Semiring[A]
): ValidatedNel[String, Unit] =
  Either.cond[String, Unit](
    sr.times(sr.empty, x) == sr.empty,
    (),
    "Не соблюдается мультипликативное свойство нуля справа: 0 * a = 0"
  ).toValidatedNel

def checkLeftMultiplicativePropertyOfZero[A](x: A)(using
    sr: Semiring[A]
): ValidatedNel[String, Unit] =
  Either.cond[String, Unit](
    sr.times(x, sr.empty) == sr.empty,
    (),
    "Не соблюдается мультипликативное свойство нуля слева: a * 0 = 0"
  ).toValidatedNel      

Схема

classDiagram
    class Semigroup~A~{
        +combine(x: A, y: A) A
    }
    class Monoid~A~{
        +empty() A
    }
    Semigroup <|-- Monoid
    class CommutativeSemigroup~A~
    Semigroup <|-- CommutativeSemigroup  
    class CommutativeMonoid~A~
    Monoid <|-- CommutativeMonoid
    CommutativeSemigroup <|-- CommutativeMonoid
    class Semiring~A~
    CommutativeMonoid <|-- Semiring
    class MultiplicativeSemigroup~A~{
        +times(x: A, y: A) A
    }
    MultiplicativeSemigroup <|-- Semiring
    Semigroup .. MultiplicativeSemigroup

Реализация в библиотеках

Реализация в Spire

import spire.algebra.Semiring
import spire.math.Rational

Semiring.plus(Rational(1, 2), Rational(1, 3))
// val res0: spire.math.Rational = 5/6
Semiring.times(Rational(1, 2), Rational(1, 3))
// val res1: spire.math.Rational = 1/6
Semiring.pow(Rational(1, 2), 3)
// val res2: spire.math.Rational = 1/8

Ссылки: