3

Python のdivmod機能はきちんと動作しており、ほぼ希望どおりです。ただし、整数以外での動作は、実行する必要がある操作に対してわずかに異なる必要があります。次のコードを実行すると、何をしようとしているかがわかります。

>>> function = divmod
>>> from math import pi
>>> function(pi * pi, pi) == (pi, 0)
False
>>> 

function最終的な式がTrueではなくに評価されるように、上記でどのように定義できます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)

次の関数を使用して、操作を逆にして結果を確認できます。

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
>>> 

また、最後の 2 つの文字列がどのように同等であるかも示されていますが、出力は 2 番目の文字列の形式である必要があります。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

私が見逃しているものはありますか、この問題の解決策はありますか、それとも愚か者の用事と見なすべきですか?

4

3 に答える 3

3

浮動小数点数の非整数ベース表現を決定するアルゴリズムを探しています。

ウィキペディアでは、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 の展開は最初の桁では貪欲ではなく、最後の桁では貪欲であるため、これでもうまくいきません。

于 2012-08-08T14:32:21.243 に答える
3

浮動小数点演算は定義上不正確であることを認識してください。のような操作pi*piは、数学定数と等しいことが保証されていませんπ^2(その点についてmath.piは、「利用可能な精度」と同じくらい正確です。つまり、正しい値でもありません)。したがって、浮動小数点数を実数のように扱う演算を実際に行うことはできません。

一般的な解決策は、あるイプシロン値からの距離をチェックすることですが、これには明らかな制限があります。根本的な要件を再検討し (なぜ実数の精度が必要なのですか?)、別の方向から問題を解決しようとする方がよいでしょう。

あなたが説明した例では、実際に π の値を使用する必要があるのはなぜですか? 実際の π の計算は最後までそのままにして、係数だけを操作していただけませんか?

たとえば、リスト[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__()dotoFloat()の動作を作成して、オブジェクトを表示することで得られる最も正確な値を得ることができます。

于 2012-08-08T14:17:42.943 に答える