Trova la password


12

Un normale lucchetto a combinazione a N cifre è composto da N dischi rotanti. Ogni disco ha le cifre 0-9 inscritte in ordine e devi aprirle con la password corretta per aprirlo. Ovviamente, se non si conosce la password, è necessario provare al massimo 10 N volte prima di sbloccare esso. Questo non è interessante.

Lucchetto a combinazione

Quindi consideriamo una variante del lucchetto a combinazione, chiamiamola lucchetto rivelatore di distanza.

In ogni tentativo fallito di aprire un blocco rivelatore di distanza, risponderà al numero minimo di movimenti da sbloccare.

Un movimento è definito come una rotazione di una posizione, ad esempio ha bisogno di 1 movimento da 890a 899e 9 movimenti da 137a 952.

La sfida

Dato un blocco rivelatore di distanza con la sua password sconosciuta, prova ad aprire il blocco con un numero minimo di tentativi (non movimenti), evitando che il programma diventi troppo lungo.

Regole e punteggi

  • Dovresti scrivere un programma completo che input da stdin e output a stdout. Il programma dovrebbe fare input / output come segue:
Start
    Input an integer N (number of digits) from stdin
    Do
        Output a line containing decimal string of length N (your attempt) to stdout
        Input an integer K (response of the lock) from stdin
    While K not equal 0
End
  • Il tuo programma dovrebbe gestire fino a N = 200 e dovrebbe funzionare per meno di 5 secondi su qualsiasi input.

  • Gli zeri iniziali nell'output non devono essere omessi.

  • Sono 5 dati di test per ogni lunghezza, quindi il numero totale di dati di test è 1000. I dati di test vengono generati casualmente.

  • Il punteggio finale sarà (numero totale di ipotesi in tutti i dati di test) * ln (lunghezza del codice in byte + 50). Il punteggio più basso vince. (ln è log naturale)

  • Segnerò il programma per te. Se vuoi sapere come segnerò il tuo programma, o se vuoi segnarlo da solo, dai un'occhiata alle modifiche precedenti su questo post .

  • Questa sfida terminerà alle 14:00 UTC 2017/12/07. Invierò quindi la mia soluzione.

Esempio in esecuzione

Le righe che iniziano con >rappresentano l'input e altre rappresentano l'output del programma.

Puoi avere una password nella tua mente e interagire con il tuo programma per testarla.

> 3   # 3-digit lock. The hidden password is 746
000   # 1st guess (by program)
> 11  # response to the 1st guess
555   # 2nd guess
> 4   # ...
755
> 2
735
> 2
744
> 2
746   # finally the correct answer! The program attempts 6 times.
> 0   # this is not necessary

Programma di esempio

EDIT: forse il formato di input / output sopra non era chiaro. Ecco un programma di esempio in Python.

Python, 369 byte, numero totale di tentativi = 1005973, punteggio = 6073935

import sys

N = int(input()) # get the lock size

ans = ''
for i in range(N): # for each digit
    lst = []
    for j in range(10): # try all numbers
        print('0' * i + str(j) + '0' * (N - i - 1)) # make a guess
        result = int(input()) # receive the response
        lst.append(result)
    ans += str(lst.index(min(lst)))
print(ans) # output the final answer

Grazie a Giona per semplificare la sfida.

Risposte:


3

C, 388 374 368 byte, tentativi totali = 162751, punteggio = 982280

char s[999];i;G;H;t;n;h;e;R(x){scanf("%d",x);}W(i,x,a){printf((i-n?0:4)+"%0*d%0*d\n",i,x,n-i,0);R(a);}S(k,i){if(!(t=e=k>4?s[i]=48:k<1?s[i]=53:!G?H=k,G=i:0)){for(;t++<n;)putchar(48|t==i|t==G);puts("");R(&t);t==h?W(G,e=1,&t):0;s[G]=t>h?53+H:53-H,s[i]=t>h^e?53+k:53-k;G=0;}}main(){R(&n);for(W(n,0,&h);i++<n;S(t-h+5>>1,i))W(i,5,&t);s[G]=53+H,puts(s+1),s[G]=53-H,puts(s+1);}

Benvenuti in PPCG! Hai un bel punteggio di 162751*ln(388+50)=989887.
Colera,

3

C # (.NET Core) , 617 byte, tentativi totali = 182255, punteggio = 1185166

using System;namespace p{class C{static void Main(){var l=Console.ReadLine();int L=int.Parse(l);var g=new int[L];var p=n(g);for(int i=0;i<L;i++){g[i]=5;var d=n(g);var f=d-p;var s=f;switch(s){case 5:g[i]=0;d-=5;break;case-5:break;case 3:g[i]=1;d=n(g);f=d-p;if(f>0){g[i]=9;d-=2;}break;case 1:g[i]=2;d=n(g);f=d-p;if(f>0){g[i]=8;d-=4;}break;case-1:g[i]=3;d=n(g);f=d-p;if(f>0){g[i]=7;d-=4;}break;case-3:g[i]=4;d=n(g);f=d-p;if(f>-3){g[i]=6;d-=2;}break;}p=d;}n(g);}static int n(int[] g){foreach(var i in g){Console.Write(i);}Console.WriteLine();var s=Console.ReadLine();var d=int.Parse(s);if(d<1) Environment.Exit(0);return d;}}}

Spero che C # in questo formato funzioni per te. È nella forma di un programma completo, quindi dovrebbe esserci un modo per compilare un eseguibile. Fammi sapere se c'è qualcosa che posso fare per renderlo più semplice. I byte fanno parte del punteggio anche se il tag Code Golf viene rimosso, quindi la mia presentazione ufficiale rimuove tutti gli spazi bianchi non necessari e i nomi utili. La mia spiegazione di seguito utilizzerà frammenti del codice non salvato:

Spiegazione

Questo programma utilizza un solo metodo di supporto:

static int newGuess(IEnumerable<int> guess)
        {
            foreach (var item in guess)
            {
                Console.Write(item);
            }
            Console.WriteLine();
            var distanceString = Console.ReadLine();
            var distance = int.Parse(distanceString);
            if (distance < 1) System.Environment.Exit(0);
            return distance;
        }

Questo scrive l'ipotesi di stdout, quindi legge la distanza dallo stdin. Inoltre termina immediatamente il programma se un'ipotesi è la combinazione esatta. Lo chiamo molto. Successivamente l'installazione iniziale:

var lengthString = Console.ReadLine();
int length = int.Parse(l);
var guess = new int[length];
var prevDistance = newGuess(guess);

Questo ottiene la lunghezza della combinazione, quindi inizia a indovinare con tutti gli 0. Successivamente, scorre ogni cifra in un forciclo.

guess[i] = 5;
var distance = newGuess(guess);
var difference = distance - prevDistance;
var switchVar = difference;
switch (switchVar)

Per ogni cifra, indovina 5, quindi decide il passaggio successivo in base a come la distanza è cambiata rispetto all'ipotesi precedente (dove quella cifra era 0).

case 5:
    guess[i] = 0;
    distance -= 5;
    break;

Se la distanza aumentava di 5, allora 0 era corretto per quella distanza. Riportare la cifra su 0. Modificare manualmente la distanza registrata impedisce un'ipotesi aggiuntiva.

case -5:
    break;

Se la distanza diminuisce di 5, allora 5 è la cifra corretta e passiamo immediatamente alla cifra successiva.

Dopo ciò le cose sono difficili. L'uso di 5e 0per le mie ipotesi iniziali significa che le rimanenti possibilità di differenza sono 3, 1, -1, -3con 2 possibilità per ciascuna, che richiedono 1 ipotesi aggiuntiva per distinguere. Ognuno di questi casi assume una forma simile

case 3:
    guess[i] = 1;
    distance = newGuess(guess);
    difference = distance - prevDistance;
    if (difference > 0)
    {
        guess[i] = 9;
        distance -= 2;
    }

Alcuni numeri cambiano, ma essenzialmente proviamo una delle due possibilità e controlliamo se la modifica è stata quella corretta per quella cifra. In caso contrario, l'altra cifra è corretta, quindi impostiamo quella cifra e regoliamo manualmente la differenza.

Questo metodo significa che dovremmo richiedere al massimo 1 ipotesi per gli 0 iniziali, 2 ipotesi per cifra e quindi 1 ipotesi finale per garantire che l'ultima cifra non passi.

Potrebbe essere difettoso, funziona fino a quando ho controllato manualmente ma non è una garanzia. Bug trovato e schiacciato grazie a Colera Su


L'ho provato e non ha funzionato quando la risposta è 37. L'output è: 00, 50, 30, 75, 75(sì, due 75s).
Colera,

La sostituzione <con >in ogni ifin switchsembra correggere l'errore. Se è quello che vuoi, il tuo punteggio è 182255*ln(617+50)=1185166.
Colera,

@ColeraSu Indeed! Devo aver fatto un errore nel trovare / sostituire quando accorciamo il codice. Ho apportato la correzione nel codice golf (la versione dettagliata aveva i confronti corretti).
Kamil Drakari,

2

Python 2 e 3: 175 byte, tentativi totali = 1005972, punteggio = 5448445

Questo programma richiede ceil (log (n)) * 10 tentativi per combinazione o ogni singola cifra richiede 10 tentativi (quindi, 333richiede 30 tentativi).

N=int(input());o=0
def c(a):
 print("0"*(N-len(str(a)))+str(a))
 return int(input())
for j in range(N):m={c(i):i for i in reversed(range(0,10**(j+1),10**j))};o+=m[min(m)]
c(o)

Grazie infinite a Colera Su per avermi aiutato con la funzionalità di input / output.

Versione Python di Challenge ( modificata da OP ).

Ho scritto una versione del codice di blocco all'interno di Python. Puoi andare avanti e usarlo se stai cercando di risolverlo in Python (come me). Quanto segue funziona in Python 2 e 3. Ha avuto molto più senso implementare il blocco come una classe su cui è possibile testare e ho deciso di creare una funzione generatore per indovinare gli input.

import sys

class Lock:
    def __init__(self, number):
        self.lock = str(number)
    def l(self): #lengthOfNumber
        return len(self.lock)
    def c(self, guess): #check a guess
        guess = str(guess)
        guess = "0" * (len(self.lock) - len(guess)) + guess
        difference = 0
        for i in range(len(self.lock)):
            d1 = abs(int(self.lock[i]) - int(guess[i]))
            d2 = 10 - d1
            difference += d1 if d1 < d2 else d2
        return difference

def masterLock():
    testdata = ["000","555","755","735","744","746"]
    for answer in testdata:
        yield Lock(answer)

class LockIO:
    def __init__(self):
        self.lock = int(input())
    def l(self):
        return self.lock
    def c(self, guess):
        guess = str(guess)
        guess = "0" * (self.lock - len(guess)) + guess
        print(guess)
        sys.stdout.flush()
        return int(input())

for l in masterLock():
    # Write your code here that tries to guess it
    #   When you're done testing you can unindent your code,
    #   replace "for l in masterLock():" with "l = LockIO()"
    #   and submit the code.
    # 
    # Examples:
    #  l.l()      # returns the length of the lock
    #  l.c("952") # returns the distance to the lock
    #  l.c(952)   #  number also works
    pass

Innanzitutto, mi dispiace per aver scritto la LockIOlezione in modo sbagliato. Ho inviato una richiesta di modifica. Secondo, penso che tu abbia letto male il criterio del punteggio. Il punteggio viene calcolato dai dati di test generati dal generatore, non dall'esempio (ho eseguito il programma e il numero totale è 1005972). Manca anche il registro naturale. In terzo luogo, ho specificato che è necessario fornire un programma completo, quindi è necessario includere anche la LockIOparte nel conteggio dei byte. Inoltre, è necessario emettere il risultato finale e anche questo viene conteggiato nel punteggio.
Colera,

@ColeraSu In che modo è correlata "la classe LockIO" qui? Inoltre, a cosa serve il secondo blocco di codice Python?
user202729

@ user202729 Locked masterLockè solo per comodità di test. LockIOè ciò che devi effettivamente inviare, poiché utilizza il formato I / O richiesto.
Colera,

@nfnneil Ho aggiunto un programma di esempio nel post principale. Ho anche inviato una richiesta di modifica per il tuo riferimento.
Colera,

@ColeraSu Mentre mi stavo addormentando ho capito cosa volevi dire e grazie amico. È stata una bella sfida.
Neil,

2

R , 277 byte (punteggio = 1175356) 258 byte, tentativi totali = 202999, punteggio = 1163205

f=file("stdin","r")
w=function(b=1,A=X){cat(A,"\n",sep="");if(b){b=scan(f,n=1)};b}
M=0:9
s1=c(-5,-3,-1,1,3,5,3,1,-1,-3)
s2=s1+rep(c(1,-1),,,5)
L=w(1,"")
X=S=rep(0,L)
v0=w()
for(i in 1:L){X[i]=5
v1=v0-w()
X[i]=4
v2=v0-w()
S[i]=M[s1==v1&s2==v2]
X=0*S}
b=w(0,S)

Provalo online!

Versione stdin-stdout, come richiesto dall'OP, nessuna piastra di cottura. Grazie a Colera Su per aver corretto un bug iniziale. Questa è una versione leggermente più corta di quella nei commenti.


Di seguito l'invio di TIO per l'esecuzione di una serie di test all'interno di TIO

R , 189 byte

M=0:9
s1=c(-5,-3,-1,1,3,5,3,1,-1,-3)
s2=c(-4,-2,0,2,4,4,2,0,-2,-4)
f=function(L){S=rep(0,L)
v0=v(S)
X=S
for(i in c(1:L)){X[i]=5
v1=v0-v(X)
X[i]=4
v2=v0-v(X)
S[i]=M[s1==v1&s2==v2]
X[i]=0}
S}

Provalo online!

Consideriamo un vettore di zeri come ipotesi iniziale. Chiamiamo V la distanza tra l'ipotesi attuale e la soluzione. Per ogni posizione devi solo controllare le modifiche in V quando sostituisci 0 con 5 e con 4. In effetti, le differenze tra la modifica di 0 con 5 sono elencate nel mio vettore s1. Le differenze tra la modifica di 0 con 4 sono elencate nel mio vettore s2. Come vedi questi due vettori mappano in modo univoco le cifre della soluzione.

Il numero totale di test è quindi 3 * L 2 * L + 1, dove L è la lunghezza del codice: un controllo iniziale contro tutti gli zeri, quindi due controlli per ogni cifra, uno contro 5 e uno contro 4.

Il miglioramento da un fattore 3 a un fattore 2 è stato ispirato dalla presentazione di Kamil Drakari.

Il resto della presentazione TIO è una piastra di caldaia per R. La presentazione TIO mostra il tempo di esecuzione e il numero totale di operazioni per 1000 corse con L = 1 ... 200, 5 replicati per ogni valore di L.


Ottengo Error in scan(n = 1) : scan() expected 'a real', got 'S=rep(0,L)'quando eseguo.
Colera,

1
Sembra che scan(file=file("stdin"),n=1)funzioni. Questo programma (uguale al tuo, I / O corretto) ottiene un punteggio di 202999*ln(277+50)=1175356.
Colera,

@ ColeraSu, potrei essermi perso qualcosa, ma ho pensato che202999*ln(258+50) != 202999*ln(277+50)
NofP,

Sembra che @ user202729 abbia fatto un refuso. Fisso.
Colera il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.