Diberikan simetris nyata matriks , adakah algoritma yang menghitung jumlah atas semua 1 \ leq i <j <k \ leq n dengan kompleksitas waktu yang lebih baik daripada O (n ^ 3) ?
Diberikan simetris nyata matriks , adakah algoritma yang menghitung jumlah atas semua 1 \ leq i <j <k \ leq n dengan kompleksitas waktu yang lebih baik daripada O (n ^ 3) ?
Jawaban:
Ada pendekatan yang cukup praktis yang bekerja dalam waktu , di mana adalah jumlah bit dalam kata prosesor. Gagasan utamanya adalah Anda mengulangi elemen-elemen dari matriks satu per satu dalam urutan yang meningkat (memutuskan hubungan secara sewenang-wenang) dan "mengaktifkannya". Pertimbangkan saat ketika elemen terbesar dari triple diaktifkan. Untuk mempermudah, mari kita asumsikan bahwa elemen tersebut adalah . Adalah wajar untuk menambahkan nilai rangkap tiga ke jawaban sekarang, ketika elemen terakhir diaktifkan. Jadi kita harus menghitung jumlah kemungkinan sehingga dansudah diaktifkan (itu akan menjadi jumlah tiga kali lipat, di sini adalah elemen terbesar, jadi mereka benar-benar diaktifkan sekarang). Di sini kita dapat mempercepat implementasi naif dengan menggunakan optimisasi bit.
Untuk detail, Anda bisa merujuk ke implementasi berikut di C ++ 11 yang seharusnya bekerja untuk , (tidak terlalu dioptimalkan; namun, masih mengalahkan penjumlahan naif untuk dengan margin besar setidaknya di mesin saya).
// code is not very elegant,
// but should be understandable
// here the matrix a has dimensions n x n
// a has to be symmetric!
int64_t solve (int n, const vector<vector<int32_t>> &a)
{
std::vector<boost::dynamic_bitset<int64_t>> mat
(n, boost::dynamic_bitset<int64_t>(n));
vector<pair<int, int>> order;
for (int j = 1; j < n; j++)
for (int i = 0; i < j; i++)
order.emplace_back(i, j);
sort(order.begin(), order.end(),
[&] (const pair<int, int> &l, const pair<int, int> &r)
{return a[l.first][l.second] < a[r.first][r.second];});
int64_t ans = 0;
for (const auto &position : order)
{
int i, j;
tie (i, j) = position;
mat[i][j] = mat[j][i] = 1;
// here it is important that conditions
// mat[i][i] = 0 and mat[j][j] = 0 always hold
ans += (mat[i] & mat[j]).count() * int64_t(a[i][j]);
}
return ans;
}
Jika Anda mempertimbangkan untuk menggunakan sedikit optimisasi kecurangan, Anda dapat menggunakan metode empat Rusia untuk hasil yang sama di sini, menghasilkan algoritma , yang seharusnya kurang praktis (karena cukup besar pada sebagian besar perangkat keras modern) tetapi secara teori lebih baik. Memang, mari kita pilih dan pertahankan setiap baris matriks sebagai larik integer dari hingga , di mana angka ke- dalam array berkorespondensi dengan bit baris mulai dari inklusif ke eksklusif dalam-indeksasi. Kami dapat menghitung ulang produk skalar dari setiap dua blok tersebut dalam waktu waktu. Memperbarui posisi dalam matriks itu cepat karena kami hanya mengubah satu bilangan bulat. Untuk menemukan produk skalar dari baris dan hanya mengulangi array yang sesuai dengan baris itu, cari produk skalar dari blok yang sesuai dalam tabel dan jumlah produk yang diperoleh.
Paragraf di atas mengasumsikan bahwa operasi dengan integer membutuhkan waktu. Ini adalah asumsi yang cukup umum , karena biasanya tidak benar-benar mengubah kecepatan komparatif dari algoritma (misalnya, jika kita tidak menggunakan asumsi itu, metode brute force sebenarnya bekerja dalam waktu (di sini kita mengukur waktu dalam operasi bit) jika mengambil nilai integer dengan nilai absolut setidaknya hingga untuk beberapa konstanta (dan jika tidak kita dapat menyelesaikan masalah dengan perkalian matriks tetap), namun metode empat Rusia menyarankan penggunaan di atas operasi dengan jumlah ukuran dalam kasus itu; sehingga membuat operasi bit , yang masih lebih baik daripada brute force meskipun ada perubahan model).
Namun, pertanyaan tentang keberadaan pendekatan masih menarik.
Teknik (optimisasi bit dan empat metode Rusia) yang disajikan dalam jawaban ini sama sekali tidak asli dan disajikan di sini untuk kelengkapan penjelasan. Namun, menemukan cara untuk menerapkannya tidaklah sepele.
mat[i]
mat[j]
mat
yang tampaknya penting. Saya mengerti bagaimana itu dapat didefinisikan, tetapi saya bertanya-tanya apakah (mat[i] & mat[j]).count()
akan bekerja seperti yang diinginkan dengan wadah STL.
mat
- saya kira kita harus menggunakan std::vector<boost::dynamic_bitset<int64_t>>
.
mat
: ya, sebenarnya saya sudah memikirkan bitet standar, tetapi boost::dynamic_bitset
bahkan lebih baik dalam hal ini, karena ukurannya tidak harus konstan waktu kompilasi. Akan mengedit jawaban untuk menambahkan detail ini dan untuk mengklarifikasi pendekatan empat Rusia.