---
tags: Python系統設計
---
# 大數運算_除法.py
20240503發現兩個bug記得改進,一個是Quotient,一個是CommaAddress,剛好兩個bug錯的相輔相成,導致結果盡然是正確的,要不適偶然,還真沒發現。
```python=
'''大數運算除法'''
'''缺點被除數過大,除數過小,運算時間會很長,等哪天心情好記得多寫個函式專門用來計算除數小於多少時的運算。'''
import gc
import copy
''''''
def borrow(Remainder):
'''
Remainder為整數list,
由於Python中陣列是傳址運算,所以不做Remainder結果回傳。
return 0:執行正常狀態結果表示
'''
for i in range(len(Remainder)):
if Remainder[i] < 0:
for j in range(i - 1, 0 - 1, -1):
if Remainder[j] > 0:
for k in range(j, i + 1):
if k == j:
Remainder[k] -= 1
elif k == i:
Remainder[k] += 10
else:
Remainder[k] += 9
break
return 0
def sub(Remainder, Divisor):
'''
return 1:回傳相減1次做為計數
'''
Remainder = [x - y for x, y in zip(Remainder, Divisor)]
'''
因為只有被除數大於等於除數的時候才會呼叫sub,所以會像減法那樣,需要因為除數大於被除數,將差做正負好翻轉,直接呼叫借位既可。
'''
borrow(Remainder)
return 1, Remainder
def move(Remainder, Dividend, Frequency):
'''
向後借位,分向被除數借位,跟向小數點後借位。
return 向被除數借位次數,向小數點後就位次數,用來計算當到達小數後第幾位時停止計算。
'''
for i in range(Frequency):
if Dividend:
'''借位前先移除Remainder的第一位數,Remainder向Dividend借位,並移除Dividend中被借位的數。'''
Remainder.remove(Remainder[0])
Remainder.append(Dividend[0])
Dividend.remove(Dividend[0])
return 1, 0
elif not Dividend:
'''被除數被借位完後,向小數點後借位。'''
Remainder.remove(Remainder[0])
Remainder.append(0)
return 0, 1
def compare(Remainder, Divisor):
'''
return 0:代表被除數比除數大
return 1:除數比被除數大
return 2:相等
'''
state =0
if not any(Remainder + Divisor):
return 2 # x,y內容物全都為0
for x, y in zip(Remainder, Divisor):
if (x > y):
state = 1
continue # 迴圈能跑完表示兩數相等或被除數大於除數
if (x == y):
continue # 迴圈能跑完表示兩數相等或被除數大於除數
elif (state == 0)and(x < y):
return 1
return 0
def Count(x, y, DividendCount, DecimalCount, Quotient):
'''DividendCount向被除數借位次數'''
DividendCount[0] += x
'''DecimalCount向小數點後借位次數'''
DecimalCount[0] += y
# '''假如DecimalCount==1則表示已經開始向小數點後借位,須在商中插入.'''
# if DecimalCount[0] == 1:
# Quotient.append('.')
return 0
def div(Dividend, Divisor, Quotient, Remainder, CommaAddress):
''''''
SubCount = 0 # 計入呼叫過幾次sub
DividendCount = [0] # 計入向後借位幾次,用來計算小數點後幾位停止迴圈。
DecimalCount = [0] # 計入被除數被借完後
# Remainder = [0]*len(Divisor) #餘數當作被除數的暫時容器,長度等於除數。
if sum(Dividend+Divisor)==0:
Quotient.append(0)
return 0
elif sum(Dividend)==0:
Quotient.append(0)
return 0
elif sum(Divisor)==0:
Quotient.append(0)
return 0
elif Dividend == Divisor:
Quotient.append(1)
return 1
if len(Dividend) < len(Divisor):
for i in range(len(Divisor) - len(Dividend)):
Dividend.append(0)
for i in range(len(Divisor)):
'''將被除數與除數對等長度的數放入餘數做暫存跟運算'''
Remainder.append(Dividend[0])
Dividend.remove(Dividend[0])
while (len(Quotient) <= CommaAddress):
state = compare(Remainder, Divisor)
if state == 0:
'''被除數大於等除數,則呼叫減法。'''
z, Remainder = sub(Remainder, Divisor)
SubCount += z
elif state == 1:
'''除數大於被除數,則被除數向後借位'''
x, y = move(Remainder, Dividend, 1)
Count(x, y, DividendCount, DecimalCount, Quotient)
# '''除數大於被除數,則被除數向後借位'''
# x,y = move(Remainder,Dividend,1)
if SubCount == 0:
'''SubCount==0的時候,每向後借位一次,商加一個0。'''
Quotient.append(0)
elif SubCount > 0:
'''每當SubCount>0需要借位時,subcount加入商'''
Quotient.append(SubCount)
'''每當需要借位時,subcount計數歸0'''
SubCount = 0
elif state == 2:
'''被除數與除數相等,則商+1,被除數歸零,然後做位移'''
z, Remainder = sub(Remainder, Divisor)
# Quotient.append(z)
# Remainder = [0] * len(Divisor) # 餘數當作被除數的暫時容器,長度等於除數。
x, y = move(Remainder, Dividend, 1)
Count(x, y, DividendCount, DecimalCount, Quotient)
pass
return 0
'''
被除數=商*除數+餘數
Dividend=Quotient*Divisor+Remainder
'''
n1 = 1.1
n2 = 2.2
x = str(n1) # 被除數
y = str(n2) # 除數
Quotient = [] # 商
Remainder = [] # 餘數
xl = [] # 被加數轉換成整數存入陣列
yl = [] # 加數轉換成整數存入陣列
t = [] # 和的陣列
save = [] # 暫存變數
imaxlen = 0 # 整數最大長度
fmaxlen = 0 # 小數最大長度
CommaAddress = 0 # .在x或y中的位址
continue_state = True # 跳過狀態
'''分割x,y字串,分出整數與小數,並移除.號'''
xi = x.split(".")[0] # xi整數
xf = x.split(".")[1] # xf小數
yi = y.split(".")[0] # yi整數
yf = y.split(".")[1] # yf小數
'''將結果轉成str傳回,宣告字串strs'''
strs = ""
'''判斷整數xi,yi哪個最長存入imaxlen'''
if len(xi) >= len(yi):
imaxlen = len(xi)
else:
imaxlen = len(yi)
'''判斷小數xf,yf哪個最長存入fmaxlen'''
if len(xf) >= len(yf):
fmaxlen = len(xf)
else:
fmaxlen = len(yf)
'''根據xi,yi中整數最長數值imaxlen給相較短的整數前面補0'''
while len(xi) <= imaxlen:
xi = "0" + xi
yi = "0" + yi
'''將(xi+xf)合併後存入xl'''
for i in (xi + xf):
xl.append(int(i))
'''將(yi+yf)合併後存入yl'''
for i in (yi + yf):
yl.append(int(i))
print(xl)
print(yl)
Dividend = copy.copy(xl)
Divisor = copy.copy(yl)
DecimalLength = 21
div(Dividend, Divisor, Quotient, Remainder, CommaAddress+(DecimalLength if (len(xi)*len(yi))<=DecimalLength else (len(xi)*len(yi))))
if(len(xi)>=len(yi)):
CommaAddress = (len(xi)-len(yi))+1
elif(len(yi)==0 or yi[0]==0):
for i in yf:
if i == 0:
CommaAddress+=1
elif i>0:
break
CommaAddress = ((len(xi)-len(yi))+1)
print("CommaAddress = ",CommaAddress)
'''邏輯是將t的長度減去最長小數CommaAddress的長度,找到.在t中的插入點位址'''
Quotient.insert(CommaAddress, ".")
'''t陣列去除多餘的0'''
continue_state = True # 此時重製continue_state跳過判斷為True
'''從t的開頭開始去除多餘的0'''
for i in range(len(Quotient)):
if continue_state and Quotient[i] == 0:
'''continue_state為True且開頭為0則跳過'''
continue
else:
'''從t頭開始找,遇到第一個大於0的整數'''
continue_state = False # 關閉跳過判斷
save.append(Quotient[i]) # 並將此後的所有整數加入save陣列
Quotient.clear() # 清空t陣列
continue_state = True # 此時重製continue_state跳過判斷為True
'''從t的尾部開始去除多餘的0'''
for i in range(len(save) - 1, 0 - 1, -1):
if continue_state and save[i] == 0:
'''continue_state為True且尾部為0則跳過'''
continue
else:
'''從t尾開始找,遇到第一個大於0的整數'''
continue_state = False # 關閉跳過判斷
Quotient.insert(0, save[i]) # 並將此後的所有整數插入t陣列
'''將(int)t:list 轉成 strs:String,方便return傳回重新丟入函式計算'''
for i in Quotient:
strs += str(i)
'''由於減法會有0.0的問題,在做去除0的運算中,會有.1、1.、.的輸出問題,故須要在此做出判斷進行修正。'''
if strs == ".":
strs = "0.0"
elif strs[len(strs)-1] == ".":
strs = strs+"0"
elif Quotient[0] == ".":
strs = "0"+strs
Quotient = strs
print(Quotient," = Quotient")
print(n1/n2," = n1/n2")
```