Python的divmod
函数正常工作,几乎是我想要的。但是,对于需要执行的操作,其对非整数的行为需要略有不同。运行以下代码时,您可能会看到尝试完成的操作。
>>> function = divmod
>>> from math import pi
>>> function(pi * pi, pi) == (pi, 0)
False
>>>
如何在上面定义function
,以便最终表达式的评估结果为True
,而不是False
?如果有人能够弄明白如何获得(pi, 0)
1}}而不是(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)
以下功能可用于反转操作并检查结果。
import re
def convert_pi_to_dec(string):
match = re.search('^(\\d+) base \u03C0 \\+ (0\\.\\d+) base 10$', string)
if not match:
raise ValueError()
digits, remainder = match.groups()
return sum(int(x) * math.pi ** y for y, x in enumerate(reversed(digits))) \
+ float(remainder)
以下代码不会引发AssertionError
,因此很明显一切正常。
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 base π + 0.0 base 10
。输出是准确的,但此时并不“正确”。
编辑3:以下示例可以提供一些有关我所追求的内容的额外信息。运行具有不同π功率的循环后,我希望所有输出的形式为10... base π + 0.0 base 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
>>>
还显示了最后两个字符串是如何等效的,但输出应该是第二个字符串的形式。我发现10000000000000000000 base π
和2312130133123211021 base π
之间的差异为0.4390387422112354 base 10
令人着迷,但这种差异对代表性有很大影响。输出应该如下所示。
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
是否有我缺少的东西,是否有解决此问题的方法,或者这应该被视为傻瓜的差事?
答案 0 :(得分:3)
认识到浮点运算根据定义是不精确的。像pi*pi
这样的操作不能保证等于数学常数π^2
(就此而言math.pi
仅与“可用精度”一样准确 - 这意味着它也不是正确的值)。因此,实际上不可能对浮点数进行操作,将它们视为实数。
一般的解决方案是检查与某个epsilon值的距离,但这有明显的局限性。您最好重新检查您的基本要求(为什么需要实数精确?)并尝试从不同的方向解决问题。
对于您描述的示例,为什么需要实际使用π值?你能不能把π的实际计算结果留到最后,只对系数进行操作?
例如,直接存储列表[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
是打印前的最后一步。更好的是,你可以将这种行为包装在一个类中(并且我愿意打赌有人已经这样做了)并使__str__()
做toFloat()
的行为,以便显示你的对象给出你可以获得最准确的价值。
答案 1 :(得分:3)
您正在寻找一种算法来确定浮点数的non-integer base表示。
维基百科描述了由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)
不幸的是,这仍然不会起作用,因为OP的扩展在第一位数字中是非贪婪的,但在最后一位数字中是贪婪的。
答案 2 :(得分:0)
这个非常简单,似乎比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)
<强>更新强> 我从互联网上获得了pi的前千位数字,并使用了那个和十进制。十进制并减少了一些错误,但仍然有一对。因此,我的自信差异与精确度有关。此外,随着精度的提高,计算所需的时间也会急剧增加。