Нещодавнє повідомлення про блог із головоломки про пошук трьох рівномірно розміщених запитань привело мене до питання про стаковий перебіг із найвищою відповіддю, яка стверджує, що це потрібно зробити в O (n lg n). Цікава частина полягає в тому, що рішення включає в себе квадратичний поліном, посилання на документ, який описує, як це зробити за O (n lg n) час .
Тепер множення многочленів практично те саме, що множення чисел. Єдина реальна відмінність - відсутність носіїв. Але ... перенесення також може бути зроблено в O (n lg n) час. Наприклад:
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
Тож моє запитання таке: де помилка тут? Множення чисел на O (n lg n) - це гігантська відкрита проблема в інформатиці, і я дійсно дуже сумніваюся, що відповідь була б такою простою.
- Неправильне перенесення чи ні, O (n lg n)? Я розробив, що lg n + 1 біт на значення достатньо для відстеження переносів, і алгоритм настільки простий, я був би здивований, якщо він був неправильним. Зауважимо, що, хоча окремий приріст може зайняти час O (lg n), сукупна вартість для приросту x становить O (x).
- Чи неправильний алгоритм множення поліномів з паперу, чи є умови, які я порушую? У статті використовується швидке перетворення фур'є замість теоретичного перетворення числа, яке може бути проблемою.
- Чи багато розумних людей пропустили очевидний варіант алгоритму Шенхаге – Штрассена протягом 40 років? Це здається найменш вірогідним.
Я фактично написав код, щоб реалізувати це, за винятком ефективного множення поліномів (я ще не добре розумію теоретичне перетворення чисел). Випадкове тестування виявляється для підтвердження правильності алгоритму, тому проблема, ймовірно, в аналізі складності часу.
x^10 + 2x^8
? x ^ 10 лише один раз (x ^ 5 * x ^ 5), і x ^ 8 двічі (x ^ 6 * x ^ 2 + x ^ 2 * x ^ 6)