Python 寻找一种不同类型的divmod函数

Python 寻找一种不同类型的divmod函数,python,floating-point,division,modulus,Python,Floating Point,Division,Modulus,Python的divmod函数工作正常,这几乎就是我想要的。但是,对于需要执行的操作,它与非整数的行为需要稍有不同。当运行以下代码时,您可能会看到正在尝试执行的操作 >>> function = divmod >>> from math import pi >>> function(pi * pi, pi) == (pi, 0) False >>> 如何在上面定义函数,使最终表达式的计算结果为真,而不是假?如果有人能想出

Python的
divmod
函数工作正常,这几乎就是我想要的。但是,对于需要执行的操作,它与非整数的行为需要稍有不同。当运行以下代码时,您可能会看到正在尝试执行的操作

>>> function = divmod
>>> from math import pi
>>> function(pi * pi, pi) == (pi, 0)
False
>>> 
如何在上面定义
函数
,使最终表达式的计算结果为
,而不是
?如果有人能想出如何获得
(pi,0)
而不是
(3.0,0.4448…
,那就是答案

编辑1:现在,对于一个更复杂的示例,下面的代码应该会生成
[3,2,1,3,2,1]

>>> x = 1 * pi ** 5 + \
        2 * pi ** 4 + \
        3 * pi ** 3 + \
        1 * pi ** 2 + \
        2 * pi ** 1 + \
        3 * pi ** 0
>>> digits = []
>>> while x:
        x, y = function(x, pi)
        digits.append(y)


>>> digits
[0.3989191524449005, 0.2212554774328268, 2.309739581793931, 0.1504440784612413,
2.858407346410207, 1.0]
>>> 
编辑2:下面显示的代码工作正常,只是它有意外但有效的输出

import math

def convert_dec_to_pi(number):
    digits = get_pi_digits(number)
    digits, remainder = correct_pi_digits(digits)
    return make_pi_string(digits, remainder)

def get_pi_digits(number):
    digits = []
    while number:
        number, digit = divmod(number, math.pi)
        digits.append(digit)
    digits.reverse()
    return digits

def correct_pi_digits(digits):
    last = len(digits) - 1
    for index, digit in enumerate(digits):
        if index < last and digit % 1 != 0:
            a, b = get_digit_options(digit, digits[index + 1])
            digits[index:index+2] = a if 0 <= a[1] < math.pi else b
    digit, remainder = divmod(digits[-1], 1)
    digits[-1] = digit
    return digits, remainder

def get_digit_options(digit, next_digit):
    a, b = math.floor(digit), math.ceil(digit)
    if a not in range(4):
        return (b, (digit - b) * math.pi + next_digit), None
    if b not in range(4):
        return (a, (digit - a) * math.pi + next_digit), None
    c, d = ((a, (digit - a) * math.pi + next_digit),
            (b, (digit - b) * math.pi + next_digit))
    return (c, d) if digit - a < 0.5 else (d, c)

def make_pi_string(digits, remainder):
    return '{} base \u03C0 + {} base 10'.format(
        ''.join(str(int(d)) for d in digits), remainder)
下面的代码没有引发断言错误,因此很明显一切都正常

for n in range(1, 36):
    value = convert_dec_to_pi(n)
    print(value)
    assert convert_pi_to_dec(value) == n
这就引出了下面的例子。输出可以毫无问题地转换回来,但人们可能会期待一些稍微不同的东西

>>> convert_dec_to_pi(math.pi * math.pi)
'30 base π + 0.44482644031997864 base 10'
>>> convert_pi_to_dec(_) == math.pi * math.pi
True
>>> 
字符串应该是
100基π+0.0基10
。此时,输出是准确的,但不是“正确的”

编辑3:下面的例子可能会让我对我的目标有更多的了解。在运行π的不同幂次的循环之后,我希望所有的输出都是
10。。。基π+0.0基10
的形式。结果与此不同,如下所示

>>> for power in range(20):
    print(convert_dec_to_pi(math.pi ** power))


1 base π + 0.0 base 10
10 base π + 0.0 base 10
30 base π + 0.44482644031997864 base 10
231 base π + 0.8422899173517213 base 10
2312 base π + 0.6461318165449161 base 10
23122 base π + 0.029882968108176033 base 10
231220 base π + 0.0938801130760924 base 10
2312130 base π + 0.7397595138779653 base 10
23121302 base π + 0.3240230542211062 base 10
231213021 base π + 0.017948446735832846 base 10
2312130210 base π + 0.05638670840988885 base 10
23121302100 base π + 0.17714406890720072 base 10
231213021000 base π + 0.5565145054551264 base 10
2312130133130 base π + 0.6366321966964654 base 10
23121301331302 base π + 3.9032618162071486e-05 base 10
231213013313020 base π + 0.00012262302157861615 base 10
2312130133123211 base π + 0.24905356925301847 base 10
23121301331232110 base π + 0.7824248909895828 base 10
231213013312321102 base π + 0.4580601707952492 base 10
2312130133123211021 base π + 0.4390387422112354 base 10
>>> convert_pi_to_dec('2312130133123211021 base π + 0.4390387422112354 base 10')
2791563949.5978436
>>> convert_pi_to_dec('10000000000000000000 base π + 0.0 base 10')
2791563949.5978436
>>> 
1 base π + 0.0 base 10
10 base π + 0.0 base 10
100 base π + 0.0 base 10
1000 base π + 0.0 base 10
10000 base π + 0.0 base 10
100000 base π + 0.0 base 10
1000000 base π + 0.0 base 10
10000000 base π + 0.0 base 10
100000000 base π + 0.0 base 10
1000000000 base π + 0.0 base 10
10000000000 base π + 0.0 base 10
100000000000 base π + 0.0 base 10
1000000000000 base π + 0.0 base 10
10000000000000 base π + 0.0 base 10
100000000000000 base π + 0.0 base 10
1000000000000000 base π + 0.0 base 10
10000000000000000 base π + 0.0 base 10
100000000000000000 base π + 0.0 base 10
1000000000000000000 base π + 0.0 base 10
10000000000000000000 base π + 0.0 base 10
还显示了最后两个字符串是如何等效的,但输出应该是第二个字符串的形式。我发现有趣的是
100000000000000000基π
2312130123211021基π
之间的差异是
0.439038742112354基10
,但这种差异对表示有很大的影响。输出应该如下所示

>>> for power in range(20):
    print(convert_dec_to_pi(math.pi ** power))


1 base π + 0.0 base 10
10 base π + 0.0 base 10
30 base π + 0.44482644031997864 base 10
231 base π + 0.8422899173517213 base 10
2312 base π + 0.6461318165449161 base 10
23122 base π + 0.029882968108176033 base 10
231220 base π + 0.0938801130760924 base 10
2312130 base π + 0.7397595138779653 base 10
23121302 base π + 0.3240230542211062 base 10
231213021 base π + 0.017948446735832846 base 10
2312130210 base π + 0.05638670840988885 base 10
23121302100 base π + 0.17714406890720072 base 10
231213021000 base π + 0.5565145054551264 base 10
2312130133130 base π + 0.6366321966964654 base 10
23121301331302 base π + 3.9032618162071486e-05 base 10
231213013313020 base π + 0.00012262302157861615 base 10
2312130133123211 base π + 0.24905356925301847 base 10
23121301331232110 base π + 0.7824248909895828 base 10
231213013312321102 base π + 0.4580601707952492 base 10
2312130133123211021 base π + 0.4390387422112354 base 10
>>> convert_pi_to_dec('2312130133123211021 base π + 0.4390387422112354 base 10')
2791563949.5978436
>>> convert_pi_to_dec('10000000000000000000 base π + 0.0 base 10')
2791563949.5978436
>>> 
1 base π + 0.0 base 10
10 base π + 0.0 base 10
100 base π + 0.0 base 10
1000 base π + 0.0 base 10
10000 base π + 0.0 base 10
100000 base π + 0.0 base 10
1000000 base π + 0.0 base 10
10000000 base π + 0.0 base 10
100000000 base π + 0.0 base 10
1000000000 base π + 0.0 base 10
10000000000 base π + 0.0 base 10
100000000000 base π + 0.0 base 10
1000000000000 base π + 0.0 base 10
10000000000000 base π + 0.0 base 10
100000000000000 base π + 0.0 base 10
1000000000000000 base π + 0.0 base 10
10000000000000000 base π + 0.0 base 10
100000000000000000 base π + 0.0 base 10
1000000000000000000 base π + 0.0 base 10
10000000000000000000 base π + 0.0 base 10

是否有我遗漏的东西,是否有解决这个问题的方法,或者这应该被认为是一件愚蠢的差事?

认识到浮点运算在定义上是不精确的。像
pi*pi
这样的运算不能保证等于数学常数
π^2
(就这一点而言,它的精度仅与“可用精度”一样高,这意味着它也不是正确的值)。因此,实际上不可能对浮点数进行像实数一样的运算

一般的解决方案是检查与某个ε值的距离,但这有明显的局限性。您最好重新检查您的基本需求(为什么需要实数精度?)

对于您描述的示例,为什么需要实际使用π的值?你能不能把π的实际计算放在最后,只对系数进行运算

例如,直接存储列表
[3,2,1,3,2,1]
,并使用隐式约定执行操作和转换,即它们是系数,然后定义如下内容:

toFloat(ls,mult):
  pow = 0
  ret = 0
  for coef in ls:
    ret += coef * mult**pow
    pow += 1
  return ret

作为打印前的最后一步。更好的是,您可以将此行为封装在一个类中(我敢打赌以前有人做过),并生成
\uu str\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu,因此,显示对象可以获得最精确的值。

您正在寻找一种算法来确定浮点数的表示形式

维基百科描述了一个贪婪的算法,这是由Rényi和Frougny造成的;下面是一个实现的尝试:

from math import log, floor
def expansion(x, b):
    k = int(floor(log(x) / log(b)))
    d, r = divmod(x / float(b ** k), 1)
    digits = [int(d)]
    for _ in range(k):
        d, r = divmod(b * r, 1)
        digits.append(int(d))
    def rest(b, d, r):
        while r:
            d, r = divmod(b * r, 1)
            yield int(d)
    return digits, rest(b, d, r)
这就给出了词典的初始扩展;您只需稍加修改即可获得按字典顺序排列的终端扩展:

def expansion(x, b, greedy=True):
    if not greedy:
        m = (floor(b) / (b - 1)) - 1
    k = int(floor(log(x) / log(b)))
    d, r = divmod(x / float(b ** k), 1)
    if not greedy and r < m:
        d, r = d - 1, r + 1
    digits = [int(d)]
    for _ in range(k):
        d, r = divmod(b * r, 1)
        if not greedy and r < m:
            d, r = d - 1, r + 1
        digits.append(int(d))
    def rest(d, r):
        while r:
            d, r = divmod(b * r, 1)
            if not greedy and r < m:
                d, r = d - 1, r + 1
            yield int(d)
    return digits, rest(d, r)
def扩展(x,b,greedy=True):
如果不贪婪:
m=(楼层(b)/(b-1))-1
k=int(楼层(对数(x)/对数(b)))
d、 r=divmod(x/浮动(b**k),1)
如果不贪婪且r

不幸的是,这仍然不起作用,因为OP的扩展在第一个数字中不是贪婪的,但在最后一个数字中是贪婪的。

这一个非常简单,似乎比OP工作得更好。我认为结果中的缺陷与精度有关:

import math
import struct
import os
from decimal import Decimal, getcontext

getcontext().prec = 1000

def digits_base_b(n, b):
    n = Decimal(n)
    b = Decimal(b)
    digits = {}
    while n >= b:
        exp = int(math.log(n, b))
        digit = int(n/b**exp)
        digits[exp] = digit
        n -= digit*b**exp
    return digits, n # n is less than b**1, idk how you want to handle those

def digits_2_str(digits, base):
    exps = sorted(digits, reverse=True)
    result = []
    format_spec = '%d*'+base+'^%d'
    for exp in exps:
        result.append(format_spec % (digits[exp], exp))
    return ' + '.join(result)

pi = Decimal(
'3.14159265358979323846264338327950288419716939937510'
'58209749445923078164062862089986280348253421170679'
'82148086513282306647093844609550582231725359408128'
'48111745028410270193852110555964462294895493038196'
'44288109756659334461284756482337867831652712019091'
'45648566923460348610454326648213393607260249141273'
'72458700660631558817488152092096282925409171536436'
'78925903600113305305488204665213841469519415116094'
'33057270365759591953092186117381932611793105118548'
'07446237996274956735188575272489122793818301194912'
'98336733624406566430860213949463952247371907021798'
'60943702770539217176293176752384674818467669405132'
'00056812714526356082778577134275778960917363717872'
'14684409012249534301465495853710507922796892589235'
'42019956112129021960864034418159813629774771309960'
'51870721134999999837297804995105973173281609631859'
'50244594553469083026425223082533446850352619311881'
'71010003137838752886587533208381420617177669147303'
'59825349042875546873115956286388235378759375195778'
'18577805321712268066130019278766111959092164201989'
)

if __name__ == '__main__':
    random_float = lambda: struct.unpack('d', os.urandom(8))[0]
    x = random_float()
    while x < pi: # some floats are no good, i've only tested with positives
        x = random_float()

    digits, leftover = digits_base_b(x, pi)
    print x, '='
    print digits_2_str(digits, u'\u03C0')

    for i in range(20):
        digits, leftover = digits_base_b(pi**i, pi)
        print float(pi**i), '=', digits_2_str(digits, u'\u03C0'), '+', float(leftover)
导入数学
导入结构
导入操作系统
从decimal导入decimal,getcontext
getcontext().prec=1000
def数字基数(n,b):
n=十进制(n)
b=十进制(b)
数字={}
当n>=b时:
exp=int(数学日志(n,b))
数字=整数(不适用**exp)
数字[exp]=数字
n-=数字*b**exp
返回数字,n#n小于b**1,idk如何处理这些数字
def数字\u 2 \u str(数字,基数):
exps=已排序(数字,反向=真)
结果=[]
格式规格=“%d*”+base+“^%d”
对于exp中的exp:
结果.追加(格式规格%(数字[exp],exp))
返回“+”。联接(结果)
pi=十进制(
'3.14159265358979323846264338327950288419716939937510'
'58209749445923078164062862089986280348253421170679'
'82148086513282306647093844609550582231725359408128'
'48111745028410270193852110555964462294895493038196'
'44288109756659334461284756482337867831652712019091'
'45648566923460348610454326648213393607260249141273'
'72458700660631558817488152092096282925409171536436'
'78925903600113305305488204665213841469519415116094'
'33057270365759591953092186117381932611793105118548'
'07446237996274956735188575272489122793818301194912'
'98336733624406566430860213949463952247371907021798'
'609437027705392171762931767523846748184