Dejemos que $f(n, m)$ sea el coste óptimo en el peor de los casos, $0 \le m \le n$ . Ya lo tienes $f(n, 1) \sim \log_2(n)$ . Específicamente, $f(n, 1) = \lceil \log_2(n) \rceil$ . También $f(n, 0) = f(n, n) = 0$ ya que no será necesario adivinar para determinar las falsificaciones.
$f(n, n-1)$ : Como sólo hay una moneda real, la única manera de determinar la verdadera es pasar por todas las monedas (excepto posiblemente la última) - no tiene sentido dividir los montones. Por lo tanto, $f(n, n-1) = n-1$ .
$f(4, 2)$ : Puedes preguntar por una pila de tamaño $1$ o $2$ (no tiene sentido preguntar por un montón de tamaño $3$ ya que está garantizada su falsificación). Si pregunta por un montón de $1$ , entonces eso se reduce a $1+\max(f(3, 1), f(3, 2)) = 1+\max(2, 2) = 3$ . Si preguntas por un grupo de tamaño $2$ y obtienes un "no" como respuesta, has terminado. Si la respuesta es "sí", tendrá que preguntar $2$ más preguntas, así que $f(4, 2) = 3$ .
$f(n, 2)$ : Preguntemos sobre $k$ monedas, $1 \le k < n$ . Si la respuesta es "no", entonces se convierte en $1+f(n-k, 2)$ . Para el caso de que la respuesta sea "sí", definiré una función de ayuda, $g(a, b)$ que representa el coste óptimo en el peor de los casos cuando hay $2$ total de falsificaciones, $a$ es el tamaño del grupo de monedas que se garantiza que tiene al menos $1$ falsificación, $b$ es el tamaño del grupo con un número desconocido de falsificaciones (ya sea $1$ o $0$ ). Entonces $f(n, 2) = \min(\max(1+f(n-k, 2), 1+g(k, n-k)) \forall k, 1 \le k < n)$ .
$g(a, b)$ : $g(a, 0) = f(a, 2)$ , $g(1, b) = f(b, 1)$ . Tenemos dos opciones: o bien preguntar por $k_1 (1 \le k_1 < a)$ del grupo $a$ o $k_2 (1 \le k_2 \le b)$ del grupo $b$ . Si preguntamos por $k_1$ y obtener una respuesta afirmativa, que se convierte en $1+g(k_1, a+b-k_1)$ . Si obtenemos un "no" como respuesta, eso se convierte en $1+g(a-k_1, b)$ . Si preguntamos $k_2$ y obtener un "sí", que se convierte en $1+f(a, 1)+f(k_2, 1)$ . Si obtenemos un "no", eso se convierte en $1+g(a,b-k_2)$ . Por lo tanto, $g(a, b) = \min(\max(1+g(k_1, a+b-k_1), 1+g(a-k_1, b)) \forall k_1, 1 \le k_1 < a, \max(1+f(a, 1)+f(k_2, 1), 1+g(a,b-k_2)) \forall k_2, 1 \le k_2 \le b)$
Usando esto, me las arreglé para encontrar A200311 en la OEIS. Obsérvese que la entrada de la OEIS está desplazada por dos (es decir, A200311(n+2) = f(n, 2)) y la primera entrada es diferente. A partir de ahí, he descubierto que $$f(n, 2) = \left\lfloor 2\log_2(n-1) + 2\log_2 \left(\frac{7}{6} \right) \right\rfloor +_? 1$$
(No he podido encontrar la notación de " $x$ o $x+1$ ", así que me inventé algo - espero que quede claro)
Lo que noté fue que para el primer grupo, se podía tomar cualquier cosa de $\approx n/4$ a $ \approx n/3$ . Por ejemplo, para $n = 400$ , cualquier cosa desde $96 \approx \frac{400}{4}$ a $128 \approx \frac{400}{3}$ estaba bien para el tamaño de la primera suposición. Todo lo demás no era óptimo. En este rango, el coste del peor caso era el mismo tanto si el oráculo respondía "sí" como "no" a esa consulta.
Editar para f(n, m) :
Voy a definir una función de ayuda, $g(p_1;p_2;...;p_k, b, c)$ para el coste óptimo en el peor de los casos, donde $p_i$ representa el número de pilas de tamaño $i$ garantiza tener al menos una moneda, $b$ monedas no examinadas, y $c$ total de falsificaciones en la configuración.
La primera consulta equivale a $g$ , $$f(n, m) = g(\lbrack \rbrack, n, m)$$
Echemos un vistazo a $g$ ahora. Los casos base son $g(p_1;p_2;...;p_k, b, 1\cdot p_1 + 2\cdot p_2 + ... + k\cdot p_k + b) = 0$ y $g(p_1;p_2;...;p_k, b, p_1+p_2+...+p_k) = \sum_{i=1}^{k} p_i \cdot f(i, 1)$ .
Para cualquier otro momento, tenemos varias opciones: desde un montón de tamaño $t$ con una falsificación conocida, podemos hacer cualquier consulta hasta el tamaño $m_1, 1 \le m_1 < t$ . Digamos que elegimos $m_1$ de $t$ . Entonces, con una respuesta afirmativa, eso hace que $p_t$ bajar $1$ , $p_{m_1}$ subir $1$ y $b$ subir $t-m_1$ . Con una respuesta "no", eso hace que $p_t$ bajar $1$ y $p_{t-m_1}$ subir $1$ .
De la " $b$ ", podemos hacer una consulta hasta el tamaño $m_2, 1 \le m_2 \le \min(b, b-c+\sum_{i=1}^{k} k\cdot p_k)$ . Con una respuesta afirmativa, $p_{m_2}$ sube $1$ y $b$ baja $m_2$ . Con una respuesta negativa, $b$ baja $m_2$ .
Utilizando un programa de Python, obtuve el coste óptimo correcto en el peor de los casos (código en la parte inferior). Lo que noté fue que $$f(n, n-k) = n-1$$ para un fijo $k$ después de $n$ es lo suficientemente alto. Por ejemplo, para $k = 7$ , $f(n, n-7) \not = n-1$ sólo para $ n \le 10$ . En general, los resultados numéricos sugieren que $f(n, n-k) = n-1$ si $$n \ge \left\lfloor \frac{3k+1}{2} \right\rfloor$$
Equivalentemente, $f(n, m) = n-1$ para $$m \ge \left\lceil \frac{n}{3} \right\rceil$$
El código de Python es bastante lento: fuerza bruta todas las ramas posibles. No implementé la poda alfa-beta como podría haberlo hecho.
def f(n, m, dic1 = {}, dic2 = {}):
T = (n, m)
if T in dic1:
return dic1[T]
if m == 0:
dic1[T] = 0
return 0
if m == 1:
dic1[T] = math.ceil(math.log2(n))
return dic1[T]
if n == m:
dic1[T] = 0
return 0
elif n == m+1:
dic1[T] = m
return m
dic1[T] = g((), n, m, dic1, dic2)
return dic1[T]
def g(P, b, c, dic1, dic2):
#precondition: P[-1] != 0
#would also speed up computation since more memoization possibility
T = (P, b, c)
#T = (tuple, int, int)
numPiles = sum(P)
totalCoins = sum((i+1)*P[i] for i in range(len(P)))+b
if T in dic2:
return dic2[T]
if c == numPiles:
#one coin in each pile
dic2[T] = sum(P[i]*f(i+1, 1, dic1, dic2) for i in range(len(P)))
return dic2[T]
if c == totalCoins:
#all the remaining coins are counterfeits
dic2[T] = 0
return 0
worstCase = math.inf
for index in range(len(P)):
if P[index] != 0:
#can only ask if there is a pile with that many coins
t = index+1 #to adjust for 0-based indexing
for m1 in range(1, min(t-1, totalCoins-c)+1):
tmpP = P[:t-1]+(P[t-1]-1,)+P[t:]
firstNewP = tmpP[:m1-1]+(tmpP[m1-1]+1,)+tmpP[m1:]
secondNewP = tmpP[:t-m1-1]+(tmpP[t-m1-1]+1,)+tmpP[t-m1:]
while len(firstNewP) > 0 and firstNewP[-1] == 0:
#to make sure that the last element is not zero
firstNewP = firstNewP[:-1]
while len(secondNewP) > 0 and secondNewP[-1] == 0:
#to make sure that the last element is not zero
secondNewP = secondNewP[:-1]
comp = 1+max(g(firstNewP, b+t-m1, c, dic1, dic2), g(secondNewP, b, c, dic1, dic2))
if comp < worstCase:
worstCase = comp
for m2 in range(1, min(b, totalCoins-c)+1):
if len(P) < m2:
firstNewP = P+(0,)*(m2-len(P))
else:
firstNewP = P
firstNewP = firstNewP[:m2-1]+(firstNewP[m2-1]+1,)+firstNewP[m2:]
comp = 1+max(g(firstNewP, b-m2, c, dic1, dic2), g(P, b-m2, c, dic1, dic2))
if comp < worstCase:
worstCase = comp
dic2[T] = worstCase
return worstCase