Apa cara paling efisien yang diberikan untuk menaikkan bilangan bulat ke kekuatan bilangan bulat lain di C?
// 2^3
pow(2,3) == 8
// 5^5
pow(5,5) == 3125
c
algorithm
math
exponentiation
Doug T.
sumber
sumber
int
s aktual (dan bukan kelas int besar), banyak panggilan ke ipow akan meluap. Itu membuat saya bertanya-tanya apakah ada cara cerdas untuk pra-menghitung tabel dan mengurangi semua kombinasi yang tidak melimpah ke pencarian tabel sederhana. Ini akan membutuhkan lebih banyak memori daripada sebagian besar jawaban umum, tetapi mungkin lebih efisien dalam hal kecepatan.pow()
bukan fungsi yang amanJawaban:
Eksponen dengan mengkuadratkan.
Ini adalah metode standar untuk melakukan eksponensial modular untuk angka besar dalam kriptografi asimetris.
sumber
while (exp)
danif (exp & 1)
denganwhile (exp != 0)
danif ((exp & 1) != 0)
masing - masing.unsigned exp
, atau menangani negatifexp
dengan benar.n*n*n*n*n*n*n*n
menggunakan 7 perkalian. Algoritma ini sebagai gantinya menghitungm=n*n
, kemudiano=m*m
, kemudianp=o*o
, di manap
= n ^ 8, hanya dengan tiga perkalian. Dengan eksponen besar perbedaan dalam kinerja sangat signifikan.Perhatikan bahwa eksponensial dengan mengkuadratkan bukan metode yang paling optimal. Ini mungkin yang terbaik yang dapat Anda lakukan sebagai metode umum yang bekerja untuk semua nilai eksponen, tetapi untuk nilai eksponen tertentu mungkin ada urutan yang lebih baik yang membutuhkan lebih sedikit perkalian.
Misalnya, jika Anda ingin menghitung x ^ 15, metode eksponensial dengan mengkuadratkan akan memberi Anda:
Ini adalah total 6 perkalian.
Ternyata ini bisa dilakukan dengan menggunakan "hanya" 5 perkalian melalui eksponensial rantai penjumlahan .
Tidak ada algoritma yang efisien untuk menemukan urutan perkalian yang optimal ini. Dari Wikipedia :
sumber
Jika Anda perlu meningkatkan 2 ke daya. Cara tercepat untuk melakukannya adalah dengan sedikit menggeser kekuatan.
sumber
2 ** 0 == 1 << 0 == 1
Ini metode di Jawa
sumber
sumber
pow(1, -1)
tidak meninggalkan kisaran int meskipun eksponen negatif. Sekarang yang satu bekerja secara tidak sengaja, seperti halnyapow(-1, -1)
.Jika Anda ingin mendapatkan nilai integer untuk 2 dengan kekuatan sesuatu, selalu lebih baik menggunakan opsi shift:
pow(2,5)
dapat digantikan oleh1<<5
Ini jauh lebih efisien.
sumber
power()
berfungsi hanya untuk IntegerKompleksitas = O (log (exp))
power()
berfungsi untuk bekerja untuk basis exp dan float negatif .Kompleksitas = O (log (exp))
sumber
float
dalam blok kode kedua yang disajikan (pertimbangkan untuk menunjukkan bagaimana carapower(2.0, -3)
dikomputasi).negative exp and float base
solusinya? mengapa kita menggunakan temp, pisahkan exp dengan 2 dan periksa exp (datar / ganjil)? Terima kasih!Kasus yang sangat khusus adalah, ketika Anda perlu mengatakan 2 ^ (- x ke y), di mana x, tentu saja negatif dan y terlalu besar untuk melakukan pengalihan pada int. Anda masih dapat melakukan 2 ^ x dalam waktu konstan dengan mengacaukan float.
Anda bisa mendapatkan lebih banyak kekuatan 2 dengan menggunakan ganda sebagai tipe dasar. (Terima kasih banyak kepada para komentator karena telah membantu untuk memperbaiki posisi ini).
Ada juga kemungkinan bahwa mempelajari lebih lanjut tentang IEEE mengapung , kasus eksponensial khusus lainnya mungkin muncul dengan sendirinya.
sumber
Sama seperti tindak lanjut komentar tentang efisiensi eksponensial dengan mengkuadratkan.
Keuntungan dari pendekatan itu adalah berjalan dalam waktu log (n). Misalnya, jika Anda akan menghitung sesuatu yang besar, seperti x ^ 1048575 (2 ^ 20 - 1), Anda hanya perlu melalui loop 20 kali, bukan 1 juta + menggunakan pendekatan naif.
Juga, dalam hal kompleksitas kode, ini lebih sederhana daripada mencoba menemukan urutan perkalian yang paling optimal, saran ala Pramod.
Edit:
Saya kira saya harus mengklarifikasi sebelum seseorang menandai saya untuk potensi meluap. Pendekatan ini mengasumsikan bahwa Anda memiliki semacam perpustakaan bigint.
sumber
Terlambat ke pesta:
Di bawah ini adalah solusi yang juga menangani
y < 0
sebaik mungkin.intmax_t
untuk rentang maksimum. Tidak ada ketentuan untuk jawaban yang tidak cocokintmax_t
.powjii(0, 0) --> 1
yang merupakan hasil umum untuk kasus ini.pow(0,negative)
, hasil tidak terdefinisi lain, kembaliINTMAX_MAX
Kode ini menggunakan loop selamanya
for(;;)
untuk menghindari akhirbase *= base
umum dalam solusi looped lainnya. Multiplikasi itu 1) tidak diperlukan dan 2) bisaint*int
meluap yaitu UB.sumber
powjii(INT_MAX, 63)
menyebabkan UB dibase *= base
. Pertimbangkan untuk memeriksa apakah Anda dapat melipatgandakan, atau pindah ke yang tidak ditandatangani dan membiarkannya membungkus.exp
ditandatangani. Ini menyulitkan kode karena situasi aneh di mana(-1) ** (-N)
valid, dan apa punabs(base) > 1
akan0
untuk nilai negatifexp
, jadi lebih baik untuk tidak ditandatangani dan menyimpan kode itu.y
ditandatangani tidak benar-benar diperlukan dan membawa komplikasi yang Anda komentari, namun permintaan OP bersifat spesifikpow(int, int)
. Jadi komentar baik itu termasuk dalam pertanyaan OP. Karena OP belum menentukan apa yang harus dilakukan pada overflow, jawaban yang salah dengan baik hanya sedikit lebih baik dari UB. Diberi "cara paling efisien", saya ragu OP peduli tentang OF.solusi yang lebih umum mempertimbangkan eksponen negatif
sumber
pow(i, INT_MIN)
bisa menjadi loop tak terbatas.pow(i, INT_MIN)
bukan integer overflow. Penugasan hasil itutemp
pasti meluap, berpotensi menyebabkan akhir waktu , tetapi saya akan puas dengan nilai yang tampaknya acak. :-)Satu lagi implementasi (di Jawa). Mungkin bukan solusi yang paling efisien tetapi # iterasi sama dengan solusi Eksponensial.
sumber
Saya menggunakan rekursif, jika exp bahkan, 5 ^ 10 = 25 ^ 5.
sumber
Selain jawaban oleh Elias, yang menyebabkan Perilaku Tidak Terdefinisi ketika diimplementasikan dengan bilangan bulat yang ditandatangani, dan nilai yang salah untuk input tinggi ketika diimplementasikan dengan bilangan bulat yang tidak ditandatangani,
di sini adalah versi modifikasi dari Exponentiation by Squaring yang juga berfungsi dengan tipe integer yang ditandatangani, dan tidak memberikan nilai yang salah:
Pertimbangan untuk fungsi ini:
Jika terjadi overflow atau pembungkus,
return 0;
Saya menggunakan
int64_t
, tetapi lebar apa pun (ditandatangani atau tidak ditandatangani) dapat digunakan dengan sedikit modifikasi. Namun, jika Anda perlu menggunakan tipe integer lebar tidak-tetap, Anda perlu mengubahnyaSQRT_INT64_MAX
dengan(int)sqrt(INT_MAX)
(jika menggunakanint
) atau sesuatu yang serupa, yang harus dioptimalkan, tetapi lebih jelek, dan bukan ekspresi konstan C. Juga casting hasil darisqrt()
keint
tidak terlalu baik karena precisi floating point dalam kasus kuadrat sempurna, tetapi karena saya tidak tahu implementasi apa pun di manaINT_MAX
- atau maksimum jenis apa pun - adalah kuadrat sempurna, Anda dapat hidup dengan itu.sumber
Saya telah mengimplementasikan algoritma yang menghafal semua kekuatan yang dihitung dan kemudian menggunakannya ketika dibutuhkan. Jadi misalnya x ^ 13 sama dengan (x ^ 2) ^ 2 ^ 2 * x ^ 2 ^ 2 * x di mana x ^ 2 ^ 2 diambil dari tabel alih-alih menghitungnya sekali lagi. Ini pada dasarnya adalah implementasi dari jawaban @Pramod (tetapi dalam C #). Jumlah perkalian yang dibutuhkan adalah Ceil (Log n)
sumber
public
? 2 fungsi bernama sama? Ini adalah pertanyaan C.Kasus saya sedikit berbeda, saya mencoba membuat topeng dari kekuatan, tapi saya pikir saya akan membagikan solusi yang saya temukan.
Jelas, itu hanya berfungsi untuk kekuatan 2.
sumber
#define MASK(e) (((e) >= 64) ? -1 :( (1 << (e)) - 1))
, sehingga dapat dihitung pada waktu kompilasiJika Anda tahu eksponen (dan bilangan bulat) pada waktu kompilasi, Anda bisa menggunakan templat untuk membuka gulungannya. Ini dapat dibuat lebih efisien, tetapi saya ingin menunjukkan prinsip dasar di sini:
Kami mengakhiri rekursi menggunakan spesialisasi templat:
Eksponen perlu diketahui saat runtime,
sumber
(c != c++) == 1