Saya tahu bahwa algoritma Euclid adalah algoritma terbaik untuk mendapatkan GCD (pembagi umum yang hebat) dari daftar bilangan bulat positif. Namun dalam praktiknya Anda dapat membuat kode algoritma ini dengan berbagai cara. (Dalam kasus saya, saya memutuskan untuk menggunakan Java, tetapi C / C ++ mungkin merupakan pilihan lain).
Saya perlu menggunakan kode seefisien mungkin dalam program saya.
Dalam mode rekursif, Anda dapat menulis:
static long gcd (long a, long b){
a = Math.abs(a); b = Math.abs(b);
return (b==0) ? a : gcd(b, a%b);
}
Dan dalam mode berulang, tampilannya seperti ini:
static long gcd (long a, long b) {
long r, i;
while(b!=0){
r = a % b;
a = b;
b = r;
}
return a;
}
Ada juga algoritma Biner untuk GCD, yang mungkin dikodekan seperti ini:
int gcd (int a, int b)
{
while(b) b ^= a ^= b ^= a %= b;
return a;
}
algorithms
recursion
arithmetic
Jonaprieto
sumber
sumber
Jawaban:
Kedua algoritma Anda adalah setara (setidaknya untuk bilangan bulat positif, apa yang terjadi dengan bilangan bulat negatif dalam versi imperatif tergantung pada semantik JavaSebuahsaya bsaya saya
%
yang saya tidak hafal). Dalam versi rekursif, biarkan dan b i menjadi argumen dari saya th panggilan rekursif: a i + 1 = b i b i + 1 = a i m o d b iDalam versi imperatif, biarkan danb ' i menjadi nilai-nilai variabeldanpada awalith iterasi dari loop. a ′ i + 1 = b ′ i b ′ i + 1 = a ′ i m o d b ′ iSebuah′saya b′saya saya
a
b
Perhatikan kemiripan? Versi imperatif Anda dan versi rekursif Anda menghitung nilai yang persis sama. Selain itu, mereka berdua akhirnya pada saat yang sama, ketika (resp. A ' i = 0 ), sehingga mereka melakukan jumlah yang sama iterasi. Jadi secara algoritmik, tidak ada perbedaan di antara keduanya. Perbedaannya adalah masalah implementasi, sangat tergantung pada kompiler, perangkat keras yang dijalankannya, dan sangat mungkin sistem operasi dan program apa yang dijalankan secara bersamaan.Sebuahsaya= 0 Sebuah′saya= 0
Versi rekursif hanya membuat panggilan rekursif ekor . Sebagian besar kompiler untuk bahasa imperatif tidak mengoptimalkannya, sehingga kemungkinan kode yang mereka hasilkan akan menghabiskan sedikit waktu dan memori membangun kerangka tumpukan pada setiap iterasi. Dengan kompiler yang mengoptimalkan panggilan ekor (kompiler untuk bahasa fungsional hampir selalu melakukannya), kode mesin yang dihasilkan mungkin sama untuk keduanya (dengan asumsi Anda menyelaraskan panggilan tersebut dengan
abs
).sumber
Untuk angka yang kecil, algoritma GCD biner sudah cukup.
GMP, sebuah perpustakaan yang teruji dengan baik dan nyata, akan beralih ke setengah algoritma GCD khusus setelah melewati ambang khusus, generalisasi dari Algoritma Lehmer. Lehmer menggunakan perkalian matriks untuk meningkatkan algoritma Euclidian standar. Menurut dokumen, waktu berjalan asimtotik dari HGCD dan GCD adalah
O(M(N)*log(N))
, di manaM(N)
waktu untuk mengalikan dua nomor N-tungkai.Detail lengkap tentang algoritme mereka dapat ditemukan di sini .
sumber
Seperti yang saya tahu Java tidak mendukung optimasi rekursi ekor secara umum, tetapi Anda dapat menguji implementasi Java Anda untuk itu; jika tidak mendukungnya,
for
simplo-simpangan seharusnya lebih cepat, jika tidak rekursi harus sama cepatnya. Di sisi lain, ini adalah optimasi bit, pilih kode yang Anda pikir lebih mudah dan lebih mudah dibaca.Saya juga harus mencatat bahwa algoritma GCD tercepat bukan algoritma Euclid, algoritma Lehmer sedikit lebih cepat.
sumber
Pertama, jangan gunakan recursivity untuk mengganti loop ketat. Itu lambat. Jangan mengandalkan kompiler untuk mengoptimalkannya. Kedua, dalam kode Anda, Anda memanggil Math.abs () dalam setiap panggilan rekursif, yang tidak berguna.
Dalam loop Anda, Anda dapat dengan mudah menghindari variabel sementara dan bertukar a dan b sepanjang waktu.
Bertukar menggunakan a ^ = b ^ = a ^ = b membuat sumber lebih pendek tetapi membutuhkan banyak instruksi untuk dieksekusi. Ini akan lebih lambat daripada swap membosankan dengan variabel sementara.
sumber
Untuk angka kecil ,% adalah operasi yang cukup mahal, mungkin rekursif yang lebih sederhana
lebih cepat? (Maaf, kode Mathematica dan bukan C ++)
sumber
Algoritma Euclid paling efisien untuk menghitung GCD:
contoh:-
sumber