Generare un numero utilizzando un determinato elenco di numeri e operatori aritmetici


11

Ti viene dato un elenco di numeri L = [17, 5, 9, 17, 59, 14], un sacco di operatori O = {+:7, -:3, *:5, /:1}e un numero N = 569.

Compito

Emette un'equazione che utilizza tutti i numeri Lsul lato sinistro e solo il numero Nsul lato destro. Se ciò non è possibile, generare False. Soluzione di esempio:

59*(17-5)-9*17+14 = 569

Limitazioni e chiarimenti

  • Non puoi concatenare i numeri ( [13,37]non può essere usato come 1337)
  • Verranno visualizzati solo numeri naturali e zero L.
  • L'ordine in Lnon importa.
  • Devi usare tutti i numeri in L.
  • Solo gli operatori +, -, *, /appariranno O.
  • Opuò avere più operatori del necessario, ma almeno |L|-1operatori
  • È possibile utilizzare ciascun operatore un numero qualsiasi di volte fino al valore in O.
  • Tutte e quattro le operazioni in Osono le operazioni matematiche standard; in particolare, /è normale divisione con frazioni esatte.

Punti

  • Meno punti, meglio è
  • Ogni carattere del tuo codice ti dà un punto

Devi fornire una versione senza golf che sia di facile lettura.

sfondo

Una domanda simile è stata posta su Stack Overflow. Ho pensato che potesse essere un'interessante sfida per il golf del codice.

Complessità computazionale

Come ha detto Peter Taylor nei commenti, puoi risolvere la somma dei sottoinsiemi con questo:

  1. Hai un'istanza della somma del sottoinsieme (quindi un insieme S di numeri interi e un numero x)
  2. L: = S + [0, ..., 0] (| S | volte uno zero), N: = x, O: = {+: | S | -1, *: | S | - 1, /: 0, -: 0}
  3. Ora risolvi questa istanza del mio problema
  4. La soluzione per la somma dei sottoinsiemi è il numero di S che non viene moltiplicato per zero.

Se trovi un algoritmo migliore di O (2 ^ n), dimostri che P = NP. Poiché P vs NP è un problema del premio del millennio e quindi vale 1.000.000 di dollari USA, è molto improbabile che qualcuno trovi una soluzione per questo. Quindi ho rimosso questa parte della classifica.

Casi test

Le seguenti non sono le sole risposte valide, esistono altre soluzioni e sono consentite:

  • ( [17,5,9,17,59,14], {+:7, -:3, *:5, /:1}, 569)
    => 59 * (17-5)- 9 * 17 + 14 = 569
  • ( [2,2], {'+':3, '-':3, '*':3, '/':3}, 1)
    => 2/2 = 1
  • ( [2,3,5,7,10,0,0,0,0,0,0,0], {'+':20, '-':20, '*':20, '/':20}, 16)
    => 5+10-2*3+7+0+0+0+0+0+0+0 = 16
  • ( [2,3,5,7,10,0,0,0,0,0,0,0], {'+':20, '-':20, '*':20, '/':20}, 15)
    => 5+10+0*(2+3+7)+0+0+0+0+0+0 = 15

È m = |L|? Se sì, come puoi aspettarti che il runtime non dipenda dalle dimensioni di tale elenco? Ad esempio [2,2],[+,+,...,+,/],1,. Infatti, poiché n è O (m), potresti semplicemente scrivere tutto in termini di m.
stand,

3
Che tipo di aritmetica è questo da usare - frazioni esatte, numero intero ( /div), solo errori in virgola mobile e speranza per nessun arrotondamento, ...?
cessò di girare in senso antiorario il

4
Perché le complicate regole di punteggio per la complessità computazionale? C'è una facile riduzione dalla somma dei sottoinsiemi, quindi qualcosa di meglio di O (2 ^ n) vale un milione di dollari.
Peter Taylor,


1
Il terzo caso di prova non è falso ...5+10+2*3+7*0+0...
Shmiddty,

Risposte:


3

Python 2.7 / 478 caratteri

L=[17,5,9,17,59,14]
O={'+':7,'-':3,'*':5,'/':1}
N=569
P=eval("{'+l+y,'-l-y,'*l*y,'/l/y}".replace('l',"':lambda x,y:x"))
def S(R,T):
 if len(T)>1:
  c,d=y=T.pop();a,b=x=T.pop()
  for o in O:
   if O[o]>0 and(o!='/'or y[0]):
    T+=[(P[o](a, c),'('+b+o+d+')')];O[o]-=1
    if S(R,T):return 1
    O[o]+=1;T.pop()
  T+=[x,y]
 elif not R:
  v,r=T[0]
  if v==N:print r
  return v==N
 for x in R[:]:
  R.remove(x);T+=[x]
  if S(R,T):return 1
  T.pop();R+=[x]
S([(x,`x`)for x in L],[])

L'idea principale è quella di utilizzare la forma postfisso di un'espressione per la ricerca. Ad esempio, 2*(3+4)in formato postfix sarà 234+*. Quindi il problema diventa trovare una permutazione parziale di L+ Oche viene valutata N.

La seguente versione è la versione ungolfed. Lo stack stksembra [(5, '5'), (2, '5-3', (10, ((4+2)+(2*(4/2))))].

L = [17, 5, 9, 17, 59, 14]
O = {'+':7, '-':3, '*':5, '/':1} 
N = 569

P = {'+':lambda x,y:x+y,
     '-':lambda x,y:x-y,
     '*':lambda x,y:x*y,
     '/':lambda x,y:x/y}

def postfix_search(rest, stk):
    if len(stk) >= 2:
        y = (v2, r2) = stk.pop()
        x = (v1, r1) = stk.pop()
        for opr in O:
            if O[opr] > 0 and not (opr == '/' and v2 == 0):
                stk += [(P[opr](v1, v2), '('+r1+opr+r2+')')]
                O[opr] -= 1
                if postfix_search(rest, stk): return 1
                O[opr] += 1
                stk.pop()
        stk += [x, y]
    elif not rest:
        v, r = stk[0]
        if v == N: print(r)
        return v == N
    for x in list(rest):
        rest.remove(x)
        stk += [x]
        if postfix_search(rest, stk):
            return True
        stk.pop()
        rest += [x]
postfix_search(list(zip(L, map(str, L))), [])

1
Wow, è più corto di quanto mi aspettassi. Ho scarabocchiato un algoritmo che includeva un postfix di conversione <=> infix, ma il mio scarabocchio non era molto più breve della tua implementazione. Sorprendete. E grazie per la costruzione P[opr](v1, v2). Non ho mai pensato di combinare lambda e dizionari come questo, anche se adesso sembra ovvio.
Martin Thoma,

Ho provato a testare la tua soluzione con il mio quarto testcase. Dopo 2h, ho fermato l'esecuzione.
Martin Thoma,

@moose Proverò ad aggiungere un po 'di euristica per renderlo più veloce. Ma dopo ciò la lunghezza del codice potrebbe raddoppiare.
Ray,

L'uso di Fraction come ho fatto qui risolve un problema nella tua risposta. Provalo per la specifica istanza sul link che ho fornito. Il tuo codice attuale non trova una risposta, ma quando usi la frazione lo fa.
Martin Thoma,
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.