将实数转换为基数

时间:2012-12-14 17:34:02

标签: algorithm math language-agnostic

假设我有一个实数。我想用整数a和b的形式a + sqrt(b)来近似它。但我不知道a和b的值。当然我更喜欢用a和b的小值得到一个很好的近似值。让我们暂时不确定“好”和“小”是什么意思。这些术语的任何合理定义都可以。

有找到它们的理智方式吗?像continued fraction algorithm这样的东西用于查找小数的小数近似值。有关分数问题的更多信息,请参阅here

编辑:澄清一下,它是一个任意实数。我只有一堆数字。因此,根据我们想要的近似值有多好,a和b可能存在也可能不存在。蛮力自然不是一个特别好的算法。我能想到的最好的方法是开始向我的实数添加整数,对结果求平方,然后看看我是否接近整数。相当蛮力,而不是一个特别好的算法。但如果没有更好的存在,那么知道这本身就很有意思。

编辑:显然b必须为零或正数。但是a可以是任何整数。

5 个答案:

答案 0 :(得分:6)

无需持续分数;只计算b的所有“小”值的平方根(达到你认为仍然“足够小”的任何值),删除小数点前的所有内容,并将它们全部排序/存储(连同生成它的b。)

然后,当您需要逼近实数时,找到小数部分与实数的小数部分相对应的基数。这会给你b - 选择正确的a就是一个简单的减法问题。

答案 1 :(得分:5)

这实际上是一个数学问题,而不是计算机问题,但回答这个问题,我认为你是对的,你可以使用连续分数。你所做的是首先将目标数字表示为连续分数。例如,如果您想近似pi(3.14159265),则CF为:

3:7,15,1,288,1,2,1,3,1,7,4 ......

下一步是为平方根创建一个CF表,然后将表中的值与目标值的小数部分进行比较(此处:7,15,1,288,1,2,1,3 ,1,7,4 ......)。例如,假设你的表只有1-99的平方根。然后你会发现最接近的匹配是sqrt(51),它的CF重复为7:7,14。 7,14最接近pi的7,15。因此你的答案是:

SQRT(51)-4

作为最接近的给定b

使用CF的优势在于它比使用双倍或使用浮点更快。例如,在上面的例子中,你只需要比较两个整数(7和15),你也可以使用索引来快速找到表中最近的条目。

答案 2 :(得分:1)

这可以非常有效地使用mixed integer quadratic programming完成(尽管没有运行时保证,因为MIQP是NP完全的。)

定义:

d := the real number you wish to approximate
b, a := two integers such that a + sqrt(b) is as "close" to d as possible
r := (d - a)^2 - b, is the residual of the approximation

目标是尽量减少r。将您的二次程序设置为:

x := [ s b t ]
D := | 1 0 0 |
     | 0 0 0 |
     | 0 0 0 |
c := [0 -1 0]^T
with the constraint that s - t = f (where f is the fractional part of d) 
and b,t are integers (s is not)

这是一个凸(因此是最优解)混合整数二次规划,因为D是半正定的。

计算s,b,t后,只需使用b=b得出答案,s=d-a即可忽略t

你的问题可能是NP完全的,如果是这样的话会很有趣。

答案 3 :(得分:1)

以前的一些答案使用时间或空间复杂度为O(n)的方法,其中n是将被接受的最大“小数”。相反,以下方法在时间上是O(sqrt(n)),在空间中是O(1)。

假设正实数r = x + y,其中x=floor(r)0 ≤ y < 1。我们希望使用r形式的a + √b近似x+y ≈ a+√b。如果x+y-a ≈ √b然后√b ≈ h+y,那么h表示某个整数偏移b ≈ (h+y)^2(h+y)^2。为了使b成为整数,我们希望将h的小数部分最小化为所有符合条件的√n。最多h符合条件的import math, random def findb(y, rhi): bestb = loerror = 1; for r in range(2,rhi): v = (r+y)**2 u = round(v) err = abs(v-u) if round(math.sqrt(u))**2 == u: continue if err < loerror: bestb, loerror = u, err return bestb #random.seed(123456) # set a seed if testing repetitively f = [math.pi-3] + sorted([random.random() for i in range(24)]) print (' frac sqrt(b) error b') for frac in f: b = findb(frac, 12) r = math.sqrt(b) t = math.modf(r)[0] # Get fractional part of sqrt(b) print ('{:9.5f} {:9.5f} {:11.7f} {:5.0f}'.format(frac, r, t-frac, b)) 值。请参阅以下python代码和示例输出。

findb()

(注1:此代码为演示形式; y的参数为rrhi的小数部分和if round(math.sqrt(u))**2 == u: continue,平方根为最大的小数字。您可能希望更改参数的使用。注2: findb()
代码行阻止b返回b=51的完美平方值,但值b = 1除外,因为没有完美的平方可以提高b = 1提供的精度。)

示例输出如下。中间已经排除了大约十几条线。第一个输出行显示此过程产生pi来表示 frac sqrt(b) error b 0.14159 7.14143 -0.0001642 51 0.11975 4.12311 0.0033593 17 0.12230 4.12311 0.0008085 17 0.22150 9.21954 -0.0019586 85 0.22681 11.22497 -0.0018377 126 0.25946 2.23607 -0.0233893 5 0.30024 5.29150 -0.0087362 28 0.36772 8.36660 -0.0011170 70 0.42452 8.42615 0.0016309 71 ... 0.93086 6.92820 -0.0026609 48 0.94677 8.94427 -0.0024960 80 0.96549 11.95826 -0.0072333 143 0.97693 11.95826 -0.0186723 143 的小数部分,这与其他一些答案中报告的值相同。

frac, rhi = math.pi-3, 16
print ('    frac        sqrt(b)         error          b     bMax')
while rhi < 1000:
    b = findb(frac, rhi)
    r = math.sqrt(b)
    t = math.modf(r)[0]         # Get fractional part of sqrt(b)
    print ('{:11.7f}  {:11.7f}  {:13.9f}  {:7.0f}  {:7.0f}'.format(frac, r, t-frac, b,rhi**2))
    rhi = 3*rhi/2

    frac        sqrt(b)         error          b     bMax
  0.1415927    7.1414284   -0.000164225       51      256
  0.1415927    7.1414284   -0.000164225       51      576
  0.1415927    7.1414284   -0.000164225       51     1296
  0.1415927    7.1414284   -0.000164225       51     2916
  0.1415927    7.1414284   -0.000164225       51     6561
  0.1415927  120.1415831   -0.000009511    14434    14641
  0.1415927  120.1415831   -0.000009511    14434    32761
  0.1415927  233.1415879   -0.000004772    54355    73441
  0.1415927  346.1415895   -0.000003127   119814   164836
  0.1415927  572.1415909   -0.000001786   327346   370881
  0.1415927  911.1415916   -0.000001023   830179   833569

在程序结束时添加以下代码,也会出现如下所示的输出。这显示了pi的小数部分的更接近的近似值。

{{1}}

答案 4 :(得分:0)

我不知道这种问题是否有任何标准算法,但它确实引起了我的兴趣,所以这是我尝试开发一种找到所需近似值的算法。

拨打有问题的真实电话号码r。然后,首先我假设a可以是负数,在这种情况下我们可以减少问题,现在只需找到b,使得sqrt(b)的小数部分是一个很好的近似值r的小数部分。现在让我们将r写为r = x.yx为整数,y为小数部分。

Now:
b = r^2
  = (x.y)^2
  = (x + .y)^2
  = x^2 + 2 * x * .y + .y^2
  = 2 * x * .y + .y^2 (mod 1)

我们现在只需查找x 0 = .y^2 + 2 * x * .y (mod 1)(大约)。

x填入上面的公式后,我们得到b,然后可以将a计算为a = r - b。 (当然,所有这些计算都必须仔细整理。)

现在,暂时我不确定是否有办法找到这个x而没有暴力强迫它。但即使这样,人们也可以简单地使用一个简单的循环来找到x足够好的东西。

我正在考虑这样的事情(半伪代码):

max_diff_low = 0.01 // arbitrary accuracy
max_diff_high = 1 - max_diff_low
y = r % 1
v = y^2
addend = 2 * y
x = 0
while (v < max_diff_high && v > max_diff_low)
    x++;
    v = (v + addend) % 1
c = (x + y) ^ 2
b = round(c)
a = round(r - c)

现在,我认为这种算法非常有效,甚至允许您指定近似的所需精度。可以将其转换为O(1)算法的一件事是计算所有x并将它们放入查找表中。如果只关心r的前三个十进制数字(例如),查找表将只有1000个值,即只有4kb的内存(假设使用了32位整数)。

希望这对你有帮助。如果有人发现算法有任何问题,请在评论中告诉我,我会解决它。

修改 经过反思,我撤回了我的效率主张。事实上,就我无法保证上述算法将永久终止而言,即使确实如此,也可能需要很长时间才能找到一个能够充分解决方程的非常大的x

可以跟踪到目前为止发现的最佳x,并随着时间的推移放宽准确度界限,以确保算法快速终止,但可能会降低准确性。

如果只是预先计算一个查找表,这些问题当然是不存在的。