C# 递增无限二进制计数器

C# 递增无限二进制计数器,c#,binary,bit-manipulation,C#,Binary,Bit Manipulation,摘自采访: 如何在O(1)时间复杂度下实现一个增量为的无限二进制计数器 我想计算最右边的0的第一个和第二个位置,但我不确定如何实现 “无限计数器”意味着您可以无限次递增(大于MAX_INT)。对于二进制计数器… 如果你想让计数器保持在一个“正常”的位模式,你基本上不能——至少不能总是O(1),而不是摊销O(1) 如果它是一个无限计数器,它可以有任意多个位。这意味着您可以有N个位,所有这些位都是1。增加该计数器意味着将所有这些位设置为0,这可以合理地假设为O(N)操作 我们在“正常”计算中考虑增量

摘自采访:

如何在O(1)时间复杂度下实现一个增量为的无限二进制计数器

我想计算最右边的
0
的第一个和第二个位置,但我不确定如何实现


“无限计数器”意味着您可以无限次递增(大于MAX_INT)。

对于二进制计数器…

如果你想让计数器保持在一个“正常”的位模式,你基本上不能——至少不能总是O(1),而不是摊销O(1)

如果它是一个无限计数器,它可以有任意多个位。这意味着您可以有N个位,所有这些位都是1。增加该计数器意味着将所有这些位设置为0,这可以合理地假设为O(N)操作

我们在“正常”计算中考虑增量为O(1)的唯一原因是通常处理固定大小的类型,在这里我们可以说(例如)最多32位需要改变-这是一个常数,因此可以想象为O(1)操作。 只需一个计数器…

另一方面,如果你只想在O(1)时间内递增,你有无限的内存,你不在乎恢复值需要多长时间,你可以这样做,只要有效地使用一个长度为计数器大小的链表

例如,在C#中:

即使这样,也假设引用赋值为O(1),这对于无限多的对象来说可能会变得很棘手…

  • 使用某种具有加倍策略的阵列存储。这意味着分配按O(1)摊销
    链表也应该起作用
  • 使用简单的教科书添加。进位越高,进位越少。运输的平均成本为1+0.5+0.25+…=2哪个O(1)
因此,一个直接的实现已经摊销了O(1)性能。唯一的问题是您需要可变存储

当查看一个数
n
的单个增量运算时,平均时间是O(1),但最坏的情况是O(log(n))。内存使用率为O(log(n))

var counter=新列表{false};
void公司()
{
while(计数器[i])
{
计数器[i]=假;
i++;
}
if(i==计数器长度)
counter.Add(true);
其他的
计数器[i]=真;
}
如果问题仅要求O(1)计数器的增量,而没有任何其他限制,则您的计数器可以实现为数字的链接列表,项目的总和就是计数器的值

如果之前的值大于(Max-1),则递增相当于将1添加到最后一项,或添加新项=1

由于您总是在列表中最多选中2项,因此递增将是O(1)

只是不要尝试用你闪亮的新计数器做其他算术运算:D

我的尝试:

我们在连续的
1
0
上保持聚合

意思是111000111是

我可以用以下DS来表示这一点:

节点列表{数字:bool,计数器:long}

1) 如果第一批为1。它将变成0的大部分,并将下一个0变成1

现在,我们检查是否可以聚合大量的1

2) 如果第一个批量为0,则将第一个数字设为1。看看我们能不能把1加起来

示例A:

意思是111000111是

阅读:三位
1
数字,三位
0
数字,三位
1
数字,一位
0
数字

增量()

例B:

增量()

聚合:

始终存在常量更改数(最多0位)


大部分
1
s只是切换布尔成员。哪个是常数

什么是无限二进制计数器?嗅嗅嗅嗅嗅…我检测到作业…什么?增量总是有O(1),问题是什么?@stenportov:真的吗?对于任意长度的整数?我不这么认为。@JonSkeet啊,我明白了。。。他说的是一个很大的柜台。谢谢你的澄清。然后,如果目的是计数,使其成为一个1s的链表,计数器的值是1s的数目,则递增/递减变为O(1),只是不要尝试其他操作:d它不是更像O(logn)吗?毕竟,数字越大,与“价值”成比例的工作就越少您可以保持最右边的
1
和最右边的
0
的位置,并执行位操作,而不是遍历bits@voithos:这取决于您的N是数字的值还是其大小(以位为单位)。@EladBenda:“执行位操作”如何允许您在O(1)中翻转任意数量的位?跟踪什么并不重要——我想,您仍然需要能够更改任意数量的位。@EladBenda:当位宽度小于或等于指令集大小时,位操作仅为O(1)。“大容量”有多大?如果时间越长,它就不是一个常数时间算法。(老实说,我不确定我是否真的理解它的作用。如果你能显示0-10的位模式就好了。)@JonSkeet如果你不确定这是什么作用,你怎么能预测
如果它随时间变大,它不是一个常数时间算法?我在回答中加了一个例子。你能解释一下为什么大体积会成为一个问题吗?因为它只是由答案的前一个版本表示的,所以它有一个循环,看起来它不会以O(1)结束。我会进一步考虑——如果你能把它写成实际的代码,你可能会更容易想到它最终会在哪里结束(或者简单地证明它不会)。我的怀疑是,聚合部分要求您查看潜在的非恒定数量的块。@JonSkeet聚合在每个步骤中最多涉及3个节点。最右边的0
public DodgySolution
{
    public static DodgySolution Zero = new DodgySolution(null);

    private DodgySolution tail;

    private DodgySolution(DodgySolution tail)
    {
        this.tail = tail;
    }

    // This bit is O(1)
    public DodgySolution Increment()
    {
        return new DodgySolution(this);
    }

    // This bit isn't...
    public BigInteger ToBigInteger()
    {
        return tail == null ? BigInteger.Zero
                            : BigInteger.One + tail.ToBigInteger();
    }
}
var counter=new List<bool>{false};

void Inc()
{
  while(counter[i])
  {
      counter[i]=false;
      i++;
  }
  if(i==counter.Length)
    counter.Add(true);
  else
    counter[i]=true;
}