假设我们有K
个不同的二进制数,每个二进制数的长度为N
(N
可能很大)。是否有一种有效的算法来确定将这些数字相互区分所需的最小位数?
例如:
鉴于110
和011
,我们只需检查第一个(或最后一个)位以区分它们,因此最小数字为1
。
鉴于1000
,0100
,0010
和0001
,我们需要检查至少三位来区分,因此最小数字为3
。
鉴于0000
,0100
,1000
和1100
,我们只需检查前两位,因此最小数字为2
。< / p>
跟进:输出要检查的相应索引。
编辑:假设这些二进制数表示为a1[0,1,…,N-1]
,...,aK[0,1,…,N-1]
。此问题等同于查找[i,j,…,m]
的最小子序列[0,1,…,N-1]
,以便a1[i,j,…,m]
,...,aK[i,j,…,m]
是不同的数字。
答案 0 :(得分:1)
Set Cover是根据集合 U 和 U 子集的 S 集合定义的。 U 中的每个元素必须由 S 中的(至少)一个集合覆盖。
如果您可以解决Set Cover,也可以解决此问题。假设您构建了一个 U 集合,其中每个条目 u i,j (其中 i&lt; j ),对应于 k 数字的对(i,j)和(j,i)(因此 | U | = k (k - 1)/ 2 )。现在构建 n 集, S 1 ,...,S n ,对应 n 可能的位置。设置 S j 是对应于位置 j 的所有元素的子集。也就是说,如果数字 k,l 的位置 j 不同,则设置 u k,l ∈S j 子>
因此,simple greedy algorithm for set cover可以给出最小位数的有界近似值。不幸的是,它不会为您提供最小位数。正如@augurar在评论中指出的那样,这个问题是NP-Hard的减少,因此,可能没有可行的精确最优算法。
答案 1 :(得分:0)
你想要做的是某种异或。但不是简单地完全删除所有数字。但是如果你能产生一个二进制数,当它是一个相关的位时,它就是1,如果这个位是不相关的,那么就是0。无关的位是总是具有相同值的位,无论数字是什么:您不需要它来区分数字。例如:
11001
01001
10101
-----
11100
第一位和第二位无关,因为它们总是具有相同的值。
怎么做
为此你需要从你的集合中计算两个二进制数。第一个是所有数字之间的逻辑或。仍为0的位无关紧要,很容易看到。第二个数字是集合中所有数字的逻辑AND。留给1的位是无关紧要的,它也很容易看到。现在,将这两个数字混合在一起。这是你的结果。应用于前一个示例:
11001 | 01001 | 10101 = 11101
11001 & 01001 & 10101 = 00001
11101 ^ 00001 = 11100 // Here is your result, the first 3 bits are relevant
答案 2 :(得分:0)
解决方案必须位于[log2(K), K-1]
范围内。 log2(K)是最好的情况。所以一个强力方法可能是迭代这个范围,每次迭代都采用每个可能的位组合,直到它区分所有K个字符串。但时间复杂度可能是指数级的。
答案 3 :(得分:0)
我在创建哈希映射算法(https://github.com/robert-king/my-hash-map)
时解决了这个问题我的解决方案是使用线性整数编程。如果第i位重要则让Xi为1,否则为零。然后对于具有第i位和第i位的每一对数字A和B,我们需要Sum(Xi,Ai!= Bi)> = 1,这样我们就可以区分A和B.目标函数是最小化Sum( Xi),从而最小化位数。
性能:对于最多100个随机数,它需要一秒钟。 150个随机数,需要10-20秒。 (不记得它们是32位还是64位数字。)
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
}