这个数字的精确度是-2 :(非常)硬模式


26

这是最近挑战的一个版本,这个数字是2的整数次幂吗?具有一系列不同的标准,这些标准旨在突出问题的有趣性质并使挑战更加艰巨。我在这里考虑了一下

托比在链接的问题中很好地说明了这一挑战:

有很多聪明的方法可以确定整数是否为2的精确幂。这不再是一个有趣的问题,因此让我们确定给定的整数是否为-2的精确幂。例如:

-2 => yes: (-2)¹
-1 => no
0 => no
1 => yes: (-2)⁰
2 => no
3 => no
4 => yes: (-2)²

规则:

  • 整数是64位带符号的二进制补码。这是您可以使用的唯一数据类型。
  • 您只能使用以下操作。这些都算作一项操作。
    • n << kn >> k:左/右移位n通过k比特。符号位右移扩展。
    • n >>> k:右移,但不扩展符号位。0移入。
    • a & ba | ba ^ b:按位AND,OR,XOR。
    • a + ba - ba * b:加,减,乘。
    • ~b:按位反转。
    • -b:补码取反。
    • a / ba % b:除(整数商,四舍五入为0)并取模。
      • 负数的模使用C99中指定的规则:(a/b) * b + a%b相等a。所以5 % -32-5 % 3-2
      • 5 / 315 % 32,因为1 * 3 + 2 = 5。
      • -5 / 3-1-5 % 3-2,为-1 * 3 + -2 = -5。
      • 5 / -3-15 % -32,如-1 * -3 + 2 = 5。
      • -5 / -31-5 % -3-2,为1 * -3 + -2 = -5。
      • 请注意,Python的下位//除法运算符不满足这里的除法的“朝0取整”属性,Python的%运算符也不满足要求。
    • 分配不算作一项操作。与C中一样,赋值的结果是赋值后的左侧值:a = (b = a + 5)设置ba + 5,然后设置ab,并计为一次操作。
    • 复合分配可以用作a += b均值a = a + b并算作一项操作。
  • 您可以使用整数常量,它们不算作任何东西。
  • 指定操作顺序的括号是可以接受的。
  • 您可以声明函数。函数声明可以采用任何方便的样式,但是请注意,64位整数是唯一有效的数据类型。函数声明不算作操作,但函数调用算作一个。另外,要明确一点:函数可以包含多个return语句,并且可以return从任何点开始执行。在return本身并不能算作一个操作。
  • 您可以免费声明变量。
  • 您可以使用while循环,但不能使用ifforwhile条件中使用的运算符计入您的分数。while只要条件的计算结果为非零值,循环就会执行(在具有此概念的语言中,“真” 0为无效结果)。由于早期的回报是允许的,你被允许使用break,以及
  • 允许上溢/下溢,并且不会进行值钳位。可以将其视为操作实际上已正确发生,然后被截断为64位。

得分/获胜标准:

如果输入是-2的幂,则您的代码所产生的值必须为非零,否则为零。

这是。您的分数是代码中定义的操作总数(如上定义),而不是运行时执行的操作总数。如下代码:

function example (a, b) {
    return a + ~b;
}

function ispowerofnegtwo (input) {
    y = example(input, 9);
    y = example(y, 42);
    y = example(y, 98);
    return y;
}

包含5个操作:函数中的两个和三个函数调用。

无论如何显示结果,使用语言中方便的方式,无论最终将结果存储在变量中,从函数返回结果还是其他方式都无所谓。

获胜者的职位应证明是正确的(如有必要,可提供临时证明或正式证明),并且得分最低,如上所述。

奖励非常困难模式挑战!

为了有机会赢得任何绝对的胜利,除了可能在聚会上打动人们,请在不使用while循环的情况下提交答案!如果提交了足够多的这些,我什至可以考虑将获胜小组分为两类(有循环和无循环)。


注意:如果您想提供仅支持32位整数的语言的解决方案,可以这样做,只要您有充分的理由证明它对于64位整数仍然是正确的,就可以解释。

另外:如果某些特定于语言的功能没有逃避规则,但对于强迫您的语言按照上述规则行事,则是免费的。例如(做作),我将允许在循环中使用自由不等于0的比较,while将其应用于整个条件时,作为具有“真实” 0的语言的解决方法。不允许尝试利用这些类型的事物进行明确的尝试 -例如,上述规则集中不存在“真实的” 0或“未定义的”值的概念,因此可能不依赖它们。


评论不作进一步讨论;此对话已转移至聊天
丹尼斯

@hvd如果您阅读以下内容:您应该完全取消删除答案!假设它是正确的,即使没有 m ^= s它仍然令人印象深刻,我认为完全可以进行替代以进一步改进它。
Jason C

它是如何让油然而生,让whilebreak,但不是ifif (x) { ... }等价于while (x) { ... break; }
R.,

@R ..没有100%的意义(break而早期的回报是令人遗憾的部分),这是一个漫长的故事,也是在未来挑战规则中吸取的教训。总是有“奖金”版本!:)
Jason C

1
为什么iffor被禁止?int x=condition; while (x) { ... x=0; }是免费的,只需更多代码。与c样式相同for
Qwertiy

Answers:


35

C ++,15个操作

我不知道为什么while循环被允许破坏了整个挑战。这是一个没有任何答案的答案:

int64_t is_negpow2(int64_t n) {
    int64_t neg = uint64_t(n) >> 63; // n >>> 63
    n = (n ^ -neg) + neg; // if (n < 0) n = -n;
    int64_t evenbits = n & int64_t(0xaaaaaaaaaaaaaaaaull >> neg);
    int64_t n1 = n - 1;
    int64_t pot = n & n1;
    int64_t r = pot | (n1 >> 63) | evenbits;
    return ~((r | -r) >> 63); // !r
}

为什么while循环会破坏整个挑战
Xcoder先生17年

10
@ Mr.Xcoder因为挑战在于如何通过简单的按位操作来做到这while一点,并且在各个方面都与之相反。
orlp

我的意思是,除非您将while循环乘以操作数乘以在循环中为静态n或类似内容执行的次数。
Magic Octopus

我在这里对此发表了评论。
Jason C

@JasonC那是因为我应该使用无符号位的右移。我编辑了代码(使用它uint64_t是因为这是获得不带符号扩展名的正确移位的唯一方法。)
orlp

25

Python 2、3操作

def f(n):
 while n>>1:
  while n&1:return 0
  n=n/-2
 return n

在线尝试!

该操作是>>&/

想法是反复除以-2。-2次方的力量下降到1 :-8 -> 4 -> -2 -> 1。如果我们点击1,请接受。如果我们先打一个奇数1,再拒绝。我们还需要拒绝0,这永远消失了。

while n>>1:循环,直至n为0或1当循环中断,n则返回本身,并且1是一个Truthy输出和0一个Falsey一个。在循环内部,我们拒绝重复应用,n -> n/-2并拒绝任何奇数n

由于/只会在偶数值上使用,因此其舍入行为永远不会起作用。因此,Python与规范的取舍没有关系。


真好 算法中巧妙的逻辑以及将条件组合成位运算的出色工作。此外,可以确认,实施工程C.
杰森ç

为什么要while n&1代替if n&1
Mark Ransom

2
@MarkRansom挑战不允许if
xnor

啊哈,错过了。替换非常聪明。
Mark Ransom

1
@EvSunWoodard评分是代码中操作符的数量,而不是执行期间对其进行调用的次数,具体取决于输入:“这是原子代码高尔夫。您的得分是代码中存在的操作总数。”
xnor

11

锈, 14 12个操作(无循环)

需要优化(-O)或-C overflow-checks=no启用溢出减法而不是惊慌。

fn is_power_of_negative_2(input: i64) -> i64 {
    let sign = input >> 63;
    // 1 op
    let abs_input = (input ^ sign) - sign;
    // 2 ops
    let bad_power_of_two = sign ^ -0x5555_5555_5555_5556; // == 0xaaaa_aaaa_aaaa_aaaa
    // 1 op
    let is_not_power_of_n2 = abs_input & ((abs_input - 1) | bad_power_of_two);
    // 3 ops 
    let is_not_power_of_n2 = (is_not_power_of_n2 | -is_not_power_of_n2) >> 63;
    // 3 ops 
    input & !is_not_power_of_n2
    // 2 ops
}

(澄清:!x按位NOT这里,不逻辑NOT)

测试用例:

#[test]
fn test_is_power_of_negative_2() {
    let mut value = 1;
    for _ in 0 .. 64 {
        assert_ne!(0, is_power_of_negative_2(value), "wrong: {} should return nonzero", value);
        value *= -2;
    }
}

#[test]
fn test_not_power_of_negative_2() {
    for i in &[0, -1, 2, 3, -3, -4, 5, -5, 6, -6, 7, -7, 8, 1<<61, -1<<62, 2554790084739629493, -4676986601000636537] {
        assert_eq!(0, is_power_of_negative_2(*i), "wrong: {} should return zero", i);
    }
}

在线尝试!


这个想法是检查| x | 是2的幂((y & (y - 1)) == 0照常使用)。如果x是2的幂,那么我们进一步的检查(1)时x >= 0,还应当为2的偶数次方,或(2)时x < 0,它应该是2的奇次幂,我们通过检查这个&-ing的“ bad_power_of_two“屏蔽0x…aaaa时x >= 0(仅当它是偶数幂时才产生0),或0x ... 5555时x < 0


我偷了你的~((r | -r) >> 63)把戏,以完成我的答案。
orlp

6

Haskell,进行2 3次操作

import Data.Bits (.&.)

f 0 = False
f 1 = True
f n | n .&. 1 == 0 = f (n `div` -2)
f n | otherwise    = False

定义一个递归函数f(n)。使用的操作是函数调用(f),除法(div)以及按位与(.&.)。

由于Haskell没有循环语句,因此不包含循环:-)


4
为什么我不惊讶有名叫“机会主义者”的人提供了不使用循环的Haskell解决方案?=)
科特·阿蒙-恢复莫妮卡

1
犹豫的f 0f 1f n ...在这里,因为他们基本上if的伪装,但还是那句话,我也允许while+ break和早期returnS,所以它似乎是公平的。尽管它似乎确实利用了我的规则集不经意间可以进行解释的优势,但这是一个不错的解决方案。
Jason C

3
特别是|s尤其是空中广播。也就是说,这确实以一种不太值得商bat的方式违反了一条特定规则:==不允许进行比较。但是请注意,如果我对这段代码的解释是正确的,则此处使用布尔值确实可以接受,因为用任意整数值代替它们似乎不会改变结果,它们更像是一种最终的表示形式。
Jason C

@JasonC我只使用==,因为没有其他办法从铸造IntBool或“Truthy”在Haskell。您是否if
机会主义者

18
使用模式匹配,您可以使用0操作对所有64位整数的结果进行硬编码。
xnor

5

Python 3、10 或11 9操作

def g(x):
 while x:
  while 1 - (1 + ~int(x - -2 * int(float(x) / -2))) & 1: x /= -2
  break
 while int(1-x):
     return 0
 return 5  # or any other value

返回5的幂-20否则


评论不作进一步讨论;此对话已转移至聊天
丹尼斯

5

C,5次操作

long long f(long long x){
    x=x ^ ((x & 0xaaaaaaaaaaaaaaaa) * 6);
    while(x){
        while(x&(x-1))
            return 0;
        return 1;
    }
    return 0;
}

C,10次操作,无循环

long long f(long long x){
    x = x ^ ((x & 0xaaaaaaaaaaaaaaaa) * 6);
    long long t = x & (x-1);
    return (((t-1) & ~t) >> 63) * x;
}

C,1次操作

long long f(long long x){
    long long a0=1, a1=-2, a2=4, a3=-8, a4=16, a5=-32, a6=64, a7=-128, a8=256, a9=-512, a10=1024, a11=-2048, a12=4096, a13=-8192, a14=16384, a15=-32768, a16=65536, a17=-131072, a18=262144, a19=-524288, a20=1048576, a21=-2097152, a22=4194304, a23=-8388608, a24=16777216, a25=-33554432, a26=67108864, a27=-134217728, a28=268435456, a29=-536870912, a30=1073741824, a31=-2147483648, a32=4294967296, a33=-8589934592, a34=17179869184, a35=-34359738368, a36=68719476736, a37=-137438953472, a38=274877906944, a39=-549755813888, a40=1099511627776, a41=-2199023255552, a42=4398046511104, a43=-8796093022208, a44=17592186044416, a45=-35184372088832, a46=70368744177664, a47=-140737488355328, a48=281474976710656, a49=-562949953421312, a50=1125899906842624, a51=-2251799813685248, a52=4503599627370496, a53=-9007199254740992, a54=18014398509481984, a55=-36028797018963968, a56=72057594037927936, a57=-144115188075855872, a58=288230376151711744, a59=-576460752303423488, a60=1152921504606846976, a61=-2305843009213693952, a62=4611686018427387904, a63=-9223372036854775807-1, a64=0;
    while(a0){
        long long t = x ^ a0;
        long long f = 1;
        while(t){
            f = 0;
            t = 0;
        }
        while(f)
            return 1;
        a0=a1; a1=a2; a2=a3; a3=a4; a4=a5; a5=a6; a6=a7; a7=a8; a8=a9; a9=a10; a10=a11; a11=a12; a12=a13; a13=a14; a14=a15; a15=a16; a16=a17; a17=a18; a18=a19; a19=a20; a20=a21; a21=a22; a22=a23; a23=a24; a24=a25; a25=a26; a26=a27; a27=a28; a28=a29; a29=a30; a30=a31; a31=a32; a32=a33; a33=a34; a34=a35; a35=a36; a36=a37; a37=a38; a38=a39; a39=a40; a40=a41; a41=a42; a42=a43; a43=a44; a44=a45; a45=a46; a46=a47; a47=a48; a48=a49; a49=a50; a50=a51; a51=a52; a52=a53; a53=a54; a54=a55; a55=a56; a56=a57; a57=a58; a58=a59; a59=a60; a60=a61; a61=a62; a62=a63; a63=a64;
    }
    return 0;
}

2
哦,老兄,那最后的只是邪恶。真好
Jason C

4

组装,1次操作

.data

    .space 1         , 1 # (-2)^31
    .space 1610612735, 0
    .space 1         , 1 # (-2)^29
    .space 402653183 , 0
    .space 1         , 1 # (-2)^27
    .space 100663295 , 0
    .space 1         , 1 # (-2)^25
    .space 25165823  , 0
    .space 1         , 1 # (-2)^23
    .space 6291455   , 0
    .space 1         , 1 # (-2)^21
    .space 1572863   , 0
    .space 1         , 1 # (-2)^19
    .space 393215    , 0
    .space 1         , 1 # (-2)^17
    .space 98303     , 0
    .space 1         , 1 # (-2)^15
    .space 24575     , 0
    .space 1         , 1 # (-2)^13
    .space 6143      , 0
    .space 1         , 1 # (-2)^11
    .space 1535      , 0
    .space 1         , 1 # (-2)^9
    .space 383       , 0
    .space 1         , 1 # (-2)^7
    .space 95        , 0
    .space 1         , 1 # (-2)^5 = -32
    .space 23        , 0
    .space 1         , 1 # (-2)^3 = -8
    .space 5         , 0
    .space 1         , 1 # (-2)^1 = -2
    .space 1         , 0
dataZero:
    .space 1         , 0
    .space 1         , 1 # (-2)^0 = 1
    .space 2         , 0
    .space 1         , 1 # (-2)^2 = 4
    .space 11        , 0
    .space 1         , 1 # (-2)^4 = 16
    .space 47        , 0
    .space 1         , 1 # (-2)^6 = 64
    .space 191       , 0
    .space 1         , 1 # (-2)^8
    .space 767       , 0
    .space 1         , 1 # (-2)^10
    .space 3071      , 0
    .space 1         , 1 # (-2)^12
    .space 12287     , 0
    .space 1         , 1 # (-2)^14
    .space 49151     , 0
    .space 1         , 1 # (-2)^16
    .space 196607    , 0
    .space 1         , 1 # (-2)^18
    .space 786431    , 0
    .space 1         , 1 # (-2)^20
    .space 3145727   , 0
    .space 1         , 1 # (-2)^22
    .space 12582911  , 0
    .space 1         , 1 # (-2)^24
    .space 50331647  , 0
    .space 1         , 1 # (-2)^26
    .space 201326591 , 0
    .space 1         , 1 # (-2)^28
    .space 805306367 , 0
    .space 1         , 1 # (-2)^30
    .space 3221225471, 0
    .space 1         , 1 # (-2)^32

.globl isPowNeg2
isPowNeg2:
    movl dataZero(%edi), %eax
    ret

使用巨大的查找表来查找数字是否为2的幂。您可以将其扩展为64位,但是作为练习,读者可以自己找一台计算机来存储那么多的数据:-P


1
索引表不是允许的操作之一。
R.,

1
同样,这显然不能扩展到64位。:-)
R..17年

事实上,索引表中并没有打算在目前的规则是允许的。我用标量的目的指定了“可以声明变量”和“可以指定整数文字”,从语义上讲,这是一个数组(而且从学说上讲,我不允许数组类型,也不允许任何类型的索引作为其中之一)。操作,尽管您可以在汇编程序的上下文中将其称为“加法”),但作为机会主义者,您就是... :)
Jason C

3

C,31次作业

现场演示

我的想法很简单,如果它是2的幂,那么如果它的对数是偶数,则它必须是正数,否则它的对数必须是奇数。

int isPositive(int x) // 6
{
    return ((~x & (~x + 1)) >> 31) & 1;
}

int isPowerOfTwo(int x) // 5
{
    return isPositive(x) & ~(x & (x-1));
}

int log2(int x) // 3
{
    int i = (-1);

    while(isPositive(x))
    {
        i  += 1;
        x >>= 1;
    }

    return i;
}

int isPowerOfNegativeTwo(int x) // 17
{
    return (  isPositive(x) &  isPowerOfTwo(x) & ~(log2(x) % 2) )
         | ( ~isPositive(x) & isPowerOfTwo(-x) & (log2(-x) % 2) );
}

1
你实际上比你想象的要好。函数调用仅计为1,不计为函数中的运算符数量。因此,如果我计算正确(仔细检查),您会发现isPositive为6 + isPowerOfTwo为5 + log2为3 + isPowerOfNegativeTwo为17 + isPowerOfNegativeTwo为31 =
Jason C

1

C,7次操作

int64_t is_power_of_neg2(int64_t n)
{
    int64_t x = n&-n;
    while (x^n) {
        while (x^-n)
            return 0;
        return x & 0xaaaaaaaaaaaaaaaa;
    }
    return x & 0x5555555555555555;
}

要么:

C,13个无条件循环操作

int64_t is_power_of_neg2(int64_t n)
{
    int64_t s = ~(n>>63);
    int64_t a = ((n/2)^s)-s;
    int64_t x = n&-(uint64_t)n; // Cast to define - on INT64_MIN.
    return ~(a/x >> 63) & x & (0xaaaaaaaaaaaaaaaa^s);
}

说明:

  • n&-n产生的最低设置位n
  • a是的求反绝对值n/2,必须为负,因为/2可以避免求反的溢出。
  • a/x只有在a精确的2的幂时才为零;否则,至少要设置另外一位,并且x该最低位高于最低位,从而产生负数结果。
  • ~(a/x >> 63)然后产生一个全掩码(如果n或是2 -n的幂)的位掩码,否则为全零。
  • ^s用于面罩以检查的符号n以查看是否为的幂-2

1

PHP,3个操作

三元且if不允许;所以让我们滥用while

function f($n)
{
    while ($n>>1)               # 1. ">>1"
    {
        while ($n&1)            # 2. "&1"
            return 0;
        return f($n/-2|0);      # 3. "/-2" ("|0" to turn it into integer division)
    }
    return $n;
}
  1. $n>>1:如果数字是0或1,则返回数字
  2. $n&1:如果数字为奇数,则返回0
  3. 其他测试$n/-2(+ cast到int)

0

JavaScript ES6,7个操作

x=>{
  while(x&1^1&x/x){
    x/=-2;x=x|0
  }
  while(x&0xfffffffe)x-=x
  return x
}

在线尝试!

说明

while(x&1^1&x/x)

当x!= 0和x%2 == 0时4个操作
x / x等于1,只要x不为0(0/0给出NaN的评估结果为false)
&按位并且
x&1 ^ 1等于1如果x是偶数(x和1)xor 1

x/=-2;x=x|0

这是问题1 op定义的除法形式

while(x&0xfffffffe)  

当x!= 1和x!= 0时 1 op
当x == 0或x == 1时需要退出的条件,因为这两个是返回值,进入无限循环将不会产生效果。从理论上讲,可以通过增加十六进制数将其扩展为更大的值。目前适用于±2 ^ 32-1

x-=x

将x设置为0 1
ops我本可以将return 0用于0 ops,但我觉得被另一条语句破坏的任何while循环都感觉像在作弊。

return x

返回x(如果幂为-2,则返回1,否则为0)

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.