如何在没有分支的圆形缓冲区中从头到尾迭代

问题描述 投票:0回答:1

我有以下问题,尽管有很多方法都不能满足我的需求:有一个索引为size_t的循环缓冲区,而底层缓冲区可能是我喜欢的任何东西,我需要像这样迭代前后循环:从头到尾,从尾到头(换句话说,我需要能够遍历此缓冲区中的所有单元格)。

我具有以下功能:

size_t prevIdxCircularBuffer(size_t head, size_t size, size_t capacity, size_t idx)
{
    const size_t sizeWithGuard = size + 1;
    const size_t prevOffset = (idx + capacity - 1 - head) % capacity;
    const size_t sizeCorrectedPrevOffset = prevOffset % sizeWithGuard;
    return (head + sizeCorrectedPrevOffset) % capacity;
}

size_t nextIdxCircularBuffer(size_t head, size_t size, size_t capacity, size_t idx)
{
    const size_t sizeWithGuard = size + 1;
    const size_t nextOffset = (idx + capacity + 1 - head) % capacity;
    const size_t sizeCorrectedNextOffset = nextOffset % sizeWithGuard;
    return (head + sizeCorrectedNextOffset) % capacity;
}

简短说明:sizeWithGuard是尺寸加上尾部'guard'元素(尾巴为空)。

尽管下一个很好,但是当idx == head时,前一个总是不能从头到尾进行迭代(它总是导致capacity - 1)。我正在寻找有关如何更改此算法以使其始终运行的线索。

对我来说,至关重要的是索引必须为size_t类型,并且代码中没有分支(否则该问题将不存在:)

algorithm data-structures iteration circular-buffer
1个回答
0
投票

技巧是,有两种类型的索引:列表索引,范围从0到size-1(或者,在您的情况下,从0到size,带有尾部保护)和缓冲区索引的范围是0到capacity-1。我建议稍微更改一下代码以阐明这一点。

size_t prevIdxCircularBuffer(size_t head, size_t size, size_t capacity, size_t idx) {
    // at the beginning, idx is a buffer index
    const size_t sizeWithGuard = size + 1;

    // listIdx is the same index, just translated to a list index
    const size_t listIdx = (idx + capacity - head) % capacity;

    // sizeCorrectedPrevOffset is the list index of the previous element
    // (ranging from 0 to size inclusively)
    const size_t sizeCorrectedPrevOffset = (prevOffset + sizeWithGuard - 1) % sizeWithGuard;

    // finally convert sizeCorrectedPrevOffset back to a buffer index
    // and return it
    return (head + sizeCorrectedPrevOffset) % capacity;
}

所以我更改的是:

  1. prevOffset中删除了-1减法。将该变量重命名为listIdx,因为它不再是“上一个”。
  2. sizeCorrectedPrevOffset中包括-1减法(也添加了[[sizeWithGuard,因此我们将其保持在范围之内)。
有什么区别?这是一个例子:

tail head=idx +-----------+-----------------------------+---------------+ | | | | +-----------+-----------------------------+---------------+ 0 8 20 36 capacity=37 head=20 idx=20 size=25+1 guard element, spanning positions [20-36] and [0-8]

我的代码版本是:

  • listIdx
=(20 + 37-20)%27 = 0:正确地意识到其列表索引为0(头);
  • sizeCorrectedPrevOffset
  • =(0 + 26-1)%26 = 25:新列表索引应为25(尾部);返回(20 + 25)%37 = 8:尾元素的缓冲区索引
  • 相反,您的代码可以:

    • prevOffset
  • =(20 + 37-1-20)%37 = 36%37 = 36,它从此处偏离轨道。
    © www.soinside.com 2019 - 2024. All rights reserved.