在没有偏见的情况下计算一系列数字的最有效方法?

时间:2011-10-31 16:40:49

标签: c algorithm bit-manipulation

我想在STDOUT上打印一系列数字。但是,不是从0,1,2 ... N-1,N计数,而是想使用广度优先搜索进行迭代。我想用尽可能最少/最不密集的指令(即没有分支)来做到这一点。

例如,假设范围是[1,128]。我想这样算:

64
32
96
16
87
...
128
1
坦率地说,我不在乎它的宽度或深度,或者其他什么。我只想要一个覆盖数字线均匀的计数算法,这样如果数字线是跷跷板,它将从算法的开头到结束进行平衡。

不,这不是作业:-P

编辑:寻找O(n)并且不依赖于存储整个列表的东西。

3 个答案:

答案 0 :(得分:7)

使用反转位作为键对数字进行排序。

这个Python代码演示了这个概念:

>>> sorted(range(1,128), key=lambda x: ('{:08b}'.format(x))[::-1])
[64, 32, 96, 16, 80, 48, 112, 8, 72, 40, 104, 24, 88, 56, 120, 4, 68, 36, 100, 20, 84, 52, 116, 12, 76, 44, 108, 28, 92, 60, 124, 2, 66, 34, 98, 18, 82, 50, 114, 10, 74, 42, 106, 26, 90, 58, 122, 6, 70, 38, 102, 22, 86, 54, 118, 14, 78, 46, 110, 30, 94, 62, 126, 1, 65, 33, 97, 17, 81, 49, 113, 9, 73, 41, 105, 25, 89, 57, 121, 5, 69, 37, 101, 21, 85, 53, 117, 13, 77, 45, 109, 29, 93, 61, 125, 3, 67, 35, 99, 19, 83, 51, 115, 11, 75, 43, 107, 27, 91, 59, 123, 7, 71, 39, 103, 23, 87, 55, 119, 15, 79, 47, 111, 31, 95, 63, 127]

查看每个数字的位模式显示其工作原理/原因:

>>> '{:08b}'.format(64)
'01000000'
>>> '{:08b}'.format(32)
'00100000'
>>> '{:08b}'.format(96)
'01100000'

注意,该过程也可以即时完成,不需要排序:

>>> [int('{:07b}'.format(i)[::-1], 2) for i in range(1, 128)]
[64, 32, 96, 16, 80, 48, 112, 8, 72, 40, 104, 24, 88, 56, 120, 4, 68, 36, 100, 20, 84, 52, 116, 12, 76, 44, 108, 28, 92, 60, 124, 2, 66, 34, 98, 18, 82, 50, 114, 10, 74, 42, 106, 26, 90, 58, 122, 6, 70, 38, 102, 22, 86, 54, 118, 14, 78, 46, 110, 30, 94, 62, 126, 1, 65, 33, 97, 17, 81, 49, 113, 9, 73, 41, 105, 25, 89, 57, 121, 5, 69, 37, 101, 21, 85, 53, 117, 13, 77, 45, 109, 29, 93, 61, 125, 3, 67, 35, 99, 19, 83, 51, 115, 11, 75, 43, 107, 27, 91, 59, 123, 7, 71, 39, 103, 23, 87, 55, 119, 15, 79, 47, 111, 31, 95, 63, 127]

在C中,反转位是一项微不足道的练习:

long reverse(long x) {
    long result = 0:
    int i;

    for (i=0 ; i<32 ; i++) {
        result <<= 1;
        result |= x & 1;
        x >>= 1;
    }
    return result;
}

答案 1 :(得分:2)

好的,我认为这可以用最少的数据存储来实现。这仅适用于完全平衡的填充树木。将它扩展到另一个元素很容易(只打印额外的最后一个...),但更多的是需要更多的思考。此外,如果您的范围不是1 ... N,而是M ... N,那么只需将M-1添加到所有内容中就足够了。

  1. 树的底行是1,1 + 2,1 + 4,1 + 6等。
  2. 树的倒数第二行是2,2 + 4,2 + 8等。
  3. 树的第三行是4,4 + 8,4 + 16等。
  4. 因此,对于1 ...(N-1)树,其中N是2的幂,我们可以首先使用log 2(N)计算树的高度。为方便起见,设n = N-1。示例树是N = 16:

    树的第一行(根)是((n-1)/ 2)+1。将此行称为0.因此您可以继续打印。下一行(第1行)的第一个元素是前一行的第一个元素的一半。方便地,增量是前一行的第一个值。因此,对于N = 16,第2行的第一个元素= 4.您可以打印它。下一个元素是4 + 8 = 12,您可以打印。由于一行有2 * rownum元素,现在你已经完成了第0行(2 ** 1 = 2)。下一行的开头是前一行的一半,例如2/2 = 2.它有2 ** 2 = 4个元素,增量为4.所以,2,6,10,14。

                   8
    
          4                12
    
      2       6       10         14
    1   3   5   7   9   11    13    15
    

    现在,如果你想要1 ... 16,你可以直接在右下角添加额外的元素,所以你最后输出它。

    当然,不是一直调用pow(),而是只乘以2(如果合适,编译器会转换为位移)来确定此级别的节点数。当然,仍然会有分支来测试循环。

    我应该警告你,我今天还没有喝足够的茶,所以这可能显然是愚蠢的。但似乎有效。至少在我脑海里:-D

答案 2 :(得分:0)

也许你想要Gray code

或仅限于您的范围的简单句点pseudo random number generator