具有相同数量的0和1的二进制数

5
当我解决Euler项目问题#15时,我意识到可以通过从起点到终点的路径的组合方式的数量来解决它。生成的路线始终具有相同数量的向右或向下选择(或0和1),而正确的路线始终具有相同数量的0和1。
因此,在二进制字中具有相同数量的0和1的数字的数量为 对于1位长度,C(2,1) 对于2位“”,C(4,2) 对于4位“”,C(6,3) ...

现在出现了我的问题: 是否有一个函数可以解决数字具有相同数量的0和1? 我想这更像是一个逻辑函数,我不想迭代所有数字或使用正则表达式(那比迭代还糟糕)。

**另一个问题是这些“平衡”值之间的增长和空间?

6个回答

5
您正在寻找一个popcount函数,它可以计算给定数字中设置位的数量。一些处理器已经内置了该功能,而有些则没有。
c=0; while (n &= (n-1)) c++;

虽然这个方法可以完成任务,但会破坏n的值。

请参考此链接获取更好的算法,或者搜索“popcount”。


你真的不想通过迭代所有可能的数字并测试人口计数来进行暴力破解 - 我们真正寻找的是反向操作。 - Paul R
@Paul R:引用OP的话:“是否有一个函数可以解决一个数字是否具有相同数量的0和1?”,我认为我回答了这个问题。如果问题是错误的,那就是OP的问题。 - Alexandre C.
@Alexandre:你说得对,不过现在已经太晚去除-1了,抱歉。 - Paul R
其实现在删除它也不迟,我猜你应该在踩之前更仔细地阅读答案,没有人甚至尝试遍历所有数字,只是提供了一种检查的方法。毕竟有人认为P != NP,因此搜索不等于识别 ;) - Artem Barger
@Artem:如果超过5分钟,你如何删除投票? - Paul R
我之前不知道popcount指令,这对于这个问题非常有用,谢谢。至于反函数,我考虑过了,但是存在许多序列左侧有0的问题需要评估,所以我无法想出一种求逆的方法。 - AndreDurao

3
作为对Paul R回答的跟进,中心二项式系数的公式有简化,详见http://mathworld.wolfram.com/CentralBinomialCoefficient.html p = n! / ((n/2)!)² = 2n/2 (n-1)!! / (n/2)!
k!!是“双阶乘”,意味着在计算时跳过每个其他数字:k!! = k * (k-2) * (k-4) * ...(只要因子是正数)。
对于您的计算,很多数字将被取消(在同时计算分子和分母时可以使用最大公约数)。

1

如果我理解你说的话是正确的,那么你想要的只是 p = nCr,其中 r = n/2。所以:

p = n! / ((n/2)! * (n/2)!)


这很可能会溢出。计算二项式系数的正确方法是通过使用loggamma计算log(C(n,k))。 - Alexandre C.
6C3 = 20,而不是3!= 6。我认为你有点混淆了。 - Artem Barger
@Artem:是的,我试图简化表达式时引入了一个错误 - 我现在已经编辑了答案,因为似乎没有明显的简化方法。 - Paul R
如果结果不会溢出,可以使用质因数分解法(不会溢出)计算二项式系数。 - st0le

0

可以通过想象你沿着路径选择的方式(即在2n次中选择向上走n次)来直接找到平衡值。

因此,有C(2n,n)个这些值,即2n! / (n! * n!)。当然,总值的数量是2^2n。使用斯特林逼近公式,我们得到

ln n! ~= n ln n - n + ln(2*pi*n)/2
ln 2n!/(n!*n!) = ln(2n!) - 2*ln(n!) ~= 2n*ln(2) - n + ln(4*pi*n)/2 - ln(2*pi*n)

这样好的值的比例,C(2n,n)/2^(2n)

exp(2n*ln(2) - n + ln(4*pi*n)/2 - ln(2*pi*n) - 2n*ln(2)) =
exp(-n)/sqrt(pi*n)

因此,好值的数量呈指数级下降。

这就是为什么直接挑选它们而不是测试是明智的。

但是,如果你真的想要测试,有各种位计数方法在这里。 (Kernighan的方法可能是最快的 - 请注意,他并不是第一个注意到它的人,并且等效算法已经发布在这里!)


是的,我在那个popcount列表中找到了Kernighan的方法,感谢您对好值减少的解释。我从2^2 .. 2^10开始绘制一个图形,居中后,只有中心值(直到2^16)出现在2^10宽度的图形中。 http://img839.imageshack.us/img839/6369/distribuicao.png - AndreDurao

0

对于偶数的'i',在2^i和2^(i-1)之间相等的数字(1和0)的数量由以下公式给出:

(i-1)!/[x! * (x-1)!]

其中x=i/2。

我尝试用C语言编写程序来生成这个公式,但它溢出了。然后我使用浮点运算进行了计算。直到i=56为止,一切都很好。之后精度会降低。

但是BigInt库应该能够在不溢出的情况下完成此任务。


我可以通过组合找到具有相同数量的0和1的数字的数量。帕斯卡三角形的中央列上的值是第一个答案简化公式的相同值。我需要知道是否有逻辑函数可以给出真或假,以确定该数字是否为其中之一。Popcount是我找到的最接近最佳解决方案的方法。使用它,我可以在O(1)的时间内找到该数字是否具有相同的数量。 - AndreDurao

0
你可以使用一个查找表,将n位二进制数映射到其1的数量。例如,如果你总是有23位无符号整数,那么一个16位和7位的查找表将允许你拆分数字,并为16位和7位部分的查找表添加1的数量。(7位查找表可以只是16位表的一部分)。

嗯,我不确定我是否理解正确。但是仅填充表格的成本至少为O(n),我猜这没有比popcount更好的方法来做到这一点。 - AndreDurao

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接