0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Re:pythonで順序数を扱いたい

Last updated at Posted at 2023-04-30

注)初回投稿のときから値のデータ管理を変えたのでコードも一新されています。

1.pythonで順序数を扱うclassがほしい

ので作りました。(バグあるかもしれません)

class Ordinal: #基本的にあるのはOrdinal([(0,0)])だけ。あとはカントール標準形でのみ生成する。
  def __init__(self,l):
    if l != []:
      self.l = l
    else:
      self.l = [(0,0)]
  
  def __repr__(self):
    if self.is_finit():
      return str(int(self))
    l = self.l
    s = []
    for i in l:
      if i[0] == 1:
        if i[1] == 0:
          s.append("1")
        elif i[1] == 1:
          s.append("omega")
        else:
          p = repr(i[1])
          if "+" in p or "*" in p or "**" in p:
            s.append(f"omega**({p})")
          else:
            s.append(f"omega**{p}")
      else:
        if i[1] == 0:
          s.append(f"{i[0]}")
        elif i[1] == 1:
          s.append(f"omega*{i[0]}")
        else:
          p = str(i[1])
          if "+" in p or "*" in p or "**" in p:
            s.append(f"omega**({p})*{i[0]}")
          else:
            s.append(f"omega**{p}*{i[0]}")
    return " + ".join(s)

  def __str__(self):
    if self.is_finit():
      return str(int(self))
    l = self.l
    s = []
    for i in l:
      if i[0] == 1:
        if i[1] == 0:
          s.append("1")
        elif i[1] == 1:
          s.append("ω")
        else:
          p = str(i[1])
          if "+" in p or "*" in p or "^" in p:
            s.append(f"ω^({p})")
          else:
            s.append(f"ω^{p}")
      else:
        if i[1] == 0:
          s.append(f"{i[0]}")
        elif i[1] == 1:
          s.append(f"ω*{i[0]}")
        else:
          p = str(i[1])
          if "+" in p or "*" in p or "^" in p:
            s.append(f"ω^({p})*{i[0]}")
          else:
            s.append(f"ω^{p}*{i[0]}")
    return " + ".join(s)

  def is_zero(self):
    if len(self.l) == 1 and type(self.l[0][1]) == int and self.l == [(0,0)]: #0であるか?
      return True
    return False

  def is_finit(self):
    if self.is_zero():
      return True
    if len(self.l) == 1 and type(self.l[0][1]) == Ordinal and self.l[0][1].is_zero(): #1以上の自然数か?
      return True
    return False

  def _richcmp(self,other):
    if self.is_zero():
      if other.is_zero():
        #print("premitive")
        return None
      else:
        return True
    else:
      if other.is_zero():
        return False
    for i in range(min(len(self.l),len(other.l))):
      B = self.l[i][1]._richcmp(other.l[i][1])
      #print(B)
      if B == True:
        return True
      elif B == False:
        return False
      if self.l[i][0] < other.l[i][0]:
        return True
      elif self.l[i][0] > other.l[i][0]:
        return False
    if len(self.l) == len(other.l):
      return None
    elif len(self.l) < len(other.l):
      return True
    else:
      return False

  def __eq__(self,other):
    #int型が混ざったときの処理
    if type(self) == int and type(other) == Ordinal:
      if self == 0:
        return Ordinal([(0,0)]) == other
      else:
        return Ordinal([(self,Ordinal([(0,0)]))]) == other
    if type(self) == Ordinal and type(other) == int:
      if other == 0:
        return self == Ordinal([(0,0)])
      else:
        return self == Ordinal([(other,Ordinal([(0,0)]))])
    #通常の処理
    B = self._richcmp(other)
    return B == None

  def __le__(self,other): # 'le' means 'self is less than or equal other'
    #int型が混ざったときの処理
    if type(self) == int and type(other) == Ordinal:
      if self == 0:
        return Ordinal([(0,0)]) <= other
      else:
        return Ordinal([(self,Ordinal([(0,0)]))]) <= other
    if type(self) == Ordinal and type(other) == int:
      if other == 0:
        return self <= Ordinal([(0,0)])
      else:
        return self <= Ordinal([(other,Ordinal([(0,0)]))])
    #通常の処理
    B = self._richcmp(other)
    return (B == True) or (B == None)


  def __lt__(self,other):
    #int型が混ざったときの処理
    if type(self) == int and type(other) == Ordinal:
      if self == 0:
        return Ordinal([(0,0)]) < other
      else:
        return Ordinal([(self,Ordinal([(0,0)]))]) < other
    if type(self) == Ordinal and type(other) == int:
      if other == 0:
        return self < Ordinal([(0,0)])
      else:
        return self < Ordinal([(other,Ordinal([(0,0)]))])
    #通常の処理
    B = self._richcmp(other)
    return B == True


  def __ge__(self,other):
    #int型が混ざったときの処理
    if type(self) == int and type(other) == Ordinal:
      if self == 0:
        return Ordinal([(0,0)]) >= other
      else:
        return Ordinal([(self,Ordinal([(0,0)]))]) >= other
    if type(self) == Ordinal and type(other) == int:
      if other == 0:
        return self >= Ordinal([(0,0)])
      else:
        return self >= Ordinal([(other,Ordinal([(0,0)]))])
    #通常の処理
    B = self._richcmp(other)
    return (B == False) or (B == None)

  def __gt__(self,other):
    #int型が混ざったときの処理
    if type(self) == int and type(other) == Ordinal:
      if self == 0:
        return Ordinal([(0,0)]) > other
      else:
        return Ordinal([(self,Ordinal([(0,0)]))]) > other
    if type(self) == Ordinal and type(other) == int:
      if other == 0:
        return self > Ordinal([(0,0)])
      else:
        return self > Ordinal([(other,Ordinal([(0,0)]))])
    #通常の処理
    B = self._richcmp(other)
    return B == False

  def __ne__(self,other):
    #int型が混ざったときの処理
    if type(self) == int and type(other) == Ordinal:
      if self == 0:
        return Ordinal([(0,0)]) != other
      else:
        return Ordinal([(self,Ordinal([(0,0)]))]) != other
    if type(self) == Ordinal and type(other) == int:
      if other == 0:
        return self != Ordinal([(0,0)])
      else:
        return self != Ordinal([(other,Ordinal([(0,0)]))])
    #通常の処理
    B = self._richcmp(other)
    return (B == True) or (B == False)

  def __int__(self):
    if self.is_zero():
      return 0
    elif self.is_finit():
      return self.l[0][0]
    return -1

  def __bool__(self):
    return self.is_zero()

  def normed(self):
    if self == Ordinal([(0,0)]):
      return Ordinal([(0,0)])
    l = list(self.l)
    for x in range(len(l)):
      l[x] = (l[x][0],l[x][1].normed())
    #print(l)
    i = 0
    while i < len(l):
      if i == 0:
        i += 1
        #print(i,l)
      elif l[i-1][1] < l[i][1]:
        l = self.l[:i-1] + l[i:]
        i -= 1
        #print(i,l)
      elif l[i-1][1] > l[i][1]:
        i += 1
        #print(i,l)
      else:
        l = l[:i-1] + [(l[i-1][0] + l[i][0],l[i][1])] + l[i+1:]
        i -= 1
        #print(i,l)
    return Ordinal(l)

  def __add__(self,other):
    #int型が混ざったときの処理
    if type(self) == Ordinal and type(other) == int:
      if other == 0:
        return self
      else:
        return self + Ordinal([(other,Ordinal([(0,0)]))])
    #そもそも二項とも0でないことを仮定する
    if self.is_zero():
      return other
    elif other.is_zero():
      return self
    #まず二分探索でotherの最高次の指数に対応するselfの指数の位置を調べる
    l_index = [i[1] for i in self.l] #指数の配列
    a = other.l[0][1]
    right = len(l_index)
    left = 0
    #print(left,right,None,l_index[left:right+1])
    while left < right:
      mid = (right+left)//2
      # left ~ mid の範囲にあるか?
      B = l_index[mid]._richcmp(a) # l_index[mid] < a の意
      if B == False:
        left = mid+1
      elif B == True:
        right = mid
      else:
        index = mid+1
        break
      #print(left,right,mid,l_index[left:right+1])
    if left == right:
      index = left
    #計算部分
    L = self.l[:index]
    if L == []:
      L = list(other.l)
    elif L[-1][1] == a:
      L[-1] = (L[-1][0]+other.l[0][0],a)
      L = L + other.l[1:]
    else:
      L = L + other.l
    return Ordinal(L)
    

  def __radd__(self,other):
    if type(self) == Ordinal and type(other) == int:
      if other == 0:
        return self
      else:
        return Ordinal([(other,Ordinal([(0,0)]))]) + self

  def __sub__(self,other): #a-b,,,self-other
    if type(self) == Ordinal and type(other) == int:
      if other == 0:
        return self
      else:
        return self - Ordinal([(other,Ordinal([(0,0)]))])
    #そもそも結果が負にならないことが前提
    if self < other:
      return None
    #そもそもself=otherでないことを確認する
    if self == other:
      return Ordinal([(0,0)])
    #そもそも二項とも0でないことを仮定する。other!=0なら、self >= other > 0より両方調べたことになる。
    if other.is_zero():
      return self
    #まずω^(a_r)*n_r > ω^(b_r)*m_rとなる初めてのrを探す。
    r = 0
    while r < len(other.l):
      if self.l[r][1] > other.l[r][1]:
        L = self.l[r:]
        return Ordinal(L)
      if self.l[r][1] == other.l[r][1] and self.l[r][0] > other.l[r][0]:
        L = [(self.l[r][0]-other.l[r][0],self.l[r][1])] + self.l[r+1:]
        return Ordinal(L)
      r += 1
    return Ordinal(self.l[r:])
      

  def __rsub__(self,other):
    if type(self) == Ordinal and type(other) == int:
      if other == 0:
        if self.is_zero():
          return Ordinal([(0,0)])
        return None
      else:
        return Ordinal([(other,Ordinal([(0,0)]))]) - self
  
  def __mul__(self,other):
    #int型が混ざったときの処理
    if type(self) == Ordinal and type(other) == int:
      if other == 0:
        return Ordinal([(0,0)])
      else:
        return self * Ordinal([(other,Ordinal([(0,0)]))])
    #そもそも0じゃないか?
    if self.is_zero() or other.is_zero():
      return Ordinal([(0,0)])
    #otherに定数項があるか?
    if other.l[-1][1].is_zero():
      m = other.l[-1][0]
    else:
      m = 0
    #mで場合分け
    if m > 0:
      a = self.l[0][1]
      L = [(i[0],a+i[1])for i in other.l[:-1]] + [(self.l[0][0]*m,self.l[0][1])] + self.l[1:]
    else:
      a = self.l[0][1]
      L = [(i[0],a+i[1])for i in other.l]
    return Ordinal(L)

  def __rmul__(self,other):
    if type(self) == Ordinal and type(other) == int:
      if other == 0:
        return Ordinal([(0,0)])
      else:
        return Ordinal([(other,Ordinal([(0,0)]))]) * self

  def _divmod(self,other):
    q = Ordinal([(0,0)])
    r = Ordinal(self.l)
    d = other.l[0][1]
    while r >= other:
      if r.l[0][1] != d:
        q = q + Ordinal([(r.l[0][0],r.l[0][1]-d)])
        r = r - other*Ordinal([(r.l[0][0],r.l[0][1]-d)])
      else:
        a = r.l[0][0]
        b = other.l[0][0]
        n = a//b
        if a > n*b: #引き算する余裕があるとき
          if n != 0:
            q = q + Ordinal([(n,Ordinal([(0,0)]))])
            r = r - other*Ordinal([(n,Ordinal([(0,0)]))])
        else: # a == n*b 
          if n != 0:
            q_ = q + Ordinal([(n,Ordinal([(0,0)]))])
            r_ = r - other*Ordinal([(n,Ordinal([(0,0)]))])
            if r_ is None:
              n -= 1
              if n != 0:
                q = q + Ordinal([(n,Ordinal([(0,0)]))])
                r = r - other*Ordinal([(n,Ordinal([(0,0)]))])
            else:
              q = q_
              r = r_
    if self != other*q+r:
      print("Fxxk you!!")
      exit()
    return q,r

  def __truediv__(self, other):
    if type(self) == Ordinal and type(other) == int:
      return self / Ordinal([(other,Ordinal([(0,0)]))])
    if other == 0:
      raise ZeroDivisionError("division by zero")
    res = self._divmod(other)
    if res[1] == 0:
      return res[0]
    return None

  def __rtruediv__(self, other):
    if type(self) == Ordinal and type(other) == int:
      return Ordinal([(other,Ordinal([(0,0)]))]) / self
    
  def __floordiv__(self, other):
    if type(self) == Ordinal and type(other) == int:
        return self // Ordinal([(other, Ordinal([(0, 0)]))])
    if other == 0:
        raise ZeroDivisionError("division by zero")
    res = self._divmod(other)
    return res[0]

  def __rfloordiv__(self, other):
      if type(self) == Ordinal and type(other) == int:
          return Ordinal([(other, Ordinal([(0, 0)]))]) // self

  def __mod__(self, other):
    if type(self) == Ordinal and type(other) == int:
      return self % Ordinal([(other, Ordinal([(0, 0)]))])
    if other == 0:
      raise ZeroDivisionError("division by zero")
    res = self._divmod(other)
    return res[1]

  def __rmod__(self, other):
    if type(self) == Ordinal and type(other) == int:
      return Ordinal([(other, Ordinal([(0, 0)]))]) % self

  def __pow__(self,other):
    #int型が混ざったときの処理
    if type(self) == Ordinal and type(other) == int:
      if other == 0:
        return Ordinal([(1,Ordinal([(0,0)]))])
      else:
        return self ** Ordinal([(other,Ordinal([(0,0)]))])
    #そもそも0じゃないか?
    if other.is_zero():
      return Ordinal([(1,Ordinal([(0,0)]))])
    elif self.is_zero():
      return Ordinal([(0,0)])
    #otherに定数項があるか?
    if other.l[-1][1].is_zero():
      m = other.l[-1][0]
    else:
      m = 0
    #selfが有限のときor無限のとき
    if self.is_finit():
      if other.is_finit():
        return Ordinal([(int(self)**int(other),Ordinal([(0,0)]))])
      if self == 1:
        return 1
      #有限の無限乗
      if m > 0:
        I = Ordinal([(1,Ordinal(other.l[:-1]))])
      else:
        I = Ordinal([(1,other)])
      a = Ordinal(list(self.l))
      b = m
      res = Ordinal([(1,Ordinal([(0,0)]))])
      while b:
        if b % 2 == 1:
          res *= a
        a = a*a
        b //= 2
      return I*res
    else:
      if m > 0:
        I = Ordinal([(1,self.l[0][1]*Ordinal(other.l[:-1]))])
      else:
        I = Ordinal([(1,self.l[0][1]*other)])
      a = Ordinal(list(self.l))
      b = m
      res = Ordinal([(1,Ordinal([(0,0)]))])
      while b:
        if b % 2 == 1:
          res *= a
        a = a*a
        b //= 2
      return I*res

  def __rpow__(self,other):
    if type(self) == Ordinal and type(other) == int:
      if other == 0:
        if self.is_zero():
          return Ordinal([(1,Ordinal([(0,0)]))])
        return Ordinal([(0,0)])
      else:
        return Ordinal([(other,Ordinal([(0,0)]))]) ** self


  def sqrt(self): #平方数であるか?
    if self.is_finit():
      n = int(self)
      if n == 0:
        return Ordinal([(0,0)])
      digit = (len(bin(n)[2:])+1)//2 # 2**len(bin(n)[2:]-1)  <=  n  <= 2**(len(bin(n)[2:]))
      left = 2**(digit-1)            # 1 -> 1, 2 -> 1, 3 -> 2, 4 -> 2
      right = 2**digit
      while left <= right:
          mid = (left + right) // 2
          square = mid * mid
          if square == n:
              return mid
          if square < n:
              left = mid + 1
          else:
              right = mid - 1
      return None
    a = self.l[0][1].l[0][0] #最高次の指数の係数
    if a%2 != 0:
      return None
    s = Ordinal([(self.l[0][0],Ordinal([(self.l[0][1].l[0][0]//2,self.l[0][1].l[0][1])]+self.l[0][1].l[1:]))])
    r = Ordinal(self.l) - s**2
    if r == 0:
      return s
    while True:
      p = r.l[0][1] - s.l[0][1]
      if p is None:
        break
      s = s + Ordinal([(r.l[0][0],p)])
      r = Ordinal(self.l) - s**2
      if r == 0:
        return s
    return None

  def pred(self):
    if self.l[-1][1].is_zero():
      m = self.l[-1][0]
      if m == 1:
        return Ordinal(self.l[:-1])
      else:
        return Ordinal(self.l[:-1]+[(m-1,Ordinal([(0,0)]))])
    else:
      return None
  
  def deg(self):
    if self.is_zero():
      return None
    return self.l[0][1]

  #計算のための情報
  #(ω^n)? = n-1             (n < ω)
  #(ω^n)? = ω^(ω^n)       (n >= ω)
  #(α+ω^n)? = α?+ω^n       (α >= ω^n)

  def factorial(self):
    #selfを展開する。つまり、ω^ω*2+ω^4*5+4 -> ω^ω+ω^ω+ω^4+ω^4+ω^4+ω^4+1+1+1+1
    #実際に展開するわけではないが、そう考えるとやりやすい...か?
    if self == 0:
      return 1
    if type(self) == int:
      self = Ordinal([(self,Ordinal([(0,0)]))])
    flag = True
    now = 0
    res = Ordinal([(1,Ordinal([(0,0)]))])
    if self.l[-1][1].is_zero():
      m = self.l[-1][0]
    else:
      m = 0
    for x in self.l:
      #print(x)
      c,p = x
      for i in range(c):
        #print(p,i,res)
        if flag:
          if p == 1:
            res = Ordinal([(1,Ordinal([(1,Ordinal([(0,0)]))]))])**(p-1) #つまり、ω^ω^(p-1)
          elif 1 < p:
            res = Ordinal([(1,Ordinal([(1,Ordinal([(0,0)]))]))])**p #つまり、ω^ω^p=ω^ω^(p-1)。統一してもいいんだけどね。
          flag = False
          now += Ordinal([(1,Ordinal([(1,Ordinal([(0,0)]))]))])**p
          continue
        if p != 0:
          res += now.deg()*Ordinal([(1,Ordinal([(1,Ordinal([(0,0)]))]))])**p
          now += Ordinal([(1,Ordinal([(1,Ordinal([(0,0)]))]))])**p
    if not self.is_finit():
      res = Ordinal([(1,Ordinal([(1,Ordinal([(0,0)]))]))])**res
    for i in range(1,m+1):
      res *= Ordinal(self.l[:-1])+i
    #print(now)
    return res 

OMEGA = Ordinal([(1,Ordinal([(1,Ordinal([(0,0)]))]))])
ω = OMEGA

コメントアウトは気にしないでください。バグ発見したらコメントで教えてください。

追記)べき乗の計算が間違っていたので修正しました。

2.機能

足し算    ... α+β
引き算    ... α-β
掛け算    ... α*β
割り算    ... α/β
切り捨て除算 ... α//β
剰余     ... α%β
平方根    ... α.sqrt()
階乗     ... α.factorial()

引き算,割り算,平方根では解が存在しない(割り切れないなど)ときはNoneが返されます。
階乗はオリジナルの定義なので異論は認めます。コメント書いてください。

3.参考文献

https://www.behounek.online/logic/papers/ordcalc/index.html
https://proofwiki.org/wiki/Division_Theorem_for_Ordinals
https://manabitimes.jp/math/1318

4.追記

意味もなくPyPIに上げました。
https://pypi.org/project/ordinal-number/
import ordinal_number でできます。

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?