Algorithm 算法-区分一组给定二进制数的最小位数
假设我们得到了不同的二进制数,每个二进制数的长度都是N(Algorithm 算法-区分一组给定二进制数的最小位数,algorithm,Algorithm,假设我们得到了不同的二进制数,每个二进制数的长度都是N(N)。是否有一种有效的算法来确定区分这些数字所需的最小位数 例如: 11001 01001 10101 ----- 11100 给定110和011,我们只需检查第一位(或最后一位)来区分它们,因此最小的数字是1 给定1000、0100、0010和0001,我们需要检查至少三个位来区分,因此最小的数字是3 给定0000、0100、1000和1100,我们只需检查前两位,因此最小数字为2 跟进:输出要检查的相应指标 编辑:假设这些二进制数表
N
)。是否有一种有效的算法来确定区分这些数字所需的最小位数
例如:
11001
01001
10101
-----
11100
给定110
和011
,我们只需检查第一位(或最后一位)来区分它们,因此最小的数字是1
给定1000
、0100
、0010
和0001
,我们需要检查至少三个位来区分,因此最小的数字是3
给定0000
、0100
、1000
和1100
,我们只需检查前两位,因此最小数字为2
跟进:输出要检查的相应指标
编辑:假设这些二进制数表示为
a1[0,1,…,N-1]
,…,aK[0,1,…,N-1]
。这个问题相当于找到[0,1,…,N-1]
的最小子序列[i,j,…,m]
,因此a1[i,j,…,m]
,,aK[i,j,…,m]
是不同的数字。编辑:误解了问题,方案不起作用
package algorithms
import (
"github.com/draffensperger/golp"
"fmt"
)
const numOnes = uint64(19)
const ones = uint64(1 << numOnes - 1)
func diffBit(a, b, i uint64) bool {
return (a >> i) & 1 != (b >> i) & 1
}
func differentBits(a, b uint64) (ints []uint64) {
if a < b {
a,b = b,a
}
for i := uint64(0); a >> i > 0; i++ {
if diffBit(a, b, i) {
ints = append(ints, i)
}
}
return ints
}
func checkBitsDistinguishNums(nums []uint64, bits []uint16) bool {
offsets := make(map[uint16]bool)
for _, num := range nums {
offset := bitScore(num, bits)
_, ok := offsets[offset]
if ok {
return false
}
offsets[offset] = true
}
return true
}
func minimumDistinguishingBits(nums []uint64) (bits []uint16) {
if len(nums) == 1 {
return bits
}
var likelySolutions = [][]uint16{
[]uint16{0},
[]uint16{1},
[]uint16{2},
[]uint16{3},
[]uint16{4},
[]uint16{0,1},
[]uint16{0,2},
[]uint16{5},
[]uint16{1,2},
[]uint16{0,3},
[]uint16{6},
[]uint16{7},
[]uint16{8},
[]uint16{0,4},
[]uint16{1,3},
[]uint16{1,4},
[]uint16{2,3},
[]uint16{0,5},
[]uint16{2,4},
[]uint16{1,5},
[]uint16{0,1,2},
[]uint16{0,1,3},
[]uint16{2,5},
[]uint16{0,6},
[]uint16{0,7},
[]uint16{9},
[]uint16{0,2,3},
[]uint16{2,6},
[]uint16{1,7},
[]uint16{1,6},
[]uint16{3,5},
[]uint16{2,7},
[]uint16{10},
[]uint16{1,3,8},
[]uint16{4,7},
[]uint16{3,6},
[]uint16{0,2,11},
[]uint16{3,8},
[]uint16{1,2,5},
[]uint16{0,3,4},
[]uint16{0,1,12},
[]uint16{0,1,2,5},
[]uint16{4,6},
[]uint16{3,4},
[]uint16{0,10},
[]uint16{0,8},
[]uint16{0,1,4},
[]uint16{11},
[]uint16{1,2,3},
[]uint16{0,2,4},
[]uint16{1,2,9},
[]uint16{3,12},
}
for _, likelySolution := range likelySolutions {
if checkBitsDistinguishNums(nums, likelySolution) {
return likelySolution
}
}
var equations [][]golp.Entry
maxDiffBit := uint64(0)
for i := range nums {
for j := 0; j < i; j++ {
var equation []golp.Entry
for _, bit := range differentBits(nums[i], nums[j]) {
if bit > maxDiffBit {
maxDiffBit = bit
}
equation = append(equation, golp.Entry{int(bit), 1})
}
equations = append(equations, equation)
}
}
lp := golp.NewLP(0, int(maxDiffBit)+1)
for _, equation := range equations {
lp.AddConstraintSparse(equation, golp.GE, 1)
}
var objFn []float64
for i := 0; i <= int(maxDiffBit); i++ {
lp.SetInt(i, true)
objFn = append(objFn, float64(i))
}
lp.SetObjFn(objFn)
lp.Solve()
for i, bit := range lp.Variables() {
if bit == 1 {
bits = append(bits, uint16(i))
}
}
fmt.Println("Likely solution not found but found ", bits)
return bits
}
你想做的是某种异或运算。但不是简单地对所有数字进行XOR运算。但是如果你能产生一个二进制数,当它是一个相关的位时,它有1,如果这个位是不相关的,它有0。不相关位是指无论数字是什么,始终具有相同值的位:您不需要它来区分数字。例如:
11001
01001
10101
-----
11100
第一位和第二位是不相关的,因为它们总是具有相同的值
如何做
为此,需要从集合中计算两个二进制数。第一个是所有数字之间的逻辑OR。仍然为0的位是不相关的,很容易看到。第二个数字是集合中所有数字的逻辑AND。留给1的位是不相关的,也很容易看到。现在,将这两个数字XOR在一起。这是你的结果。应用于上一个示例:
11001 | 01001 | 10101 = 11101
11001 & 01001 & 10101 = 00001
11101 ^ 00001 = 11100 // Here is your result, the first 3 bits are relevant
编辑-不工作。见评论 这个问题可以使用Trie解决 您所需要做的就是以Trie的形式表示二进制数 然后,计算至少有一个节点的不同级别数,该节点的阶数为2 每个级别代表相同的位位置。因此,您必须选择其中一个 这是有效的,因为在每个级别上,您基本上都在做出决策并识别不同的字符串集 如图中所示,有3个级别,其中至少有1个节点的阶数为2。这就是你的答案 从现在开始,很容易输出检查的索引 另一个例子,强调为什么我们要计算级别而不是节点 000001010 011100101110111 答案是3位。定义为U的集合U和U的子集集合S。U中的每个元素必须(至少)由S中的一个集合覆盖 如果你能解决集合覆盖,你也能解决这个问题。假设您构建了一个集合U,其每个条目ui,j(其中i
因此,可以为您提供最小位数的有界近似值。不幸的是,它不会给出最小的位数。正如@augurar在评论中指出的,这个问题是NP难问题,因此,可能没有可行的精确优化算法。解决方案必须位于范围
[log2(K),K-1]
内。log2(K)是最好的情况。因此,蛮力方法可能是迭代这个范围,每次迭代都会使用所有可能的位组合,直到它区分所有的K字符串。但是时间复杂度可能是指数级的。我在创建哈希映射算法时解决了这个问题()
我的解决方案是使用线性整数规划。如果第1位重要,否则为零,则设席为零。然后,对于具有第i位Ai和Bi的每对数字A和B,我们需要求和(Xi,Ai!=Bi)>=1,以便我们能够区分A和B。目标函数是最小化和(Xi),从而最小化位数
性能:对于多达100个随机数,这需要一秒钟的时间。对于150个随机数,需要10-20秒。(不记得是32位还是64位数字)
包算法
进口(
“github.com/draffensperger/golp”
“fmt”
)
常量numOnes=uint64(19)
const one=uint64(1>i)&1!=(b>>i)和1
}
函数差分位(a,b uint64)(整数[]uint64){
如果a>i>0;i++{
if diffBit(a、b、i){
ints=附加(ints,i)
}
}
返回整数
}
func checkBitsDistinguishNums(nums[]uint64,bits[]uint16)布尔{
偏移量:=make(映射[uint16]bool)
对于u,num:=范围nums{
偏移量:=位分数(位数,位)
_,确定:=偏移量[偏移量]
如果可以的话{
返回错误
}
偏移量[偏移量]=真
}
返回真值
}
函数最小区分位(nums[]uint64)(位[]uint16){
如果len(nums)==1{
返回位
}
var likelySolutions=[]]uint16{
[]uint16{0},
[]uint16{1},
[]uint16{2},
[]uint16{3},
[]uint16{4},
[]uint16{0,1},
[]uint16{0,2},
[]uint16{5},
[]uint16{1,2},
[]uint16{0,3},
[]uint16{6},
[]uint16{7},
[]uint16