Algorithm 仅使用+;1,-1和#xF7;2.

Algorithm 仅使用+;1,-1和#xF7;2.,algorithm,performance,math,time-complexity,Algorithm,Performance,Math,Time Complexity,我遇到了这个问题,在给定任何数量的x的情况下,找到最小操作数,其中一个操作是x-1,x+1,或x/2,这样在所有操作结束时x=1 我理解仅仅通过查看执行操作(基于位操作)的解决方案。然而,结果是类似于O(logn)。这个问题有O(1)解决方案吗?如果没有,是否有更好的解决方案,只运行实际操作?是的,如果您只想知道操作的数量,那么应该有一种方法可以做到这一点,而不需要实际进行所有计算。这将是非常大的数字快得多。诸如此类: public int countOps(String bits) { //

我遇到了这个问题,在给定任何数量的
x
的情况下,找到最小操作数,其中一个操作是
x-1
x+1
,或
x/2
,这样在所有操作结束时
x=1


我理解仅仅通过查看执行操作(基于位操作)的解决方案。然而,结果是类似于O(logn)。这个问题有O(1)解决方案吗?如果没有,是否有更好的解决方案,只运行实际操作?

是的,如果您只想知道操作的数量,那么应该有一种方法可以做到这一点,而不需要实际进行所有计算。这将是非常大的数字快得多。诸如此类:

public int countOps(String bits) { // bit string with only 1 and 0 and first bit is 1
    int count = 0, i = bits.length - 1, b = bits.charAt(i) - '0';
    for (;;) {
        // remove 0s with /2
        while (b == 0) {
            count++;
            b = bits.charAt(--i) - '0';
        }
        // lowest bit b is now 1
        if (i < 1)
             return count;
        if (i == 1) // the 3 edge case
             return count + 1;
        int a = bits.charAt(i - 1) - '0';
        if (a == 0) { // have to do -1
            i -= 2;
            count += 3; // -1, /2, /2
            if (i < 1)
                return count;
            b = bits.charAt(i) - '0';
        } else { // have to do +1 and propagate
            count += 2; // +1, /2 (the /2 at the end of the propagation)
            while (a == 1) {
                count++; // /2
                if (--i == 0)
                    return count;
                a = bits.charAt(i - 1) - '0';
            }
            i--;
        }
    }
}
public int countOps(字符串位){//只有1和0的位字符串,第一位是1
int count=0,i=bits.length-1,b=bits.charAt(i)-'0';
对于(;;){
//使用/2删除0
而(b==0){
计数++;
b=位字符(--i)-“0”;
}
//最低位b现在是1
if(i<1)
返回计数;
if(i==1)//3边情况
返回计数+1;
int a=位字符(i-1)-“0”;
如果(a==0){//必须执行-1
i-=2;
计数+=3;/-1,/2,/2
if(i<1)
返回计数;
b=位字符(i)-“0”;
}否则{//必须执行+1并传播
计数+=2;//+1,/2(传播结束时的/2)
while(a==1){
计数+++;///2
如果(--i==0)
返回计数;
a=位字符(i-1)-“0”;
}
我--;
}
}
}
提出的重复问题的第二部分显示了一种算法,该算法根据输入数字的最低有效位确定最佳操作

然而,结果是类似于O(logn)。这个问题有O(1)解决方案吗?如果没有,是否有一个更好的解决方案,只是在实际操作中运行

为了优化该解决方案,我们可以直接计算每个位模式所需的操作数,然后删除这些位

请注意,我们必须仅在执行加法时更改高位,否则只需要进行位移位

还注意到,当输入为0时,下面的C++代码返回1,与上述答案相反,不考虑角情况。

size_t steps_count(size_t n)
{
    size_t count{};
    while (n > 3) {
        switch ( n % 4 ) {
            case 0b00:
                count += 2;
                n >>= 2;
                break;
            case 0b01:
                count += 3;
                n >>= 2;
                break;
            case 0b10:
                ++count;
                n >>= 1;
                break;
            case 0b11:
                count += 3;
                n >>= 2;
                ++n;
                break;
        }
    }
    switch ( n ) {
        case 1:
            return count;
        case 3:
            return count + 2;
        default:
            return count + 1;
    }
}

这种方法可以推广到一次处理更多位,还可以引入查找表。这是否值得,留给读者来决定。

Big-O不是一切,特别是如果您已经有了O(logn)解决方案

理论推理

当谈到大O时间复杂性时,不断增加的值的渐近行为是相关的。所以,本质上我们不能局限于一些固定长度的整数计算

由于现在没有CPU可以在恒定时间内对无限位整数进行运算(可能永远也不会),所以除了大多数位都不相关的非常特殊的情况外,严重的O(1)本身是不可能的(例如,O(1)决定一个数是否为偶数,只看最低有效位)

所以,O(logn)是你能期望的最好的

如果您限制位长度(因此违反了渐近行为方面!),您可以通过计算一个包含所有解的表(与n无关的常数时间,表示O(1)),然后对该表进行索引(也是O(1)),轻松实现O(1)

通常,在大O分析中,我们将加法等假设为O(1)运算,如果我们的算法的运行时间已经在32位整数的限制范围内爆炸,我们可以这样做,因此可以故意忽略关于更大整数的推理。但是对于O(logn),32位内没有爆炸

实践方面

通常,我们对一些理论结果不感兴趣,但希望得到一个估计,给定一些预期的输入值范围,我们的算法需要多长时间。如果Big-O告诉我们像O(2^n)这样的结果,我们知道如果n增长超过20左右,我们就会遇到问题

但是对于像O(logn)这样的结果,最重要的不是n值越高的渐近增长,而是实际需要的计算步骤数。因此,优化Big-O复杂性不再会带来现实世界中的改进(请参阅上面我的O(1)“解决方案”——没有人会这样做)

在现实世界中

  • 使用一个合理的、可理解的算法,并具有可接受的时间和空间复杂性
  • 根据需求在相关输入范围内测试应用程序的运行时行为
  • 如果发现性能问题,请使用分析工具查找原因
  • 精确地优化您看到的热点,可能通过做一些局部改进,可能通过使用更高级的算法
回到问题

对中提出的算法的改进可能是预计算部分结果表,例如8位数字块

必须有两个表,一个用于“基本”情况,另一个用于前一块的
+1
操作导致进位到此块的情况

表格必须提供两条信息:

  • 此区块所需的步骤数
  • 是否对下一个块进行正进位的标志
然后,您可以从低到高迭代8位块,添加所需的步数,并使用进位标志选择适当的下一个表

这仍然是O(logn),但可能比目前提出的算法更快。

@Yunnosch-How