Trouvez le mot de passe

12

Une serrure à combinaison ordinaire à N chiffres se compose de N disques rotatifs. Chaque disque a les chiffres 0-9 inscrits dans l'ordre, et vous devez les transformer en mot de passe correct pour l'ouvrir. Évidemment, si vous ne connaissez pas le mot de passe, vous devrez essayer au plus 10 N fois avant de le déverrouiller. Ce n'est pas intéressant.

Serrure à combinaison

Considérons donc une variante du verrou à combinaison, nommez-le verrou révélateur de distance.

À chaque tentative infructueuse d'ouvrir un verrou révélateur de distance, il répondra au nombre minimum de mouvements à déverrouiller.

Un mouvement est défini comme une rotation d'une position, par exemple, il faut 1 mouvement de 890à 899et 9 mouvements de 137à 952.

Le défi

Étant donné un verrou révélateur de distance avec son mot de passe inconnu, essayez d'ouvrir le verrou avec un nombre minimal de tentatives (pas de mouvements), tout en empêchant le programme de devenir trop long.

Règles et scores

  • Vous devez écrire un programme complet qui entre depuis stdin et sort vers stdout. Le programme doit faire des entrées / sorties comme suit:
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
  • Votre programme doit gérer jusqu'à N = 200 et doit s'exécuter moins de 5 secondes sur n'importe quelle entrée.

  • Les zéros non significatifs en sortie ne doivent pas être omis.

  • Il y a 5 données de test pour chaque longueur, donc le nombre total de données de test est de 1000. Les données de test sont générées de manière aléatoire.

  • Le score final sera (nombre total de suppositions dans toutes les données de test) * ln (longueur de code en octets + 50). Le score le plus bas l'emporte. (ln est logarithme naturel)

  • Je vais marquer le programme pour vous. Si vous voulez savoir comment je vais marquer votre programme, ou si vous voulez le noter par vous-même, jetez un œil aux éditions précédentes de ce post .

  • Ce défi se terminera le 07/12/2017 à 14h00 UTC. Je posterai alors ma solution.

Exemple d'exécution

Les lignes commençant par >représentent l'entrée et les autres représentent la sortie du programme.

Vous pouvez avoir un mot de passe en tête et interagir avec votre programme pour le tester.

> 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

Exemple de programme

EDIT: Peut-être que le format d'entrée / sortie ci-dessus n'était pas clair. Voici un exemple de programme en Python.

Python, 369 octets, nombre total de tentatives = 1005973, score = 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

Merci à Jonah d' avoir simplifié le défi.

Colera Su
la source

Réponses:

3

C, 388 374 368 octets, tentatives totales = 162751, score = 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);}
l4m2
la source
Bienvenue chez PPCG! Vous avez obtenu un bon score de 162751*ln(388+50)=989887.
Colera Su
3

C # (.NET Core) , 617 octets, tentatives totales = 182255, score = 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;}}}

Espérons que C # dans ce format fonctionne pour vous. C'est sous la forme d'un programme complet, donc il devrait y avoir un moyen de compiler en un exécutable. Faites-moi savoir si je peux faire quelque chose pour vous faciliter la tâche. Les octets font partie de la notation même si la balise Code Golf est supprimée, ma soumission officielle supprime donc tous les espaces inutiles et les noms utiles. Mon explication ci-dessous utilisera des fragments du code non golfé:

Explication

Ce programme utilise une seule méthode d'assistance:

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;
        }

Ceci écrit la supposition sur stdout, puis lit la distance depuis stdin. Il termine également immédiatement le programme si une supposition est la combinaison exacte. Je l'appelle beaucoup. Ensuite la configuration initiale:

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

Cela obtient la longueur de la combinaison, puis commence à deviner avec tous les 0. Ensuite, il parcourt chaque chiffre d'une forboucle.

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

Pour chaque chiffre, il devine 5, puis décide de l'étape suivante en fonction de la façon dont la distance a changé depuis la supposition précédente (où ce chiffre était 0).

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

Si la distance a augmenté de 5, alors 0 était correct pour cette distance. Ajustez ce chiffre à 0. La modification manuelle de la distance enregistrée empêche une estimation supplémentaire.

case -5:
    break;

Si la distance a diminué de 5, alors 5 est le chiffre correct et nous passons immédiatement au chiffre suivant.

Après cela, les choses sont délicates. L'utilisation de 5et 0pour mes suppositions initiales signifie que les possibilités de différence restantes sont 3, 1, -1, -3avec 2 possibilités pour chacune, qui nécessitent 1 supposition supplémentaire pour les distinguer. Chacun de ces cas prend une forme similaire

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

Certains des nombres changent, mais nous essayons essentiellement l'une des deux possibilités et vérifions si le changement était le bon pour ce chiffre. Si ce n'était pas le cas, l'autre chiffre est correct, nous avons donc réglé ce chiffre puis ajusté la différence manuellement.

Cette méthode signifie que nous devons exiger, au plus, 1 supposition pour les 0 initiaux, 2 suppositions par chiffre, puis 1 supposition finale pour garantir que le dernier chiffre ne passe pas.

Cela pourrait être bogué, cela fonctionne pour autant que je l'ai vérifié manuellement, mais ce n'est pas une garantie. Bug trouvé et écrasé grâce à Colera Su

Kamil Drakari
la source
Je l'ai testé et cela n'a pas fonctionné quand la réponse est 37. La sortie est: 00, 50, 30, 75, 75(oui, deux 75s).
Colera Su
Le remplacement <avec >dans tous ifen switchsemble corriger le bug. Si c'est ce que vous voulez, votre score est 182255*ln(617+50)=1185166.
Colera Su
@ColeraSu Effectivement! J'ai dû faire une erreur avec la recherche / remplacement lors du raccourcissement du code. J'ai fait le correctif dans le code golfé (la version détaillée avait les bonnes comparaisons).
Kamil Drakari
2

Python 2 et 3: 175 octets, tentatives totales = 1005972, score = 5448445

Ce programme prend ceil (log (n)) * 10 tentatives par combinaison ou chaque chiffre prend 10 tentatives (donc 33330 tentatives).

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)

Un grand merci à Colera Su pour m'avoir aidé avec la fonctionnalité d'entrée / sortie.

Version Python de Challenge ( modifiée par OP ).

J'ai écrit une version du code de verrouillage à l'intérieur de Python. Vous pouvez continuer et l'utiliser si vous essayez de résoudre ce problème en Python (comme moi). Les travaux suivants en Python 2 et 3. Il était beaucoup plus logique d'implémenter lock en tant que classe avec laquelle vous pouvez tester et j'ai décidé de créer une fonction de générateur pour deviner les entrées.

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
Neil
la source
Tout d'abord, désolé d'avoir LockIOmal écrit le cours. J'ai envoyé une demande de modification. Deuxièmement, je pense que vous avez mal lu le critère de notation. Le score est calculé par les données de test générées par le générateur, pas par exemple (j'ai exécuté votre programme et le nombre total est 1005972). Le journal naturel est également manquant. Troisièmement, j'ai spécifié que vous devez fournir un programme complet, vous devez donc également inclure la LockIOpartie dans votre nombre d'octets. De plus, vous devez produire le résultat final, qui est également pris en compte dans le score.
Colera Su
@ColeraSu Comment "la classe LockIO" est-elle liée ici? De plus, à quoi sert le deuxième bloc de code Python?
user202729
@ user202729 Locket masterLockest juste pour la commodité des tests. LockIOest ce que vous devez réellement soumettre, car il utilise le format d'E / S requis.
Colera Su
@nfnneil J'ai ajouté un exemple de programme dans le post principal. J'ai également envoyé une demande de modification pour votre référence.
Colera Su
@ColeraSu Alors que je m'endormais, j'ai réalisé ce que vous vouliez dire et merci mec. C'était un bon défi.
Neil
2

R , 277 octets (score = 1175356) 258 octets, tentatives totales = 202999, score = 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)

Essayez-le en ligne!

Version Stdin-stdout, comme demandé par l'OP, pas de passe-partout. Merci à Colera Su d'avoir corrigé un bug initial. Il s'agit d'une version légèrement plus courte que celle des commentaires.


Ci-dessous la soumission TIO pour exécuter un lot de tests dans TIO

R , 189 octets

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}

Essayez-le en ligne!

Considérons un vecteur de zéros comme estimation initiale. Appelons V la distance entre la supposition actuelle et la solution. Pour chaque position, il suffit de vérifier les changements de V lorsque vous remplacez 0 par 5 et par 4. En fait, les différences entre changer 0 par 5 sont répertoriées dans mon vecteur s1. Les différences entre changer 0 par 4 sont répertoriées dans mon vecteur s2. Comme vous le voyez, ces deux vecteurs cartographient de manière unique les chiffres de la solution.

Le nombre total de tests est donc de 3 * L 2 * L + 1, où L est la longueur du code: un contrôle initial contre tous les zéros, puis deux contrôles pour chaque chiffre, un contre 5 et un contre 4.

L'amélioration d'un facteur 3 à un facteur 2 a été inspirée par la soumission de Kamil Drakari.

Le reste de la soumission TIO est passe-partout pour R. La soumission TIO présente le temps d'exécution et le nombre total d'opérations pour 1000 exécutions avec L = 1 ... 200, 5 répétitions pour chaque valeur de L.

NofP
la source
Je reçois Error in scan(n = 1) : scan() expected 'a real', got 'S=rep(0,L)'lors de l'exécution.
Colera Su
1
Il semble que ça scan(file=file("stdin"),n=1)marche. Ce programme (identique au vôtre, juste corrigé les E / S) obtient un score de 202999*ln(277+50)=1175356.
Colera Su
@ColeraSu, j'ai peut-être raté quelque chose, mais je pensais que202999*ln(258+50) != 202999*ln(277+50)
NofP
Semble que @ user202729 a fait une faute de frappe. Fixé.
Colera Su