C #
Il tuo compito è quello di scrivere un programma per SAT che sembra essere eseguito in tempo polinomiale.
"Appare" non è necessario. Sono in grado di scrivere un programma che esegue davvero in tempo polinomiale per risolvere i problemi di SAT. Questo è abbastanza semplice in effetti.
MEGA BONUS: Se scrivi un solutore SAT che viene effettivamente eseguito in un tempo polinomiale, otterrai un milione di dollari! Ma ti preghiamo comunque di utilizzare un tag spoiler, in modo che altri possano chiederselo.
Eccezionale. Per favore, inviami il milione di dollari. Seriamente, ho un programma proprio qui che risolverà SAT con runtime polinomiale.
Vorrei iniziare affermando che risolverò una variazione del problema SAT. Ho intenzione di dimostrare come scrivere un programma che mostra la soluzione unica di qualsiasi problema 3-SAT . La valutazione di ciascuna variabile booleana deve essere unica affinché il mio solutore funzioni.
Iniziamo dichiarando alcuni semplici metodi e tipi di supporto:
class MainClass
{
class T { }
class F { }
delegate void DT(T t);
delegate void DF(F f);
static void M(string name, DT dt)
{
System.Console.WriteLine(name + ": true");
dt(new T());
}
static void M(string name, DF df)
{
System.Console.WriteLine(name + ": false");
df(new F());
}
static T Or(T a1, T a2, T a3) { return new T(); }
static T Or(T a1, T a2, F a3) { return new T(); }
static T Or(T a1, F a2, T a3) { return new T(); }
static T Or(T a1, F a2, F a3) { return new T(); }
static T Or(F a1, T a2, T a3) { return new T(); }
static T Or(F a1, T a2, F a3) { return new T(); }
static T Or(F a1, F a2, T a3) { return new T(); }
static F Or(F a1, F a2, F a3) { return new F(); }
static T And(T a1, T a2) { return new T(); }
static F And(T a1, F a2) { return new F(); }
static F And(F a1, T a2) { return new F(); }
static F And(F a1, F a2) { return new F(); }
static F Not(T a) { return new F(); }
static T Not(F a) { return new T(); }
static void MustBeT(T t) { }
Ora scegliamo un problema 3-SAT da risolvere. Diciamo
(!x3) &
(!x1) &
(x1 | x2 | x1) &
(x2 | x3 | x2)
Parliamoci un po 'di più.
(!x3) & (
(!x1) & (
(x1 | x2 | x1) &
(x2 | x3 | x2)))
Lo codifichiamo in questo modo:
static void Main()
{
M("x1", x1 => M("x2", x2 => M("x3", x3 => MustBeT(
And(
Not(x3),
And(
Not(x1),
And(
Or(x1, x2, x1),
Or(x2, x3, x2))))))));
}
E abbastanza sicuro quando eseguiamo il programma, otteniamo una soluzione a 3-SAT in tempo polinomiale. In effetti il tempo di esecuzione è lineare nella dimensione del problema !
x1: false
x2: true
x3: false
Hai detto runtime polinomiale . Non hai detto nulla sul tempo di compilazione polinomiale . Questo programma impone al compilatore C # di provare tutte le possibili combinazioni di tipi per x1, x2 e x3 e di scegliere quella unica che non presenta errori di tipo. Il compilatore fa tutto il lavoro, quindi il runtime non deve. Nel 2007 ho esposto per la prima volta questa interessante tecnica sul mio blog: http://blogs.msdn.com/b/ericlippert/archive/2007/03/28/lambda-expressions-vs-anonymous-methods-part-five.aspx Note questo ovviamente mostra che la risoluzione del sovraccarico in C # è almeno NP-HARD. Che si tratti di NP-HARD o in realtà indecidibile dipende da alcuni sottili dettagli su come funziona la convertibilità dei tipi in presenza di una contraddizione generica, ma questo è un argomento per un altro giorno.