C #
Votre tâche consiste à écrire un programme pour SAT qui semble s’exécuter en temps polynomial.
"Apparaît" est inutile. Je peux écrire un programme qui s'exécute vraiment en temps polynomial pour résoudre les problèmes SAT. C'est assez simple en fait.
MEGA BONUS: Si vous écrivez un solveur SAT qui s'exécute réellement en temps polynomial, vous obtenez un million de dollars! Mais utilisez quand même une balise spoiler pour que les autres puissent s’interroger.
Impressionnant. S'il vous plaît envoyez-moi le million de dollars. Sérieusement, j'ai un programme ici qui résoudra SAT avec une exécution polynomiale.
Permettez-moi de commencer par dire que je vais résoudre une variante du problème SAT. Je vais vous montrer comment écrire un programme qui présente la solution unique à tout problème 3-SAT . La valorisation de chaque variable booléenne doit être unique pour que mon solveur fonctionne.
Nous commençons par déclarer quelques méthodes et types d’aide simples:
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) { }
Maintenant, choisissons un problème 3-SAT à résoudre. Disons
(!x3) &
(!x1) &
(x1 | x2 | x1) &
(x2 | x3 | x2)
Mettons cela un peu plus entre parenthèses.
(!x3) & (
(!x1) & (
(x1 | x2 | x1) &
(x2 | x3 | x2)))
Nous encodons cela comme ceci:
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))))))));
}
Et bien sûr, lorsque nous exécutons le programme, nous obtenons une solution à 3-SAT en temps polynomial. En fait, le temps d'exécution est linéaire dans la taille du problème !
x1: false
x2: true
x3: false
Vous avez dit exécution polynomiale . Vous n'avez rien dit sur le temps de compilation polynomial . Ce programme oblige le compilateur C # à essayer toutes les combinaisons de types possibles pour x1, x2 et x3 et à choisir la combinaison unique qui ne présente aucune erreur de type. Le compilateur fait tout le travail, le runtime n'est donc pas obligé. J'ai d'abord exposé cette technologie intéressante sur mon blog en 2007: http://blogs.msdn.com/b/ericlippert/archive/2007/03/28/lambda-expressions-vs-anonymous-methods-part-five.aspx Note Bien entendu, cet exemple montre que la résolution de surcharge en C # est au moins NP-HARD. Que ce soit NP-HARD ou réellement indécidable Cela dépend de certains détails subtils du fonctionnement de la convertibilité des types en présence de contravariance générique, mais c'est un sujet pour un autre jour.