使用加,减和减半计算三角形根

时间:2014-03-27 20:59:02

标签: algorithm math optimization

特定游戏中的规则是角色的力量与角色体验的triangular root成正比。例如,15-20经验提供5个权力,21-27经验提供6个权力,28-35经验提供7个权力等等。已知一些玩家已经获得了数千亿的经验。

我试图在只有三个算术指令的8位机器上实现这个游戏:加,减,除以2.例如,要将数字乘以4,程序会将它自己添加到自身。一般乘法要慢得多;我已经编写了一个软件子程序来使用四分之一平方表来完成它。

我曾考虑计算三角形根T(p)bisection search,以获得从上方和下方划分经验数的连续三角形数字。我的计划是使用T(2*p)的重复身份,直到它超过经验,然后使用它作为二分搜索的上限。但是我在二分中T((x+y)/2)找不到x*y(x+y)^2的身份时遇到问题。

是否有一种有效的算法来计算数字的三角形根,只需加,减,减半?或者我最终必须执行O(log n)乘法,一个用于计算二分搜索中的每个中点?或者考虑实施长除法以使用牛顿方法会更好吗?

T(x)的定义:

T(x) = (n * (n + 1))/2

我派生的身份:

T(2*x) = 4*T(x) - x
# e.g. T(5) = 15, T(10) = 4*15 - 5 = 55

T(x/2) = (T(x) + x/2)/4
# e.g. T(10) = 55, T(5) = (55 + 5)/4 = 15

T(x + y) = T(x) + T(y) + x*y
# e.g. T(3) = 6, T(7) = 28, T(10) = 6 + 28 + 21 = 55

T((x + y)/2) = (T(x) + T(y) + x*y + (x + y)/2)/4
# e.g. T(3) = 6, T(7) = 28, T(5) = (6 + 28 + 21 + 10/2)/4 = 15

2 个答案:

答案 0 :(得分:5)

进行二分搜索,但要确保y - x始终是2的幂。 (这不会增加渐近运行时间。)然后T((x + y) / 2) = T(x) + T(h) + x * h,其中h是2的幂,所以x * h可以通过移位来计算。

这是一个Python概念证明(匆忙编写,或多或少未经优化,但避免了昂贵的操作)。

def tri(n):
    return ((n * (n + 1)) >> 1)

def triroot(t):
    y = 1
    ty = 1

    # Find a starting point for bisection search by doubling y using
    # the identity T(2*y) = 4*T(y) - y. Stop when T(y) exceeds t.
    # At the end, x = 2*y, tx = T(x), and ty = T(y).
    while (ty <= t):
        assert (ty == tri(y))
        tx = ty
        ty += ty
        ty += ty
        ty -= y
        x = y
        y += y

    # Now do bisection search on the interval [x .. x + h),
    # using these identities:
    # T(x + h) = T(x) + T(h) + x*h
    # T(h/2) = (T(h) + h/2)/4
    th = tx
    h = x
    x_times_h = ((tx + tx) - x)
    while True:
        assert (tx == tri(x))
        assert (x_times_h == (x * h))

        # Divide h by 2
        h >>= 1
        x_times_h >>= 1
        if (not h):
            break
        th += h
        th >>= 1
        th >>= 1

        # Calculate the midpoint of the search interval
        tz = ((tx + th) + x_times_h)
        z = (x + h)
        assert (tz == tri(z))

        # If the midpoint is below the target, move the lower bound
        # of the search interval up to the midpoint
        if (t >= tz):
            tx = tz
            x = z
            x_times_h += ((th + th) - h)
    return x
for q in range(1, 100):
    p = triroot(q)
    assert (tri(p) <= q < tri((p + 1)))
    print(q, p)

答案 1 :(得分:1)

正如在math.stackexchange.com上的链接页面中观察到的那样,有一个解决这个问题的直接公式,而x = n*(n+1)/2则反过来是:

n = (sqrt(1+8*x) - 1)/2

现在有平方根和其他东西,但我建议使用这个直接公式,实现如下:

tmp  = x + x;   '2*x
tmp += tmp;     '4*x
tmp += tmp + 1; '8*x + 1
n = 0;
n2 = 0;
while(n2 <= tmp){
    n2 += n + n + 1; 'remember that (n+1)^2 - n^2 = 2*n + 1
    n++;
}
'here after the loops  n = floor(sqrt(8*x+1)) + 1
n -= 2;         'floor(sqrt(8*x+1)) - 1
n /= 2;         '(floor(sqrt(8*x+1)) - 1) / 2

当然,如果需要考虑floor(sqrt(8*x+1)) + 1的整数值是偶数,那么可以改进以获得更好的性能,因此n可以以2为步长递增(相应地重写n2计算:n2 += n + n + n + n + 4即可本身写得比这更好。)