创建具有特定位数设置的多个数字

Posted

技术标签:

【中文标题】创建具有特定位数设置的多个数字【英文标题】:Creating multiple numbers with certain number of bits set 【发布时间】:2010-10-05 03:25:24 【问题描述】:

问题

我需要创建 32 位数字(有符号或无符号都无所谓,无论如何都不会设置最高位)并且每个数字都必须设置给定数量的位。

天真的解决方案

最简单的解决方案当然是从零开始。在一个循环内,数字现在增加一,对位的数量进行计数,如果计数具有所需的值,则将数字存储到列表中,如果不是,则循环重复。如果找到足够的数字,则停止循环。当然,这工作得很好,但是一旦所需的位数变得非常高,它就会非常缓慢。

更好的解决方案

设置(假设)5 位的最简单数字是设置前 5 位的数字。这个号码可以很容易地创建。在一个循环中,第一位被设置并且数字向左移动一个。这个循环运行了 5 次,我找到了第一个设置了 5 位的数字。接下来的几个数字也很容易创建。我们现在假设这个数字是 6 位宽,并且最高的没有设置。现在我们开始将第一个零位向右移动,因此我们得到 101111、110111、111011、111101、111110。我们可以通过在前面添加另一个 0 并重复此过程来重复此过程。 0111110、1011110、1101110 等。但是,这样数字的增长速度会比必要的快得多,因为使用这种简单的方法,我们会忽略 1010111 这样的数字。

那么有没有更好的方法来创建所有可能的排列,一种通用的方法,可以使用,不管下一个数字有多少位,也不管我们需要设置多少位?

【问题讨论】:

另见:Generate all binary strings of length n with k bits set 使用combinadic form (combinatorial number system) 中的整数表示与实际(按字典排序的组合)之间的自然(也称为Lehmer)映射进行生成。处理数字的二进制表示是生成组合、排列和幂集的关键 【参考方案1】:

您可以使用bit-twiddling hack from hackersdelight.org。

在他的书中,他有代码用相同数量的一位集合获得下一个更高的数字。

如果您将其用作增加数量的原语,您所要做的就是找到一个起点。获得设置了 N 位的第一个数字很容易。只是 2^(N-1) -1。

您将通过这种方式非常快速地遍历所有可能的数字。

  unsigned next_set_of_n_elements(unsigned x) 
  
     unsigned smallest, ripple, new_smallest, ones;
   
     if (x == 0) return 0;
     smallest     = (x & -x);
     ripple       = x + smallest;
     new_smallest = (ripple & -ripple);
     ones         = ((new_smallest/smallest) >> 1) - 1;
     return ripple | ones;
  

  // test code (shown for two-bit digits)

  void test (void)
  
    int bits = 2;
    int a = pow(2,bits) - 1;
    int i;

    for (i=0; i<100; i++)
    
       printf ("next number is %d\n", a);
       a = next_set_of_n_elements(a);
    
  

【讨论】:

哇,这更好。实际上 Jon Skeet 的回复已经让我想到了一个简单的算法来获得我正在寻找的东西,但这更容易(尽管我可能从来没有想出像这段代码这么聪明的东西!):-)跨度> 哦 - 你不必想出这样的技巧。知道那里有什么技巧并记住它们是否有助于解决问题是很方便的。我只做过一个小技巧,它是高度特定于架构的。 酷!由于 minimum 总是 2 的幂,因此“感觉”应该有一种比使用除法更快的方法将 new_smallest 向右移动那么多,但我想不出办法...... @j_random_hacker:看看我发布的链接中的其他解决方案。您会找到执行相同操作的版本并通过使用 count_leading_zeros 等来避免除法。大多数 CPU 可以在一个周期内执行这些操作。这样你就摆脱了分裂。 @NilsPipenbrinck 它给了我错误“一元减号运算符应用于无符号类型”。我认为这与“未签名”有关。【参考方案2】:

尝试从相反的方式解决问题 - 您尝试做的相当于“在 0-31 范围内查找 n 个数字”。

假设您要查找 4 个数字。您从 [0,1,2,3] 开始,然后每次增加最后一个数字(获得 [0,1,2,4],[0,1,2,5] ...),直到达到极限[0,1,2,31]。然后增加倒数第二个数字,并将最后一个数字设置为高一个:[0,1,3,4]。回到增加最后一个数字:[0,1,3,5],[0,1,3,6]...等。一旦你结束了,你回到[0,1,4 ,5] - 最终你到达 [0,1,30,31] ,此时你必须再往前退一步:[0,2,3,4] 然后再走。继续前进,直到最终得到 [28,29,30,31]。

给定一组数字,显然很容易将它们转换为 32 位数字。

【讨论】:

哇,这很棘手!你是绝对正确的:我为什么要摆弄比特。实际上我只需要置换位位置,位位置可以表示为数字。这将我的问题变成了一个更容易解决的问题。【参考方案3】:

您想生成组合,请参阅此Wikipedia article。

【讨论】:

【参考方案4】:

您要么需要 Factoradic Permutations(Google 上的那个),要么需要 Wiki 上的一种算法

【讨论】:

以上是关于创建具有特定位数设置的多个数字的主要内容,如果未能解决你的问题,请参考以下文章

如何将多个 div 设置为特定目标位置的动画?

如何格式化具有特定位数的数字?

Javascript如何将十进制数转换为具有特定小数位数的字符串

Python - 具有多个警报的闹钟

如何在 info.plist 文件中为特定游戏关卡中保存的颜色设置创建条目?

vb.net怎么设置数字保留小数点位数