--- 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") ```