特定游戏中的规则是角色的力量与角色体验的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
答案 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
即可本身写得比这更好。)