Un recente post sul blog di puzzle sulla ricerca di tre equidistanti mi porta a una domanda stackoverflow con una risposta superiore che afferma di farlo in tempo O (n lg n). La parte interessante è che la soluzione prevede la quadratura di un polinomio, facendo riferimento a un documento che descrive come farlo in tempo O (n lg n) .
Ora, moltiplicare i polinomi è praticamente uguale a moltiplicare i numeri. L'unica vera differenza è la mancanza di riporti. Ma ... i carry possono anche essere eseguiti in tempo O (n lg n). Per esempio:
var value = 100; // = 0b1100100
var inputBitCount = value.BitCount(); // 7 (because 2^7 > 100 >= 2^6)
var n = inputBitCount * 2; // 14
var lgn = n.BitCount(); // 4 (because 2^4 > 14 => 2^3)
var c = lgn + 1; //5; enough space for 2n carries without overflowing
// do apparently O(n log n) polynomial multiplication
var p = ToPolynomialWhereBitsAreCoefficients(value); // x^6 + x^5 + x^2
var p2 = SquarePolynomialInNLogNUsingFFT(p); // x^12 + 2x^11 + 2x^10 + x^8 + 2x^7 + x^4
var s = CoefficientsOfPolynomial(p2); // [0,0,0,0,1,0,0,2,1,0,2,2,1]
// note: s takes O(n lg n) space to store (each value requires at most c-1 bits)
// propagate carries in O(n c) = O(n lg n) time
for (var i = 0; i < n; i++)
for (var j = 1; j < c; j++)
if (s[i].Bit(j))
s[i + j].IncrementInPlace();
// extract bits of result (in little endian order)
var r = new bool[n];
for (var i = 0; i < n; i++)
r[i] = s[i].Bit(0);
// r encodes 0b10011100010000 = 10000
Quindi la mia domanda è questa: dov'è l'errore, qui? Moltiplicare i numeri in O (n lg n) è un gigantesco problema aperto nell'informatica, e dubito davvero che la risposta sarebbe così semplice.
- Il trasporto è sbagliato o no O (n lg n)? Ho scoperto che lg n + 1 bit per valore è sufficiente per tracciare i carry e che l'algoritmo è così semplice che sarei sorpreso se fosse sbagliato. Si noti che, sebbene un singolo incremento possa richiedere tempo O (lg n), il costo aggregato per gli incrementi di x è O (x).
- L'algoritmo di moltiplicazione polinomiale del documento è errato o sono presenti condizioni che sto violando? L'articolo utilizza una rapida trasformata di Fourier invece di una trasformazione teorica numerica, che potrebbe essere un problema.
- Molte persone intelligenti hanno perso un'ovvia variante dell'algoritmo di Schönhage – Strassen da 40 anni? Questo sembra di gran lunga il meno probabile.
In realtà ho scritto codice per implementarlo, ad eccezione dell'efficiente moltiplicazione polinomiale (non capisco ancora abbastanza bene la trasformazione teorica dei numeri). Test casuali sembrano confermare che l'algoritmo è corretto, quindi il problema è probabilmente nell'analisi della complessità temporale.
x^10 + 2x^8
? x ^ 10 solo una volta (x ^ 5 * x ^ 5) e x ^ 8 due volte (x ^ 6 * x ^ 2 + x ^ 2 * x ^ 6)