Obviamente, P = NP [cerrado]

111

SAT es el problema de determinar si una expresión booleana puede hacerse realidad. Por ejemplo, (A) puede hacerse realidad estableciendo A = VERDADERO, pero (A &&! A) nunca puede ser verdad. Se sabe que este problema es NP-completo. Ver satisfacción booleana .

Su tarea es escribir un programa para SAT que se ejecute en tiempo polinómico, pero que no resuelva todos los casos.

Para algunos ejemplos, la razón por la que no es realmente polinomial podría ser porque:

  1. Hay un caso límite que no es obvio pero tiene un tiempo de ejecución deficiente
  2. El algoritmo en realidad no resuelve el problema en algún caso inesperado
  3. Algunas características del lenguaje de programación que está utilizando en realidad tienen un tiempo de ejecución más largo de lo que razonablemente esperaría que tuviera.
  4. Su código en realidad hace algo totalmente diferente de lo que parece que está haciendo.

Puede usar cualquier lenguaje de programación (o combinación de idiomas) que desee. No necesita proporcionar una prueba formal de la complejidad de su algoritmo, pero al menos debe proporcionar una explicación.

El criterio principal para juzgar debería ser qué tan convincente es el código.

Este es un concurso de popularidad, por lo que gana la respuesta mejor calificada en una semana.

Jonathan Pullano
fuente
11
Sería mejor si se restringe el dominio del problema, de lo contrario se invoca la nube de incertidumbre en torno a lo que es "conocido". ¿Por qué no elegir un solo problema NP-hard y centrarse en eso? Eso tiene la ventaja de dejar otros problemas abiertos a futuras preguntas en la misma línea. Varias preguntas limitadas pueden proporcionar mucho más deleite y entretenimiento al sitio que una amplia.
Jonathan Van Matre
99
@ gnasher729: obtuve el compilador de C # para resolver un problema SAT; Considero que es un logro razonablemente interesante.
Eric Lippert
99
Sería divertido si alguien resuelve accidentalmente SAT en tiempo polinómico aquí.
Turion
55
@Turion décadas de investigación, millones en recompensas y premios y todas las mujeres y la fama que uno podría tener, pero la verdadera motivación para resolver P = NP terminará siendo este desafío PCG.
Nada imposible el
3
Estoy votando para cerrar esta pregunta como fuera de tema porque los desafíos poco claros ya no son bienvenidos en este sitio. meta.codegolf.stackexchange.com/a/8326/20469
gato

Respuestas:

236

C#

Su tarea es escribir un programa para SAT que parezca ejecutarse en tiempo polinómico.

"Aparece" es innecesario. Puedo escribir un programa que realmente se ejecute en tiempo polinómico para resolver problemas SAT. Esto es bastante sencillo de hecho.

BONIFICACIÓN MEGA: Si escribe un solucionador SAT que realmente se ejecuta en tiempo polinómico, ¡obtendrá un millón de dólares! Pero de todos modos, use una etiqueta de spoiler para que otros puedan preguntarse al respecto.

Increíble. Por favor envíeme el millón de dólares. En serio, tengo un programa aquí que resolverá SAT con tiempo de ejecución polinómico.

Permítanme comenzar diciendo que voy a resolver una variación del problema SAT. Voy a demostrar cómo escribir un programa que exhiba la solución única de cualquier problema de 3-SAT . La valoración de cada variable booleana debe ser única para que mi solucionador funcione.

Comenzamos declarando algunos métodos y tipos de ayuda 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) { }

Ahora escojamos un problema de 3 SAT para resolver. Digamos

(!x3) & 
(!x1) & 
(x1 | x2 | x1) & 
(x2 | x3 | x2)

Hagamos un paréntesis de eso un poco más.

(!x3) & (
    (!x1) & (
        (x1 | x2 | x1) & 
        (x2 | x3 | x2)))

Codificamos eso así:

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

Y, efectivamente, cuando ejecutamos el programa, obtenemos una solución para 3-SAT en tiempo polinómico. De hecho, el tiempo de ejecución es lineal en el tamaño del problema .

x1: false
x2: true
x3: false

Dijiste tiempo de ejecución polinomial . No dijiste nada sobre el tiempo de compilación polinomial . Este programa obliga al compilador de C # a probar todas las combinaciones de tipos posibles para x1, x2 y x3, y elegir la única que no presente errores de tipo. El compilador hace todo el trabajo, por lo que el tiempo de ejecución no tiene que hacerlo. Primero exhibí esta interesante técnica en mi blog en 2007: http://blogs.msdn.com/b/ericlippert/archive/2007/03/28/lambda-expressions-vs-anonymous-methods-part-five.aspx Nota que, por supuesto, este ejemplo muestra que la resolución de sobrecarga en C # es al menos NP-HARD. Si es NP-HARD o realmente indecidible depende de ciertos detalles sutiles sobre cómo funciona la convertibilidad de tipos en presencia de contravarianza genérica, pero ese es un tema para otro día.

Eric Lippert
fuente
95
Tendrá que ponerse en contacto con el instituto de matemáticas de arcilla para obtener su millón de dólares. Pero no estoy seguro de que estén satisfechos .
Jonathan Pullano
15
Por supuesto, cualquier problema SAT puede transformarse en un problema 3-SAT equivalente, por lo que esta restricción es simplemente un inconveniente. El problema más irritante con mi "solución" es que requiere que el problema tenga una solución única . Si no hay solución o hay más de una solución, el compilador da un error.
Eric Lippert
11
@EricLippert el requisito de unicidad está bien. Siempre puede reducir SAT a SAT único (SAT, pero suponiendo que las entradas tengan asignaciones 0 o 1) utilizando una reducción aleatoria de tiempo polinómica. Palabras clave: Lema de aislamiento, teorema de Valiant-Vazirani.
Diego de Estrada
44
"En serio, tengo un programa aquí que resolverá SAT con tiempo de ejecución polinomial". - Yo también, pero desafortunadamente no cabe en este cuadro de comentarios.
CompuChip
11
@ Kobi: Sí, esa es la broma.
Eric Lippert
166

Multi-idioma (1 byte)

El siguiente programa, válido en muchos idiomas, principalmente funcional y esotérico, dará la respuesta correcta para una gran cantidad de problemas SAT y tiene una complejidad constante (!!!):

0

Sorprendentemente, el próximo programa dará la respuesta correcta para todos los problemas restantes y tiene la misma complejidad. ¡Entonces solo necesita elegir el programa correcto, y tendrá la respuesta correcta en todos los casos!

1
Mau
fuente
66
Esto es increíble. Me reí mucho.
Karl Damgaard Asmussen
2
Absolutamente jodidamente genial!
The Blue Dog
78
Hmm Ahora es fácil. ¡Todo lo que necesito hacer es escribir un programa que elija el programa correcto!
Cruncher
Precisamente! :-)
Mau
66
Con reminiscencias de xkcd.com/221 .
msh210
34

JavaScript

¡Al usar el no determinismo iterado, SAT se puede resolver en tiempo polinómico!

function isSatisfiable(bools, expr) {
    function verify() {
        var values = {};
        for(var i = 0; i < bools.length; i++) {
            values[bools[i]] = nonDeterministicValue();
        }
        with(values) {
            return eval(expr);
        }
    }
    function nonDeterministicValue() {
        return Math.random() < 0.5 ? !0 : !1;
    }

    for(var i = 0; i < 1000; i++) {
        if(verify(bools, expr)) return true;
    }
    return false;
}

Ejemplo de uso:

isSatisfiable(["a", "b"], "a && !a || b && !b") //returns 'false'

Este algoritmo solo verifica la fórmula booleana dada mil veces con entradas aleatorias. Casi siempre funciona para entradas pequeñas, pero es menos confiable cuando se introducen más variables.

Por cierto, estoy orgulloso de haber tenido la oportunidad de utilizar dos de las funciones menos utilizadas de JavaScript, una al lado de la otra: evaly with.

Peter Olson
fuente
44
Este es realmente un método de prueba bien establecido. La biblioteca QuickCheck de Haskell comenzó toda la diversión, creo. Desde entonces se ha vuelto a implementar en muchos idiomas.
John Tyree
44
Creo que debe tenerse en cuenta que, es menos probable que este programa devuelva la respuesta correcta, cuanto mayor sea la expresión sat. El 1000en el bucle for debería escalar de alguna manera con el tamaño de entrada (alguna escala polinómica no-O (1)).
Cruncher
2
@Cruncher Para ser más precisos, cuanto mayor sea el número de variables, es menos probable que devuelva la respuesta correcta. (por ejemplo, una expresión muy larga con una sola variable casi siempre devolverá la respuesta correcta)
Peter Olson
2
@TimSeguine Admito que mi uso de la palabra "no determinista" en este contexto es dudoso en el mejor de los casos, al igual que la afirmación de que SAT se puede resolver en tiempo polinómico. Sé que no es correcto, es solo parte del juego del engaño.
Peter Olson
44
@PaulDraper y luego llamarlos infrautilizados! Me reí mucho!
Rob
32

Matemática + Computación Cuántica

Es posible que no sepa que Mathematica viene con una computadora cuántica a bordo

Needs["Quantum`Computing`"];

La conmutación adiabática cuántica codifica un problema a resolver en un hamiltoniano (operador de energía) de tal manera que su estado de energía mínima ("estado fundamental") represente la solución. Por lo tanto, la evolución adiabática de un sistema cuántico al estado fundamental del hamiltoniano y la medición posterior da la solución al problema.

Definimos un subhamiltoniano que corresponde a ||partes de la expresión, con la combinación apropiada de operadores de Pauli para variables y su negación.

ingrese la descripción de la imagen aquí

Donde para una expresión como esta

expr = (! x3) && (! x1) && (x1 || x2 || x1) && (x2 || x3 || x2);

el argumento debería verse así

{{{1, x3}}, {{1, x1}}, {{0, x1}, {0, x2}, {0, x1}}, {{0, x2}, {0, x3}, {0, x2}}}

Aquí está el código para construir dicho argumento a partir de la expresión bool:

arg = expr /. {And -> List, Or -> List, x_Symbol :> {0, x}, 
    Not[x_Symbol] :> {1, x}};
If[Depth[arg] == 3, arg = {arg}];
arg = If[Depth[#] == 2, {#}, #] & /@ arg

Ahora construimos un Hamiltoniano completo, resumiendo los subhamiltonianos (la suma corresponde a &&partes de la expresión)

H = h /@ arg /. List -> Plus;

Y busca el estado de energía más bajo

QuantumEigensystemForm[H, -1]

ingrese la descripción de la imagen aquí

Si tenemos un valor propio de cero, entonces el vector propio es la solución

expr /. {x1 -> False, x2 -> True, x3 -> False}
> True

Desafortunadamente, el sitio oficial para el complemento "Quantum Computing" no está activo y no puedo encontrar un lugar para descargarlo, todavía lo tengo instalado en mi computadora. El complemento también tiene una solución documentada para el problema SAT, en el que basé mi código.

silbido
fuente
19
No tengo idea de cómo funciona esta respuesta. +1
Jonathan Pullano
55
@XiaogeSu "Naturalmente".
swish
3
@XiaogeSu Evolución determinada por el hamiltoniano, y naturalmente evoluciona a la energía más baja. Entonces, conociendo el espectro, podemos asumir que el sistema terminará en el estado fundamental.
swish
3
@XiaogeSu para ir al estado fundamental, también es necesario tener una interacción con el entorno que deexcita los estados superiores, tienes razón. La idea aquí es que esta interacción es muy pequeña, "adiabática".
Turion
3
La informática adiabática de QM tiene muchas similitudes con el recocido simulado clásico . ahora implementado por Dwave . es similar a un sistema de temperatura / energía de "enfriamiento" que "encuentra / se instala" en los mínimos locales .
vzn
27

Aquí hay tres enfoques, todos implican una reducción de SAT en su lingua franca geométrica 2D: rompecabezas de lógica de nonogramas. Las celdas en el rompecabezas lógico corresponden a variables SAT, restricciones a cláusulas.

Para obtener una explicación completa (¡y para revisar mi código en busca de errores!), Ya publiqué algunas ideas sobre los patrones dentro del espacio de solución de nonogramas. Ver https://codereview.stackexchange.com/questions/43770/nonogram-puzzle-solution-space. Enumerar> 4 mil millones de soluciones de rompecabezas y codificarlas para que encajen en una tabla de verdad muestra patrones fractales: auto-similitud y especialmente autoafinidad. Esta redundancia afín demuestra la estructura dentro del problema, explotable para reducir los recursos computacionales necesarios para generar soluciones. También muestra la necesidad de retroalimentación caótica dentro de cualquier algoritmo exitoso. Existe un poder explicativo en el comportamiento de transición de fase donde las instancias "fáciles" son aquellas que se encuentran a lo largo de la estructura gruesa, mientras que las instancias "duras" requieren una mayor iteración en detalles finos, bastante ocultas de la heurística normal. Si desea ampliar la esquina de esta imagen infinita (todas las instancias de rompecabezas <= 4x4 codificadas) consulte http://re-curse.github.io/visualizing-intractability/nonograms_zoom/nonograms.html

Método 1. Extrapolar la sombra espacial de la solución de nonogramas utilizando mapas caóticos y aprendizaje automático (piense en funciones de ajuste similares a las que generan el conjunto de Mandelbrot).

http://i.stack.imgur.com/X7SbP.png

Aquí hay una prueba visual de inducción. Si puede escanear estas cuatro imágenes de izquierda a derecha y piensa que tiene una buena idea para generar la quinta falta ... etc ... sexta imágenes, entonces me acaba de programar que como mi oráculo NP para el problema de decisión de la solución nonogram existencia. Da un paso adelante para reclamar tu premio como la supercomputadora más poderosa del mundo. Te alimentaré con descargas eléctricas de vez en cuando mientras el mundo te agradece por tus contribuciones computacionales.

Método 2. Utilice transformadas de Fourier en la versión de imagen booleana de las entradas. Los FFT proporcionan información global sobre frecuencia y posición dentro de una instancia. Si bien la porción de magnitud debe ser similar entre el par de entrada, su información de fase es completamente diferente, ya que contiene información direccionalizada sobre una proyección de solución a lo largo de un eje específico. Si es lo suficientemente inteligente, puede reconstruir la imagen de fase de la solución a través de una superposición especial de las imágenes de fase de entrada. Luego, invierta la transformación de la fase y la magnitud común de vuelta al dominio del tiempo de la solución.

¿Qué podría explicar este método? Hay muchas permutaciones de las imágenes booleanas con relleno flexible entre ejecuciones contiguas. Esto permite un mapeo entre la entrada -> solución cuidando la multiplicidad mientras conserva la propiedad de FFT de mapeos bidireccionales y únicos entre el dominio del tiempo <-> (frecuencia, fase). También significa que no existe tal cosa como "no hay solución". Lo que diría es que, en un caso continuo, existen soluciones en escala de grises que no está considerando al mirar la imagen de dos niveles de la resolución tradicional de rompecabezas de nonogramas.

¿Por qué no lo harías? Es una forma horrible de calcular, ya que las FFT en el mundo de punto flotante de hoy en día serían muy inexactas con grandes instancias. La precisión es un gran problema, y ​​la reconstrucción de imágenes a partir de imágenes cuantificadas de magnitud y fase generalmente crea soluciones muy aproximadas, aunque tal vez no sea visualmente para los umbrales del ojo humano. También es muy difícil llegar a este negocio de superposición, ya que actualmente se desconoce el tipo de función que lo realiza. ¿Sería un simple esquema de promedio? Probablemente no, y no hay un método de búsqueda específico para encontrarlo, excepto la intuición.

Método 3. Encuentre una regla de autómatas celulares (de un posible ~ 4 mil millones de tablas de reglas para las reglas de 2 estados de von Neumann) que resuelve una versión simétrica del rompecabezas de nonogramas. Utiliza una incrustación directa del problema en las celdas, como se muestra aquí. Nonogramas simétricos conservadores

Este es probablemente el método más elegante, en términos de simplicidad y buenos efectos para el futuro de la informática. La existencia de esta regla no está probada, pero tengo el presentimiento de que existe. Este es el por qué:

Los nonogramas requieren muchos comentarios caóticos en el algoritmo para ser resueltos exactamente. Esto se establece mediante el código de fuerza bruta vinculado a la Revisión de código. CA es el lenguaje más capaz para programar comentarios caóticos.

Se ve bien, visualmente. La regla evolucionaría a través de una incrustación, propagaría la información horizontal y verticalmente, interferiría y luego se estabilizaría en una solución que conservara el número de celdas establecidas. Esta ruta de propagación sigue el camino (hacia atrás) en el que normalmente piensa cuando proyecta la sombra de un objeto físico en la configuración original. Los nonogramas se derivan de un caso especial de tomografía discreta, así que imagínese sentado al mismo tiempo en dos escáneres de CT acorralados por gatitos ... así es como se propagarían los rayos X para generar las imágenes médicas. Por supuesto, hay problemas de límites: los bordes del universo CA no pueden seguir propagando información más allá de los límites, a menos que permita un universo toroidal. Esto también arroja el rompecabezas como un problema periódico de valor límite.

Explica múltiples soluciones como estados transitorios en un efecto oscilante continuo entre el intercambio de salidas como entradas, y viceversa. Explica instancias que no tienen solución como configuraciones originales que no conservan el número de celdas establecidas. Dependiendo del resultado real de encontrar dicha regla, incluso puede aproximarse a instancias insolubles con una solución cercana donde se conservan los estados celulares .

Comunidad
fuente
2
1 por dejarme decir "¿Por qué no me parece?" : P
Navin
¡Eres Stephen Wolfram y reclamo mis cinco libras!
Quuxplusone
44
Esta respuesta realmente merece más crédito, ya que es el mejor intento de hacer un programa convincente . Buen espectaculo.
Jonathan Pullano
10

C ++

Aquí hay una solución que se garantiza que se ejecutará en tiempo polinómico: se ejecuta en O(n^k)donde nestá el número de booleanos y kes una constante de su elección.

Es heurísticamente correcto, lo que creo que habla CS porque "da la respuesta correcta la mayor parte del tiempo, con un poco de suerte" (y, en este caso, un valor apropiadamente grande de k- editar realmente se me ocurrió que para cualquier narreglo que pueda establecer de kmodo que n^k > 2^n, ¿es trampa?).

#include <iostream>  
#include <cstdlib>   
#include <time.h>    
#include <cmath>     
#include <vector>    

using std::cout;     
using std::endl;     
typedef std::vector<bool> zork;

// INPUT HERE:

const int n = 3; // Number of bits
const int k = 4; // Runtime order O(n^k)

bool input_expression(const zork& x)
{
  return 
  (!x[2]) && (
    (!x[0]) && (
      (x[0] || x[1] || x[0]) &&
      (x[1] || x[2] || x[1])));
}

// MAGIC HAPPENS BELOW:    

 void whatever_you_do(const zork& minefield)
;void always_bring_a_towel(int value, zork* minefield);

int main()
{
  const int forty_two = (int)pow(2, n) + 1;
  int edition = (int)pow(n, k);
  srand(time(6["times7"]));

  zork dont_panic(n);
  while(--edition)
  {
    int sperm_whale = rand() % forty_two;
    always_bring_a_towel(sperm_whale, &dont_panic);

    if(input_expression(dont_panic))
    {
      cout << "Satisfiable: " << endl;
      whatever_you_do(dont_panic);
      return 0;
    }
  }

  cout << "Not satisfiable?" << endl;
  return 0;
}
void always_bring_a_towel(int value, zork* minefield)
{
  for(int j = 0; j < n; ++j, value >>= 1)
  {
    (*minefield)[j] = (value & 1);
  }
}

void whatever_you_do(const zork& minefield)
{
  for(int j = 0; j < n; ++j) 
  {
    cout << (char)('A' + j) << " = " << minefield[j] << endl;
  }
}
CompuChip
fuente
Buena respuesta. Pondría la explicación en una etiqueta de spoiler para que la gente pueda mirarla y rascarse un poco la cabeza.
Jonathan Pullano
Gracias por la sugerencia @JonathanPullano, agregué una etiqueta de spoiler y ofusqué un poco el código.
CompuChip
Por cierto, me acabo de enterar bitfield, tal vez hubiera preferido eso std::vector.
CompuChip
3
+1 por la ofuscación creativa y las referencias del autoestopista
Blake Miller
2
Sí, por supuesto, eso es trampa, si k depende de n, no es una constante :-)
RemcoGerlich
3

ruby / gnuplot superficie 3d

(¡oh, dura competencia!) ... de todos modos ... ¿una imagen vale más que mil palabras? Estas son 3 gráficas de superficie separadas hechas en gnuplot del punto de transición SAT. los ejes (x, y) son cláusula y recuento variable y la altura z es el número total de llamadas recursivas en el solucionador. Código escrito en rubí. Muestra 10x10 puntos a 100 muestras cada uno. Demuestra / usa principios básicos de estadística y es una simulación de Monte Carlo .

es básicamente un algoritmo de davis putnam que se ejecuta en instancias aleatorias generadas en formato DIMACS. Este es el tipo de ejercicio que idealmente se realizaría en las clases de CS en todo el mundo para que los estudiantes puedan aprender los conceptos básicos, pero casi no se les enseña específicamente ... ¿tal vez alguna razón por la que hay tantas pruebas falsas de P? = NP ? ni siquiera hay un buen artículo de wikipedia que describa el fenómeno del punto de transición (¿hay quienes lo tomen?) que es un tema muy destacado en física estadística y también es clave en CS. [a] [b] hay muchos artículos en CS sobre el punto de transición ¡Sin embargo, muy pocos parecen mostrar gráficos de superficie! (en su lugar, generalmente muestra rodajas en 2D).

El aumento exponencial en el tiempo de ejecución es claramente evidente en la primera parcela. El sillín que corre por el medio de la primera parcela es el punto de transición. los 2 nd y 3 rd gráficos muestran la transición satisfiable%.

[a] comportamiento de transición de fase en CS ppt Toby Walsh
[b] probabilidad empírica de satisfacción de k-SAT tcs.se
[c] grandes momentos en matemática empírica / experimental / (T) CS / SAT , blog TMachine

ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí

P =? NP QED!

#!/usr/bin/ruby1.8

def makeformula(clauses)
    (1..clauses).map \
    {
            vars2 = $vars.dup
            (1..3).map { vars2.delete_at(rand(vars2.size)) * [-1, 1][rand(2)] }.sort_by { |x| x.abs }
    }

end

def solve(vars, formula, assign)

    $counter += 1
    vars2 = []
    formula.each { |x| vars2 |= x.map { |y| y.abs } }
    vars &= vars2

    return [false] if (vars.empty?)
    v = vars.shift
    [v, -v].each \
    {
            |v2|
            f2 = formula.map { |x| x.dup }
            f2.delete_if \
            {
                    |x|
                    x.delete(-v2)
                    return [false] if (x.empty?)
                    x.member?(v2)
            }
            return [true, assign + [v2]] if (f2.empty?)
            soln = solve(vars.dup, f2, assign + [v2])
            return soln if (soln[0])
    }
    return [false]
end

def solve2(formula)
    $counter = 0
    soln = solve($vars.dup, formula, [])
    return [$counter, {false => 0, true => 1}[soln[0]]]
end


c1 = 10
c2 = 100
nlo, nhi = [3, 10]
mlo, mhi = [1, 50]
c1.times \
{
    |n|
    c1.times \
    {
            |m|
            p1 = nlo + n.to_f / c1 * (nhi - nlo)
            p2 = mlo + m.to_f / c1 * (mhi - mlo)
            $vars = (1..p1.to_i).to_a
            z1 = 0
            z2 = 0
            c2.times \
            {
                    f = makeformula(p2.to_i)
                    x = solve2(f.dup)
                    z1 += x[0]
                    z2 += x[1]
            }
#           p([p1, p2, z1.to_f / c2, z2.to_f / c2]) # raw
#           p(z1.to_f / c2)                         # fig1
#           p(0.5 - (z2.to_f / c2 - 0.5).abs)       # fig2
            p(z2.to_f / c2)                         # fig3
    }
    puts
}
vzn
fuente
2
Me alegra que hayas aportado esta respuesta. En cualquier prueba exitosa de P versus NP (de cualquier manera) es uno de los muchos requisitos para el poder predictivo. Gracias por señalar su importancia. :)
más reflexiones sobre P vs NP , muchos superiores / refs recogidos, etc
VZN