def sqrt_2_direct(decimal): """ 利用直接法计算2的算数平方根 :param decimal: 自定义精度,小数点后面的数字位数 :return: 2的算数平方根 """ sqrt_number = '1.' # 算数平方根的整数部分 # 判断当前的平方根是否满足要求 while not judge_sqrt(sqrt_number, decimal): # 试错法获取下一个数字 for i in range(1, 11): # 添加数字后 new_sqrt = sqrt_number + str(i) # 计算乘积 product = b_p.big_product(new_sqrt, new_sqrt) # 判断和2的比较结果 compare_result = compare_number(product) # 如果大于2,说明上一个值是对的 if compare_result == 1: sqrt_number += str(i - 1) break # 添加1-9都不大于2,说明需要添加的就是数字9 if i == 10: sqrt_number += str(9) return sqrt_number
def sqrt_2_dichotomy(decimal): """ 利用二分法计算2的算数平方根,和直接法不同,此法是通过得到的乘积和2的差来判断是否终止 :param decimal: 乘积与2的差,绝对值小于1e(-decomical) :return: 2的算数平方根 """ min_num = '0' # 二分法开始的最小值 max_num = '2' # 二分法开始的最大值 middle_num = '' # 二分法开始的中间值 product = '1' # 通过判断当前乘积和2的差是否满足条件 while not accuracy(product, decimal): # 计算中间值,注意中间值的精度稍微大于decimal即可。 # 平方根的精确位数和乘积的精确位数基本相等 # 如果平方根的前M位是精确的,那么其平方和2的差值小数点后面连续为0的个数基本也是M个。但这并不是绝对的 middle_num = str( b_d.big_division(b_s_a.big_addition(min_num, max_num), '2', decimal + 5)) # 计算乘积 product = b_p.big_product(middle_num, middle_num) # 判断大小 compare_result = compare_number(product) if compare_result == 1: # 大值变小 max_num = middle_num elif compare_result == -1: # 小值变大 min_num = middle_num return middle_num, product
def s_dichotomy(self): """ 利用二分法计算算数平方根 :return: 最终的结果 """ min_num = '0' # 二分法开始的最小值 max_num = '4' # 二分法开始的最大值 middle_num = '2.' # 二分法开始的中间值 # 判断当前解是否满足条件,保证正确性,此时多保留几位 if self.s == 'p': leave_count = self.d + self.l + self.change elif self.s == 's': leave_count = self.d + self.l + self.change + self.add # 依然是根据精度判断 while not self.control_p(middle_num, leave_count): # 注意因为中间值的精确度小的话,会导致每次的middle_num 不变,因此下面的精度要稍微大点 middle_num = str( b_d.big_division(b_s_a.big_addition(min_num, max_num), '2', leave_count * 3)) # 计算乘积 product = b_p.big_product(middle_num, middle_num) # 判断大小 compare_result = self.compare_number(product) if compare_result == 1: # 大值变小 max_num = middle_num elif compare_result == -1: # 小值变大 min_num = middle_num else: return self.get_result(middle_num, self.s) return self.get_result(middle_num, self.s)
def get_carry(str_a, str_b): """ 计算该位可以取的商值 :param str_a: 该位的被除数 :param str_b: 除数 :return: 可以取的商值 """ for i in range(1, 11): product = b_p.big_product(str(i), str_b) if compare_number(str_a, product) == -1: return str(i - 1) elif compare_number(str_a, product) == 0: return str(i) return print('被除数' + str_a, '除数' + str_b, '计算商出现错误')
def sqrt_2_sequence(decimal): """ 构造数对。数对元素之间的商值看作根号2的近似。 和二分法、牛顿迭代法相同,此法也是通过得到的乘积和2的差来判断是否终止 :param decimal: 小数的精度 :return: """ # 初始数对 s = ['1', '1'] # 初始比值 radio = '1' # 乘积 product = '1' # 通过判断当前乘积和2的差是否满足条件 while not accuracy(product, decimal): c_s = s.copy() # 构造下一个数对 s[0] = b_s_a.big_addition(c_s[0], c_s[1]) s[1] = b_s_a.big_addition(str(b_p.big_product('2', c_s[0])), c_s[1]) # 计算比值,保留位数也要比decimal稍微大点 radio = str(b_d.big_division(s[1], s[0], decimal + 5)) # 计算乘积 product = b_p.big_product(radio, radio) return radio, product
def control_p(self, s_d, decimal): """ s_d的平方与转换后的开方的数之间的差值绝对值不大于1e(-decimal) :param s_d: 需要判断的对象,代表平方根 :param decimal: 差的精度要求 :return: 不满足返回False,满足返回True """ # 首先计算平方 squre = b_p.big_product(s_d, s_d) # 然后计算差值 sub = b_s_a.big_subtraction(squre, self.n) # 如果是负数,去掉负号 if sub[0] == '-': sub = sub[1:] # 因为是比较大小 if '.' not in sub: sub += '.0' # 开始比较大小 d_index = sub.index('.') if d_index > 1: # 因为1e(-decimal)整数部分只有一位1.0,0.1,0.01,0.001……等等 return False elif d_index == 1: if int(sub[0]) > 1: return False elif int(sub[0]) == 1: if decimal != 0: return False else: # 和1.0比较大小 if list(set(sub[2:])) == ['0']: return True else: return False else: if decimal == 0: return True # 考虑到需要开方的数,可能比较小,此时精度要在这个的基础上更小。 # 也就是对于0.0002,0.05这样的整数部分为0的,计算其小数点后面连续为0的个数 # 判断小数点后连续为0的个数 count_zero = 0 # 判断差值中小数点之后,连续为0的个数 for h in sub[2:]: if h != '0': # 只要出现其他的数字,就判断连续为0的个数是否满足需求的精度 if count_zero >= self.l + decimal + self.change: # 需要加上开方的数原来自身的精度或者 return True else: return False else: # 小数点出现后,只要为0就开始加1 count_zero += 1
def sqrt_2_newton(decimal): """ 利用牛顿迭代f计算2的算数平方根,和二分法相同,此法也是通过得到的乘积和2的差来判断是否终止 :param decimal: 乘积与2的差,绝对值小于1e(-decomical) :return: 2的算数平方根 """ start_num = '1' product = '1' # 通过判断当前乘积和2的差是否满足条件 while not accuracy(product, decimal): # a = (a + (2/a)) /2 # 保留位数也要比decimal稍微大点 start_num = b_d.big_division( b_s_a.big_addition(start_num, b_d.big_division('2', start_num, decimal + 5)), '2', decimal + 5) # 计算乘积 product = b_p.big_product(start_num, start_num) return start_num, product
def s_direct(self): """ 利用直接法获得平方根, :return: 字符串形式的算数平方根 """ # 首先确定整数位上的数字 first_n = int(self.n[0]) if first_n == 9: start_n = '3.' elif 4 <= first_n < 9: start_n = '2.' elif 1 <= first_n < 4: start_n = '1.' else: start_n = '0.' # 判断当前解是否满足条件,保证正确性,此时多保留几位 while not eval('self.control_%s' % self.s)( start_n, self.l + self.d + self.change): # 试错法获取下一个数字 for i in range(0, 11): # 添加数字后 new_sqrt = start_n + str(i) # 计算平方 product = b_p.big_product(new_sqrt, new_sqrt) # 比较结果 compare_result = self.compare_number(product) # 如果大于,说明上一个值是对的 if compare_result == 1: start_n += str(i - 1) break elif compare_result == 0: return self.get_result(new_sqrt, self.s) # 添加1-9都小于,说明需要添加的就是数字9 if i == 10: start_n += str(9) return self.get_result(start_n, self.s)
def big_division(a, b, decimal): """ 任意实数的除法 :param a: 任意实数 :param b: 任意实数 :param decimal: 结果中保留的小数位数.最后一位数字不进行任何形式的近似。 对于整除的情况,商值得小数位数如果小于decimal,则不受decimal的限制。如果大于则依然遵循decimal的限制 :return: 自定义精度的商值 """ if a == b: return 1 # 不受decimal的限制 # 判断结果的符号 sign = '' if a[0] == '-': a = a[1:] if b[0] != '-': sign = '-' else: b = b[1:] else: if b[0] == '-': b = b[1:] sign = '-' # 获取实数的小数点位数 decimal_a = 0 if '.' in a: decimal_a = len(a) - a.index('.') - 1 a = a.replace('.', '') decimal_b = 0 if '.' in b: decimal_b = len(b) - b.index('.') - 1 b = b.replace('.', '') # 最长的小数位数 max_de = max(decimal_a, decimal_b) # 两个数转为整数 if max_de: a += '0' * (max_de - decimal_a) b += '0' * (max_de - decimal_b) # 将两个数前面的0去掉 new_a = '' for i in range(len(a)): if a[i] != '0': new_a = a[i:] break new_b = '' for j in range(len(b)): if b[j] != '0': new_b = b[j:] break if new_a == '': return '0' if new_b == '': return print('WARNING:商不能为0') # 被除数放大添加的0的个数 add_0 = 0 # 如果被除数小于除数,则需要将被除数放大倍数, if compare_number(new_a, new_b) == -1: add_0 = len(new_b) - len(new_a) + 1 # 保证被除数一定大于除数,被除数比除数多一位 new_a += '0' * add_0 # a和b只是小数点位置不同,其他的数字是相同的。也就是说b是a的整数倍 if add_0: if new_a == new_b + '0': return '0.' + '0' * (add_0 - 2) + '1' # 判断开始对应的位置 start_carry = 0 for c in range(len(new_a) + 1): if compare_number(new_a[:c], new_b) >= 0: start_carry = c - 1 break # 该为对应的被除数 if start_carry == 0: dividend = '' else: dividend = new_a[:start_carry] # 下面进行竖式模拟的除法 div = '' # 存储商的结果 while not judge(div, decimal): # 这么设置,会导致后面出现为数字0,这种情况后面再处理 try: # 当前的被除数为 dividend = dividend + new_a[start_carry] # 当前位的被除数不小于除数,才可以计算商 if compare_number(dividend, new_b) >= 0: # 此位获得的商 current_num = get_carry(dividend, new_b) # 存储商 div += current_num # 计算差值 dividend = b_s.big_sub_add(dividend, b_p.big_product(current_num, new_b), '-') else: # 此时需要借位 div += '0' start_carry += 1 if dividend == '0': dividend = '' except IndexError: # 当start_carry超过new_a的长度时,此时需要就要加小数点,以及加0 if '.' not in div: div += '.' # 当前的被除数为 if dividend: dividend = dividend + '0' # 当前位的被除数大于除数,才可以计算商 if compare_number(dividend, new_b) >= 0: # 此位获得的商 current_num = get_carry(dividend, new_b) # 存储商 div += current_num # 计算差值 dividend = b_s.big_sub_add(dividend, b_p.big_product(current_num, new_b), '-') else: # 此时需要借位 div += '0' if dividend == '0': dividend = '' # 如果被除数添加过数字0,此时需要恢复 if add_0: # 首先获的小数点的位置 de_index = div.index('.') # 去除小数点 div = div.replace('.', '') # 如果小数点前面的数字多于add_0 if de_index > add_0: result_div = div[:(de_index - add_0)] + '.' + div[(de_index - add_0):] elif de_index == add_0: result_div = '0.' + div else: result_div = '0.' + '0' * (add_0 - de_index) + div else: result_div = div # 对得到的结果进行规范化处理,需要将数字最后的0去掉 for c in range(len(result_div) - 1, 0, -1): if result_div[c] == '.': result_div = result_div[:(c + 1)] + '0' break elif result_div[c] != '0': result_div = result_div[:(c + 1)] break # 添加符号 result_div = sign + result_div return result_div