请不要分支


14

任何精通低级代码优化的人都知道分支的危险,无论将其实现为if语句,循环还是select语句,分支错误预测的可能性都是一件很浪费时钟的事情。

使用简单的算法可以更好地解决简单的问题,所以让我们这样做。

对于以下问题,所有变量都是32位无符号整数,并且唯一允许的代码是仅包含以下运算符的纯集语句:

+ addition
- subtraction
* multiplication
/ integer division, rounds down, division by 0 not allowed
% modulo
& binary and
| binary or
^ binary exclusive or
>> bitshift right
<< bitshift left

Logic operators, return 1 if the expression is true and 0 if it is false.
== equal
!= not equal
< less than
<= less than or equal
> greater than
>= greater than or equal

Set operator
=

每行必须包含一个变量标识符,后跟一个集合运算符,后跟一个表达式。

表达式可以不包含其他集合运算符,但可以包含变量标识符,文字数字和括号。

高尔夫球得分仅计入操作员人数。

例:

myvar = ( ( ( foo + 5 ) * bar ) % 7 ) == 3

拥有5位操作员的分数。

一个解决方案可以包含作者认为合适的变量。
未设置的变量具有value 0
溢和下溢是允许的,所有的负数下溢,所以3 - 54294967294,即使作为一个更大的语句的一部分。

任务1:最大

程序终止时,作用域中存在两个值AB,使RESULT变量包含这些值中的最大值。

任务2:中位数

三个值,ABC,在存在的范围中,进行RESULT可变包含那些值当程序终止时的中位数。

任务3:平方根

一个价值 A在程序终止时,作用域中存在使RESULT变量包含的平方根A,四舍五入。

可以仅对一个或两个问题发布答案,对某些人来说,找到有效的解决方案将是一个挑战。


一元运算符在哪里?我不在乎,-~可能会很好(即使我不知道该做什么)。
John Dvorak

当然,0xFFFF_FFFF_FFFF_FFFF ^ x0 - x。我怎么会忘记了?
约翰·德沃夏克

@JanDvorak进行了最简短的描述,因为完整性逻辑也不!是很琐碎的:x == 0
aaaaaaaaaaaaaa 2013年

被零除的行为是什么?
约翰·德沃夏克

在Mathematica中(a> b)返回True或False。Boole将False转换为0,将True转换为1。使用合法Boole[a-b]吗?
DavidC

Answers:


5

任务3,操作23

x = (A >> 16) + A / ((A >> 13) + 511) + 15
x = (x + A/x) >> 1
x = (x + A/x) >> 1
x = (x + A/x) >> 1
RESULT = x - (x > A/x)

与其他解决方案一样,使用牛顿方法,并且选择的种子更加机智。第一位A >> 16使范围的顶部保持快乐,第二位A / ((A >> 13) + 511)使范围的中间保持快乐,最后一位15使底部保持快乐,并且还防止除以零误差(15是可能的最大值,允许0正确收敛-减半三倍减去校正等于零)。对于输入值225, 275625, 82137969, 2908768489(和附近的值),初始种子是精确的。该范围上的所有边缘情况(完美的正方形,完美的正方形+ 1和完美的正方形-1)0 .. 2**32-1均已测试并且正确。

关于规则的一些注释:
允许上溢和下溢,所有负数都下溢,因此3-5是4294967294,即使作为较大语句的一部分

最后一点证明是创新杀手。我最初尝试使用Halley方法的广义形式的解决方案,但意识到鉴于上述限制,该方法无效。迭代(应用于平方根)是这样的:

x = x * (3*A + x*x) / (A + 3*x*x)

此迭代具有牛顿所没有的良好品质。它三次收敛(而不是二次收敛),它从上方或下方收敛(而不是从上方收敛),并且对选择不好的种子不那么敏感(如果提供的牛顿迭代值太低,它将大大超出收敛点,然后需要逐步降低)。

牛顿方法还存在一个问题(至少在处理整数时),它经常会到达一个x,使得A / x-x = 2-在这种情况下,它将收敛到比适当的整数根大一个的值,需要纠正的;哈雷的方法不需要这种校正。但不幸的是,的值3*A + x*x通常会大于允许的32位整数空间。

还有许多其他通用的 n 根算法,但是它们都有相同的特征:

x = x + x*(v - x**n)/(v*n)
x = (x*(n+1) - x**(n+1)/v)/n
x = ((n-2)*x + (4*v*x)/(v + x**n))/n
x = x*((n+2)*v + (n-2)*x**n)/(v + x**n)/n
x = ((n-2)*x + (n*x*v)/(v + (n-1)*x**n))/(n-1)
x = ((n-2)*x + x*((n*2-1)*v + x**n)/(v + (n*2-1)*x**n))/(n-1)

x = x + 2*x*(v - x**n)/(v + x**n)/n
x = x + x*31*(v - x**n)/(10*v + 21*x**n)/n
x = x + x*561*(v - x**n)/(181*v + 380*x**n)/n
x = x + x*1153*(v - x**n)/(372*v + 781*x**n)/n

等。大多数显示三次或二次收敛。后四个是四次收敛的一系列迭代的一部分。但实际上,除非您需要计算数百位数字,否则牛顿方法将通过较少的操作为您提供所需的信息。


很好,但是失败了4294967295。至于规则,他们必须严格使其有趣。您可以争辩说确切的前提会带来最大的挑战,但是最终,规则要清晰明确,远比它们允许的条件重要得多。
aaaaaaaaaaaaaa 2013年

我认为哈雷无论如何都不会值得,从遥远的猜测来看它将提高不到3倍,牛顿的增长不到2倍。同样,从好的猜测来看,哈雷将提高三倍。准确性,牛顿会加倍。因此,一个哈雷迭代值得完全log(3)/log(2) ~= 1.585牛顿迭代。
aaaaaaaaaaaaaa

@eBusiness我最初有2个Halley's,选择了类似的种子,总共25个操作-当出现错误时A = 0-所以实际上较短。大约4294967295,这是一个疏忽:由于65536²≡0,校正迭代无法校正。我看看是否可以找到其他选择。
primo 2013年

@eBusiness已修复。
2013年

背包的圆滑根部,干得好,并有官方的胜利徽章。
aaaaaaaaaaaaaa

5

65(61)次行动(5 + 13 + 47(43))

任务1-最大值(A,B)

RESULT = A + (B - A) * (A <= B)

这是显而易见的解决方案。您需要分配,需要比较,需要将比较与某物相乘,被乘数不能是变量之一,乘积不能是结果。

任务2-中(A,B,C)

RESULT = A                               \
       + (B - A) * (A > B) ^ (B <= C)    \
       + (C - A) * (A > C) ^ (C <  B)

这是对我之前的15步解决方案的改进,后者解决了所有三个变量的问题-节省了两个减法,但是引入了另一个中心性测试。测试本身很简单:一个元素位于中间,而两个元素中的另一个恰好在上面。

任务3 -sqrt(A)

X1     = 1024 + A / 2048
X2     = (X1  + A / X1 ) / 2
...
X10    = (X9 + A / X9 ) / 2
RESULT = X16 - (X16 * X16 > A)

十一轮牛顿逼近。魔法常数1024已被击败WolframW(在a = 2 ** 32收敛之前512使a = 0除以零),但是如果我们可以将0/0定义为零,那么十次迭代将使用起始值512。我确实承认我对十次迭代的主张并不完全清楚,但是我仍然在括号中主张它们。 但是,我将调查是否有九种可能。WolframH的解决方案九次迭代。


我认为任务3的第一行是不正确的:第二个常数应该是第一个常数的4倍(具有“纯”牛顿)。
恢复莫妮卡

@WolframH更好的初步猜测可能可以解释为什么我浪费周期。您在哪里拿出4 *?看起来好像有两次迭代合而为一。
John Dvorak

(1024 + A/1024)/2 == (512 + A/2048)(类似于X0 = 1024,然后启动Newton)。
恢复莫妮卡

解决任务1的好方法。
DavidC

@DavidCarraher当然,正确的解决方案是MOV RESULT, A; CMP A,B; CMOVA RESULT, B;-)
John Dvorak

5

1:5个运算符

RESULT = B ^ (A ^ B)*(A > B)

2:13位操作员

RESULT = B ^ (A ^ B)*(A > B) ^ (A ^ C)*(A > C) ^ (B ^ C)*(B > C)

3:27位操作员

g = 47|((0x00ffffff & A)>>10)|(A>>14)
r = (g + A/g)/3
r = (r + A/r)>>1
r = (r + A/r)>>1
r = (r + A/r)>>1
RESULT = r - (r*r-1>=A)

5

任务3,39次操作

编辑:更改了最后一行;看评论。

这是Newthon方法的实现。使用所有正平方以及正平方减去1以及100万个随机数(范围为0到2 ^ 32-1)进行测试。看似有趣的起始值是的缩写(1022 + A/1022) / 2,它需要最少的迭代次数(我认为),并且也使RESULTfor为A=0对(1024不是代替1022)。

r = (511 + A/2044)
r = (r + A/r) / 2
r = (r + A/r) / 2
r = (r + A/r) / 2
r = (r + A/r) / 2
r = (r + A/r) / 2
r = (r + A/r) / 2
r = (r + A/r) / 2
r = (r + A/r) / 2
RESULT = r - (r > A/r)

我是否应该将牛顿方法的劣质副本与您的优化并行保存,并在以后发布大量内容?伟大的思想家有相同的想法,将解决方案分为两个两个答案是不好的,但这就是当前的现状,因为您还没有回答#2。
John Dvorak 2013年

@JanDvorak:感谢您的询问。如果您将我的方法略短一些,可以。另外,感谢您对我的信任:-)
恢复莫妮卡(Monica)2013年

确实不错的尝试,但通过4294967295输入4294965360失败。–
aaaaaaaaaaaa

@eBusiness:您得到这些输入的结果是什么?我的测试中得到65535,这还可以。
恢复莫妮卡

我得到65536。也许您不使用规定的整数格式。
aaaaaaaaaaaaaa 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.