Algorithm 算法-区分一组给定二进制数的最小位数

Algorithm 算法-区分一组给定二进制数的最小位数,algorithm,Algorithm,假设我们得到了不同的二进制数,每个二进制数的长度都是N(N)。是否有一种有效的算法来确定区分这些数字所需的最小位数 例如: 11001 01001 10101 ----- 11100 给定110和011,我们只需检查第一位(或最后一位)来区分它们,因此最小的数字是1 给定1000、0100、0010和0001,我们需要检查至少三个位来区分,因此最小的数字是3 给定0000、0100、1000和1100,我们只需检查前两位,因此最小数字为2 跟进:输出要检查的相应指标 编辑:假设这些二进制数表

假设我们得到了不同的二进制数,每个二进制数的长度都是N(
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