标签: computer-science

什么是停顿问题?

每当人们询问与编程有关的暂停问题时,人们会回答:"如果你只是添加一个循环,那么你就有了停止程序,因此无法自动完成任务 "

说得通.如果你的程序有一个无限循环,那么当你的程序运行时,你无法知道程序是否仍在处理输入,或者它是否只是无限循环.

但其中一些似乎反直觉.如果我正在编写一个暂停问题求解器,它将源代码作为输入,那该怎么办?rascher@localhost$ ./haltingSolver source.c

如果我的代码(source.c)看起来像这样:

for (;;) {  /* infinite loop */  }
Run Code Online (Sandbox Code Playgroud)

看起来我的程序看起来很容易."查看循环,看看条件.如果条件只是基于文字而没有变量,那么你总是知道循环的结果.如果有变量(例如while(x <10)),看看是否这些变量是永远修改的.如果没有,那么你总是知道循环的结果."

当然,这些检查不会是微不足道的(计算指针算术等),但这似乎不可能.例如:

int x = 0
while (x < 10) {}
Run Code Online (Sandbox Code Playgroud)

可以检测到.以及 - 尽管不是轻微的:

int x = 0
while (x < 10)
{
   x++;
   if (x == 10)
   {
      x = 0
   }
}
Run Code Online (Sandbox Code Playgroud)

那么用户输入呢?这就是踢球者,这就是让程序无法预测的原因.

int x = 0;
while (x < 10) 
{
   scanf("%d", &x); /* ignoring infinite scanf loop oddities */
}
Run Code Online (Sandbox Code Playgroud)

现在我的程序可以说:"如果用户输入10或更高,程序将停止.在所有其他输入,它将再次循环."

这意味着,即使有数百个输入,也应该能够列出程序停止的条件.实际上,当我编写程序时,我总是确保有人能够终止它!我并不是说由此产生的条件清单是微不足道的,但对我来说似乎并不可能.您可以从用户那里获取输入,使用它们来计算指针索引等等 - 但这只会增加条件的数量以确保程序终止,不会使它们无法枚举. …

computer-science halting-problem

52
推荐指数
8
解决办法
2万
查看次数

二进制堆的有效实现

我正在寻找有关如何有效实现二进制堆的信息.我觉得应该有一篇关于有效实现堆的好文章,但我还没找到.事实上,我已经无法找到任何有关超出基础知识的有效实现的资源,例如将堆存储在数组中.我正在寻找制作快速二进制堆的技术,超出我在下面描述的那些.

我已经编写了一个比Microsoft Visual C++和GCC的std :: priority_queue或使用std :: make_heap,std :: push_heap和std :: pop_heap更快的C++实现.以下是我在实现中已经涵盖的技术.我自己只想出了最后两个,尽管我怀疑这些是新想法:

(编辑:添加了关于内存优化的部分)

  • 从1开始索引
    查看维基百科的二进制堆实现说明.如果堆的根位于索引0处,则索引n处的节点的父,左子和右子的公式分别为(n-1)/ 2,2n + 1和2n + 2.如果使用基于1的数组,则公式变为更简单的n/2,2n和2n + 1.因此,使用基于1的数组时,父项和左子项更有效.如果p指向基于0的数组并且q = p-1,那么我们可以将p [0]作为q [1]来访问,因此使用基于1的数组没有开销.

  • 在使用leaf替换之前,将pop/remove移动元素设置到堆的底部
    经常通过将最顶部元素替换为最左边的底部叶子然后将其向下移动直到堆属性恢复来描述.这需要我们经历的每个级别的2次比较,并且由于我们将叶子移动到堆的顶部,因此我们可能会在堆中走得很远.所以我们应该期望比较少于2 log n.

    相反,我们可以在顶部元素所在的堆中留一个洞.然后我们通过迭代地移动较大的孩子来将那个洞向下移动.这需要我们经过的每个级别只有1个比较.通过这种方式,洞将成为一片叶子.此时,我们可以将最右下方的叶子移动到孔的位置,并将该值向上移动,直到堆属性恢复为止.由于我们移动的值是一片叶子,我们不希望它在树上移动很远.所以我们应该期待比log n更多的比较,这比以前更好.

  • 支持replace-top
    假设您要删除max元素并插入新元素.然后,您可以执行上述任何一个删除/弹出实现,但不是移动最右侧的底部叶子,而是使用您希望插入/推送的新值.(当大多数操作属于这种类型时,我发现锦标赛树比堆更好,但是堆更好一点.)

  • 使sizeof(T)的幂为2
    父,子和子子公式适用于索引,并且不能使它们直接在指针值上工作.所以我们将使用索引,这意味着从索引i中查找数组p中的值p [i].如果p是T*且i是整数,那么

    &(p[i]) == static_cast<char*>(p) + sizeof(T) * i
    
    Run Code Online (Sandbox Code Playgroud)

    并且编译器必须执行此计算以获得p [i].sizeof(T)是编译时常量,如果sizeof(T)是2的幂,则可以更有效地进行乘法.通过添加8个填充字节以将sizeof(T)从24增加到32,我的实现变得更快.降低缓存效率可能意味着这对于足够大的数据集来说并不是一个胜利.

  • 预乘法索引
    我的数据集性能提升了23%.除了查找父级,左子级和右子级之外,我们对索引执行的唯一操作是在数组中查找索引.因此,如果我们跟踪j = sizeof(T)*i而不是索引i,那么我们可以进行查找p [i]而不需要在评估p [i]时隐含的乘法,因为

    &(p[i]) == static_cast<char*>(p) + sizeof(T) * i == static_cast<char*>(p) + j
    
    Run Code Online (Sandbox Code Playgroud)

    然后,j值的左子和右子公式分别变为2*j和2*j + sizeof(T).父公式有点棘手,除了将j值转换为i值之外,我还没有找到方法做到这一点,如下所示:

    parentOnJ(j) = parent(j/sizeof(T))*sizeof(T) == (j/(2*sizeof(T))*sizeof(T)
    
    Run Code Online (Sandbox Code Playgroud)

    如果sizeof(T)是2的幂,那么这将编译为2个移位.这比使用索引i的普通父亲多1次操作.然而,我们然后在查找上保存1个操作.因此,净效应是找到父母用这种方式花费相同的时间,而左子和右子的查找变得更快. …

  • c++ performance computer-science priority-queue data-structures

    50
    推荐指数
    1
    解决办法
    9996
    查看次数

    证明随机生成的数字是均匀分布的

    我在接受采访时被问到这个问题.

    给定一个随机数生成器生成[0,N)之间的数字,如何证明这个数是均匀分布的.

    我不知道如何处理这个问题,有什么建议吗?

    algorithm computer-science

    50
    推荐指数
    4
    解决办法
    5903
    查看次数

    O(log N)== O(1) - 为什么不呢?

    每当我考虑算法/数据结构时,我倾向于用常数替换log(N)部分.哦,我知道log(N)有所不同 - 但它在现实世界的应用程序中是否重要?

    所有实际用途的log(无穷大)<100.

    我真的很好奇现实世界的例子,这是不成立的.

    澄清:

    • 我理解O(f(N))
    • 我对现实世界的例子感到好奇,其中渐近行为比实际表现的常数更重要.
    • 如果log(N)可以用常量替换,它仍然可以用O(N log N)中的常量替换.

    这个问题是为了(a)娱乐和(b)收集使用的论据,如果我(再次)进行关于设计性能的争议.

    algorithm performance big-o computer-science

    47
    推荐指数
    8
    解决办法
    1万
    查看次数

    理解神经网络反向传播

    更新:更好地解决问题.

    我试图以XOR神经网络为例来理解反向传播算法.对于这种情况,有2个输入神经元+ 1个偏置,隐藏层中的2个神经元+ 1个偏置,以及1个输出神经元.

     A   B  A XOR B
     1    1   -1
     1   -1    1
    -1    1    1
    -1   -1   -1
    
    Run Code Online (Sandbox Code Playgroud)

    XOR神经网络示例

    我正在使用随机反向传播.

    在阅读了一点之后我发现输出单元的错误传播到隐藏层...最初这是令人困惑的,因为当你到达神经网络的输入层时,每个神经元都会得到一个错误调整来自隐藏层中的两个神经元.特别是,首先很难掌握错误的分配方式.

    步骤1计算每个输入实例的输出.
    步骤2计算输出神经元(在我们的例子中只有一个)和目标值(s)之间的误差:
    步骤2 http://pandamatak.com/people/anand/771/html/img342.gif
    步骤3我们使用步骤2中的错误计算每个隐藏单元的错误h:
    步骤3 http://pandamatak.com/people/anand/771/html/img343.gif

    "权重kh"是隐藏单元h和输出单元k之间的权重,这是令人困惑的,因为输入单元没有与输出单元相关联的直接权重.在盯着公式几个小时后,我开始思考求和意味着什么,并且我开始得出结论,连接到隐藏层神经元的每个输入神经元的权重乘以输出误差并总结.这是一个合乎逻辑的结论,但公式似乎有点令人困惑,因为它清楚地说明了'权重kh'(在输出层k和隐藏层h之间).

    我在这里正确理解了一切吗?任何人都可以证实吗?

    什么是输入层的O(h)?我的理解是每个输入节点有两个输出:一个进入隐藏层的第一个节点,另一个进入第二个节点隐藏层.应该将两个输出中的哪一个插入O(h)*(1 - O(h))公式的一部分?
    第3步http://pandamatak.com/people/anand/771/html/img343.gif

    computer-science machine-learning backpropagation neural-network

    47
    推荐指数
    2
    解决办法
    1万
    查看次数

    什么是图灵机?

    什么是图灵机,人们为什么一直提到它?我的IBM PC就是我完成计算所需要的!为什么有人关心这些机器?

    theory computer-science computability turing-machines

    46
    推荐指数
    5
    解决办法
    3万
    查看次数

    评估语言"图灵完整性"的实用指南是什么?

    我读过"什么是图灵完整"和维基百科页面,但我对正式证据不太感兴趣,而不是图灵完全的实际意义.

    我实际上要确定的是,我刚刚设计的玩具语言是否可以用作通用语言.我知道我可以证明我是否可以用它来编写图灵机.但是,在我确信成功之前,我不想进行这项运动.

    有没有最低限度的功能,没有图灵完整性是不可能的?是否有一系列功能几乎可以保证完整性?

    (我的猜测是条件分支和可读/可写的内存存储将使我的大部分方式)


    编辑:

    我想我已经说过"图灵完成"了.我试图以合理的信心猜测具有特定功能集的新发明的语言(或者具有特定指令集的VM)将能够计算任何值得计算的东西.我知道证明你可以用它建立图灵机是一种方式,但不是唯一的方法.

    我所期待的是一套类似的准则:"如果可以做X,Y和Z,它可以或许做任何事情".

    computer-science language-design turing-complete

    46
    推荐指数
    6
    解决办法
    9519
    查看次数

    什么是'表达问题'?

    我对这是什么有一个粗略的想法,但如果有人对"表达问题"的解释他们认为简洁直观我很乐意听到它.

    computer-science programming-languages functional-programming

    46
    推荐指数
    2
    解决办法
    5709
    查看次数

    "闭合"和"块"之间究竟有什么区别?

    我发现很多人使用闭包互换的词.这些人中的大多数都无法解释他们在谈论什么.

    一些Java程序员(甚至来自真正昂贵的咨询公司)将匿名内部类称为"块"和"闭包" - 但我知道这不是真的.(你不能从定义它们的方法范围中传递可变变量......)

    我在找:

    • 块的精确计算机科学定义
    • 关闭的精确计算机科学定义
    • 澄清两者之间的区别.

    我真的很想看到关于这些的链接,文章或书籍参考.

    theory computer-science programming-languages language-theory

    45
    推荐指数
    3
    解决办法
    9529
    查看次数

    为什么使用小端和大端?

    在大约40年的二进制计算机科学之后,为什么今天仍然使用小端和大端?是否有算法或存储格式与另一个更好地工作?如果我们都切换到一个并坚持下去,那不是更好吗?

    computer-science endianness

    44
    推荐指数
    2
    解决办法
    1万
    查看次数