有N
是或否问题的测试。您可以编写测试,教授会告诉您有多少答案是正确的。通过测试的最快方法是什么?即用最少的试验次数给出所有问题的正确答案。
UPD N+1
试验的解决方案很明显。在每次试验中,我们都会对一个问题有正确答案。它是1位信息。但教授每次给我们一个从0到N的数字,它是log 2 (N + 1)位的信息。这就是最佳解决方案具有O(N / log(N))
复杂性的原因。 我正在寻找具有亚线性最差时间复杂度的任何解决方案。
答案 0 :(得分:5)
对N + 1解决方案的明显改进:
从所有Y答案开始。
然后我们确切知道有多少是/否。
让p
为任何给定位置的肯定概率。 p >= 1/2
不失一般性。
然后,我将平均显示2 - p^2
次尝试的两个第一个答案。
我改变了我对前两个问题的答案。
至少p^2
我会知道他们两个人的确切答案。如果没有 - 那么我至少知道其中一个是Y而另一个是N,我需要再问一个问题。
所以在p = 1/2
的最糟糕情况下,我需要1 + N * 7/8
。
答案 1 :(得分:3)
免责声明:我不知道这是否是最快的方法。我敢肯定,在特定的情况下你可以减少试验,但这可能是一个严格的上限(最糟糕的情况)。
根据您的喜好填写第一轮试用版,只需记住您的选择。如果您愿意,可以为所有人选择“否”。
在下一个试验中,仅更改第一个答案(在示例后面:选择是)。根据答案的变化,您将知道第一个问题的正确答案(如果结果增加,如果没有,则给出正确答案,不正确答案)。
现在只改变第二个,依此类推。
你需要N + 1条小径。
答案 2 :(得分:3)
这是另一种方法(分而治之)。为简单起见,我将引用N
的特定值,但概括很简单。
让N=10
让我们假设在第一轮(trial=1
)我们正确回答了5
个问题。这是最有可能的结果,也是揭示信息量较少的结果。
然后以下逻辑允许我们不检查每个数字:
将答案列表分为两组1...5
,6...10
。鉴于我们有5个正确的答案,每组的可能正确答案是
(0,5)
(1,4)
(2,3)
(3,2)
(4,1)
(5,0)
现在,对于下一个试验,请翻阅答案1...5
。然后上述状态改变如下:
(0,5)--> (5,5) -- 10 correct
(1,4)--> (4,4) -- 8 correct
(2,3)--> (3,3) -- 6 correct
(3,2)--> (2,2) -- 4 correct
(4,1)--> (1,1) -- 2 correct
(5,0)--> (0,0) -- 0 correct
如果老师说10
或0
我们已经完成,或者我们需要再分别进行一次试验。在任何情况下,根据老师说的数字,我们可以知道每个区间有多少正确的答案。然后我们可以决定。
1...5
和6...10
分别有(4,1)正确。所以我们也翻转6...10
并得到8正确,见下文。6...10
并转到6正确,请参阅下文。1...5
和6..10
,我们最多需要3个步骤,因此共有8个步骤,包括前两个步骤。1...5
划分为1...3
,4...5
)并找出错误的答案。如果它在4...5
,我们需要两个步骤,否则我们需要三个步骤。因此,共有8个步骤。答案 3 :(得分:3)
在Carsten评论的article中,Erdös和Rényi展示了如何将问题表述为找到最小数量的测试序列的示例,这些测试序列可以共同为未知序列生成唯一的哈希值。由于他们的例子显示了一个五位数的序列,用四次测试求解,我试图为长度为六和七的序列提出一个亚线性数量的测试。
看看Erdös和Rényi的例子,并受到Ioannis提到的“分而治之”的启发,我想也许测试序列有点分裂然后细分序列。花了几次尝试才得到序列长度为7的工作测试。
也许考虑你要求的算法的一种方法可能是推广/自动生成这些测试序列的方法。
下面的JavaScript程序将测试序列与给定长度的所有序列进行比较,并对重合的数字进行散列。如果两个不同的序列生成相同的散列,则程序会通知找到匹配,这意味着这种组合的测试不起作用。如果没有找到匹配,则表示哈希值是唯一的,测试应该有效。
// http://resnet.uoregon.edu/~gurney_j/jmpc/bitwise.html
function setBits(i)
{
i = i - ((i >> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
}
// http://stackoverflow.com/questions/10073699/pad-a-number-with-leading-zeros-in-javascript
function pad(n, width, z) {
z = z || '0';
n = n + '';
return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
}
function numCoincidences(a,b){
var n = 0
for (var i=0; i<a.length; i++){
if (a.charAt(i) == b.charAt(i)){
n ++
}
}
return n
}
var sequenceLength = 6
var tests = [
"111111",
"111000",
"010010",
"011001",
"000100"
]
/***
var sequenceLength = 7
var tests = [
"1111111",
"1111000",
"0100100",
"0110010",
"0110001",
"0001000"
]
***/
var hash = {}
console.log(" " + tests.join(" "))
for (var i=0; i<1<<sequenceLength; i++){
if (setBits(i) < Math.floor(sequenceLength / 2)){
var tmp = pad(i.toString(2),sequenceLength)
var h = ""
for (var j in tests){
h += numCoincidences(tests[j],tmp)
}
console.log(tmp + " " + h.split("").join(" "))
if (hash[h]){
console.log("found match")
} else {
hash[h] = true
}
}
}
console.log("done")
输出:
" 111111 111000 010010 011001 000100" <-- test sequences
"000000 0 3 4 3 5"
"000001 1 2 3 4 4" <-- sequences to match, followed by
"000010 1 2 5 2 4" the number of coincidences
"000011 2 1 4 3 3"
"000100 1 2 3 2 6"
"000101 2 1 2 3 5"
"000110 2 1 4 1 5"
"000111 3 0 3 2 4"
"001000 1 4 3 4 4"
"001001 2 3 2 5 3"
"001010 2 3 4 3 3"
"001011 3 2 3 4 2"
"001100 2 3 2 3 5"
"001101 3 2 1 4 4"
"001110 3 2 3 2 4"
"010000 1 4 5 4 4"
"010001 2 3 4 5 3"
"010010 2 3 6 3 3"
"010011 3 2 5 4 2"
"010100 2 3 4 3 5"
"010101 3 2 3 4 4"
"010110 3 2 5 2 4"
"011000 2 5 4 5 3"
"011001 3 4 3 6 2"
"011010 3 4 5 4 2"
"011100 3 4 3 4 4"
"100000 1 4 3 2 4"
"100001 2 3 2 3 3"
"100010 2 3 4 1 3"
"100011 3 2 3 2 2"
"100100 2 3 2 1 5"
"100101 3 2 1 2 4"
"100110 3 2 3 0 4"
"101000 2 5 2 3 3"
"101001 3 4 1 4 2"
"101010 3 4 3 2 2"
"101100 3 4 1 2 4"
"110000 2 5 4 3 3"
"110001 3 4 3 4 2"
"110010 3 4 5 2 2"
"110100 3 4 3 2 4"
"111000 3 6 3 4 2"
"done"
答案 4 :(得分:1)
天真的解决方案将涉及O(N)尝试:从所有答案开始是,然后在每i
尝试翻转i
答案。如果你的分数增加,请保留;如果没有,请将其翻转。增加i
,重复。
更有效的解决方案可能涉及一种非常简单的遗传算法,其中启发式是教授的答案,而变异可能等同于简单地翻转所有答案。这可能接近O(log N)尝试,但当然乘法常数会更大(至少一个数量级,如果我不得不猜),所以它只适用于大N.
答案 5 :(得分:1)
一些简单的O(n)算法的Python代码:
import random
def ask_prof(A, M): return sum(x == y for x, y in zip(M, A))
N = 10
A = [ random.randint(0, 1) for x in range(N) ]
K, s = [], 0
for trial in range(N):
M = K + [ 0 for x in range(N - len(K)) ]
s1 = ask_prof(A, M)
M[len(K)] = 1
s2 = ask_prof(A, M)
if s1 < s2: K.append(1)
else: K.append(0)
print 'answers are', K