【问题标题】:Calculating a triangular root using add, subtract, and halve使用加法、减法和减半计算三角根
【发布时间】:2014-03-27 20:59:02
【问题描述】:

特定游戏中的规则是角色的力量与角色经验的triangular root 成正比。比如15-20经验5点,21-27点6点,28-35点7点,等等。有的玩家经验值上千亿。

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

我考虑过计算三角根T(p)bisection search 用于从上到下限定经验数的连续三角数。我的计划是使用T(2*p) 的重复标识,直到它超出经验,然后将其用作二等分搜索的上限。但我无法在不使用 x*y(x+y)^2 的二等分中找到 T((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

【问题讨论】:

    标签: algorithm math optimization


    【解决方案1】:

    进行二分搜索,但要确保y - x 始终是二的幂。 (这不会增加渐近运行时间。)然后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)
    

    【讨论】:

    【解决方案2】:

    正如在 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,它本身可以写得更好)当然可以改进以获得更好的性能比这个)。

    【讨论】:

    • 这个算法似乎是 O(n),这是我希望避免的。在 x = 100 亿的情况下,它将运行多少次迭代?
    • O(n^0.5) 因为只有一个循环,它用于计算平方根。实际上,这种方法表明您提出的问题(计算三角根)等同于计算平方根(所有其他步骤只完成一次,O(1) 也是如此。这表明现在要解决您的问题,您可以对平方根采取任何好的算法并将其代替当前的while循环。因此,如果您想使用二分法,您现在可以将其应用于平方根而不是三角根,也许您可​​以在文献中找到许多解决方案,周围。
    猜你喜欢
    • 1970-01-01
    • 2019-08-19
    • 2017-10-27
    • 2021-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-23
    • 1970-01-01
    相关资源
    最近更新 更多