噪声隐藏值

时间:2018-10-07 03:53:06

标签: python xor

这是一个面试问题,我丝毫不知道如何解决。采访结束了,但是我真的很想看看他们在寻找什么。我试图对此进行混淆,以尊重提出要求的公司。

存在一个由S位组成的秘密整数N。我们的工作是高度确定地猜测这个秘密整数。

我们只能通过秘密方法S访问foo(G),该秘密方法包含一个猜测G和一个XOR,它们与S和一个随机生成的值{{1 }},其中V中的每一位都有1的10%几率。然后计算1的数量并将其返回为整数

V
每次调用foo(g): generate v return bin(g ^ v ^ s).count('1') 都会生成

V 假设我们在面试失败或世界爆炸之类的事情发生前,有100,000次尝试运行foo的机会。

我们如何处理呢?

让我抓狂的是,即使猜对了正确答案,也有N / 10的机会从foo返回非零值。因此,即使是蛮力尝试也似乎不在桌上。

2 个答案:

答案 0 :(得分:0)

假设:如果您对具有相同G的函数foo(G)重复足够的次数,则foo(G)的平均结果将足够接近期望值。

例如,假设S有N位,其中M位为1,设G = N bits of 0,则foo(G)的期望值为E(N,M)=M*0.9 + (N-M)*0.1。既然我们知道N,并且我们可以得到foo(G)的平均值,那么很容易确定M的数量。实际上,我们甚至不需要找出M。

当我们有数字E(N,M)时,其余的将很简单:迭代i至N,并使G的第i位等于1,而其余的为零,然后重复foo(G)足够多次。如果s的第i位等于1,则foo(G)的期望为E(N-1,M-1)+0.1=E(N,M)-0.8,否则,如果S的第i位等于0,则期望为foo(G)中的E(N-1,M)+0.9=E(N,M)+0.8

然后您可以算出S的值。在同一个G上重复foo(G)的次数越多,确定性就越高。

一些示例代码:

import numpy as np

S = 1117506159690372465501725393907 # an 100 bit number

def foo(S, N, G):
    bits = np.random.choice(2, size=N, p=[0.9,0.1])
    V = int("".join(str(b) for b in bits), 2)
    return bin(G^V^S).count('1')

if __name__ == '__main__':
    N = 100
    result = np.empty((101,1000))
    for j in range(1000):
        G = int("0" * N, 2)
        result[0,j] = foo(S, N, G)
    for i in range(100):
        for j in range(1000):
            G = int("0"*i + "1" + "0"*(N-i-1), 2)
            result[i+1, j] = foo(S, N, G)
    avg = np.mean(result, axis=1)
    avg -= avg[0]
    out = "0b"+"".join("1" if num < 0 else "0" for num in avg[1:])
    print(str(bin(S))==out) # True

答案 1 :(得分:0)

这是我要怎么做:

from random import random as r
from collections import defaultdict

N = 8                # Number of bits
S = 123              # Secret number

stop_iter = 100      # Number of iterations
#stop_tol -- also an option, but seems risky given noise

def foo(g):
    V = int(''.join([str(int(r() < 0.1)) for _ in range(N)]), 2)
    return bin(g ^ V ^ S).count('1')

def test_guess(g, n=10):
    total = 0
    for _ in range(n):
        total += foo(g)
    return total / n

def test_perturb(g, p, n=10):
    g ^= (1 << p)
    return test_guess(g, n)

def test_bit_positions(g):
    deltas = {}
    for i in range(N):
        deltas[i] = test_perturb(g, i)

    return deltas

def itemval(i): return i[1]


history = defaultdict(list)
guess = 0                       # Initial
for _ in range(stop_iter):
    deltas = test_bit_positions(guess)
    error = sum(deltas.values())

    history[guess].append(error)

    (index, delta) = min(deltas.items(), key=itemval)
    guess ^= (1 << index)
    print(guess, bin(guess), "after flipping bit %d" % index, error)

    # If you had to, you could evaluate history at each iteration,
    #   trying to identify a value that has set itself apart
    #   potentially halting early but slowing the program

mean_error = {k:(sum(lst) / len(lst)) for (k,lst) in history.items()}

print()
print("Results:")
mean_error = sorted(mean_error.items(), key=itemval)
for (guess, err) in mean_error[:5]:
    print(guess, err)

print()
print("Guess:", mean_error[0][0])

示例输出(N = 8,S = 123,stop_iter = 100,n = 10):

Results:
123  12.799021276595743
127  17.55975
 59  17.564333333333334
251  17.583
121  17.58742857142857

Guess: 123 (Correct)

Calls to foo(): 8000

示例输出(N = 20,S = 12345,stop_iter = 100,n = 10)

Results:
 12345 56.19999999999998
 77881 69.69999999999999
 12601 69.85
274489 69.93333333333334
  8249 70.10000000000001

Guess: 12345 (Correct)

Calls to foo(): 20000

基本迭代优化,通过比较您当前猜想的N个扰动版本,尝试使error项尽可能接近零。由于噪音,这将比平时更加​​棘手。

您还可以调整一些参数,两个n函数中的test_默认参数以及迭代次数stop_iter