Jadi saya mencoba menulis angka ke- n dalam deret Fibonacci dalam fungsi sesingkat mungkin:
public uint fibn ( uint N )
{
return (N == 0 || N == 1) ? 1 : fibn(N-1) + fibn(N-2);
}
Tapi saya bertanya-tanya apakah saya bisa membuat ini lebih ringkas dan efisien dengan mengubahnya
(N == 0 || N == 1)
menjadi satu perbandingan. Apakah ada operasi bit shift yang bagus yang dapat melakukan ini?
c#
algorithm
optimization
arithmetic-expressions
pengguna6048670
sumber
sumber
fibn(N-1) + fibn(N-2)
bukanN * fibn(N-1)
?Jawaban:
Yang ini juga berfungsi
akar kuadrat dari 0 dan 1 akan mengembalikan 0 dan 1 masing-masing.
sumber
Math.Sqrt
adalah fungsi floating-point yang rumit. Ini berjalan lambat dibandingkan dengan alternatif hanya integer !!Ada beberapa cara untuk menerapkan tes aritmatika Anda menggunakan aritmatika bitwise. Ekspresi Anda:
x == 0 || x == 1
secara logis setara dengan masing-masing berikut ini:
(x & 1) == x
(x & ~1) == 0
(x | 1) == 1
(~x | 1) == (uint)-1
x >> 1 == 0
Bonus:
x * x == x
(buktinya membutuhkan sedikit usaha)Tetapi secara praktis, formulir ini adalah yang paling mudah dibaca, dan perbedaan kecil dalam performa tidak terlalu berguna jika menggunakan aritmatika bitwise:
x == 0 || x == 1
x <= 1
(karenax
adalah bilangan bulat yang tidak bertanda tangan)x < 2
(karenax
adalah bilangan bulat yang tidak bertanda tangan)sumber
(x & ~1) == 0
x == 0 || x == 1
daripada untuk(x & ~1) == 0
atau(x | 1) == 1
. Untuk yang pertama, cukup pintar untuk mengenalinya sebagai setarax <= 1
dan menghasilkan yang sederhanacmpl; setbe
. Yang lain membingungkannya dan membuatnya menghasilkan kode yang lebih buruk.Karena argumen adalah
uint
( unsigned ), Anda dapat meletakkannyaKurang terbaca (IMHO) tetapi jika Anda menghitung setiap karakter ( Code Golf atau sejenisnya)
Edit : untuk pertanyaan yang Anda edit :
Atau
sumber
return N<2?1:f(N-1)+f(n-2)
. : PAnda juga dapat memeriksa bahwa semua bit lainnya adalah 0 seperti ini:
Untuk kelengkapan, terima kasih kepada Matt , solusi yang lebih baik:
Dalam kedua kasus, Anda perlu menggunakan tanda kurung karena operator bitwise memiliki prioritas lebih rendah daripada
==
.sumber
(N|1)==1
Jika yang ingin Anda lakukan adalah membuat fungsi lebih efisien, gunakan tabel pencarian. Tabel pemeta ternyata sangat kecil, hanya 47 entri - entri berikutnya akan meluap bilangan bulat 32-bit yang tidak ditandatangani. Itu juga tentu saja membuat fungsi menulis menjadi sepele.
Anda jelas dapat melakukan hal yang sama untuk faktorial.
sumber
Bagaimana melakukannya dengan bitshift
Jika Anda ingin menggunakan bitshift dan membuat kodenya tidak jelas (tetapi singkat), Anda dapat melakukan:
Untuk integer unsigned
N
dalam bahasa c,N>>1
membuang bit orde rendah. Jika hasilnya bukan nol, ini berarti N lebih besar dari 1.Catatan: algoritma ini sangat tidak efisien karena tidak perlu menghitung ulang nilai dalam urutan yang telah dihitung.
Sesuatu JAUH lebih cepat
Hitunglah satu kali lintasan daripada secara implisit membangun pohon berukuran fibonaci (N):
Seperti yang telah disebutkan beberapa orang, tidak butuh waktu lama untuk meluap bahkan 64 bit unsigned integer. Bergantung pada seberapa besar Anda mencoba pergi, Anda harus menggunakan bilangan bulat presisi arbitrer.
sumber
uint
tidak dapat di-cast secara implisitbool
, dan pertanyaannya secara khusus diberi tag sebagai C #.--N != 0
saja. Intinya adalah sesuatu yang O (n) lebih disukai daripada O (fibn (n)).Saat Anda menggunakan uint, yang tidak bisa negatif, Anda dapat memeriksa apakah
n < 2
EDIT
Atau untuk kasus fungsi khusus itu Anda dapat menuliskannya sebagai berikut:
yang akan memberikan hasil yang sama, tentu saja dengan biaya langkah rekursi tambahan.
sumber
1 * fibn(0) = 1 * 1 = 1
fibn
begituCukup periksa untuk melihat apakah
N
<= 1 karena Anda tahu N tidak bertanda tangan, hanya ada 2 kondisiN <= 1
yang menghasilkanTRUE
: 0 dan 1sumber
(N == 0 || N == 1)
. Anda tahu itu tidak akan kurang dari 0 (karena itu akan ditandatangani!), Dan maksimal bisa 1.N <= 1
menyederhanakannya. Saya kira tipe unsigned tidak dijamin, tapi itu harus ditangani di tempat lain, menurut saya.int N
, dan Anda menyimpan kondisi aslinya, itu akan berulang tanpa batas ketika N negatif dengan kondisi aslinya. Karena itu perilaku yang tidak terdefinisi, kita sebenarnya tidak perlu khawatir tentang itu. Jadi kita dapat mengasumsikan bahwa N adalah non-negatif, terlepas dari deklarasi tersebut.Penafian: Saya tidak tahu C #, dan tidak menguji kode ini:
Tidak perlu bitshifting atau semacamnya, ini hanya menggunakan satu perbandingan, dan seharusnya jauh lebih efisien (O (n) vs O (2 ^ n) menurut saya?). Badan fungsinya lebih kompak , meskipun berakhir menjadi sedikit lebih lama dengan deklarasi.
(Untuk menghilangkan overhead dari rekursi, ada versi iteratif, seperti dalam jawaban Mathew Gunn )
PS: Ini adalah pola fungsional umum untuk iterasi dengan akumulator. Jika Anda mengganti
N--
denganN-1
Anda secara efektif tidak menggunakan mutasi, yang membuatnya dapat digunakan dalam pendekatan fungsional murni.sumber
Inilah solusi saya, tidak banyak yang bisa mengoptimalkan fungsi sederhana ini, sebaliknya yang saya tawarkan di sini adalah keterbacaan sebagai definisi matematis dari fungsi rekursif.
Definisi matematis dari bilangan Fibonacci dengan cara yang sama ..
Mengambil lebih jauh untuk memaksa switch case untuk membangun tabel pencarian.
sumber
switch
ketika Anda dapat memiliki serangkaian jawaban?untuk N adalah uint, gunakan saja
sumber
Jawaban Dmitry adalah yang terbaik tetapi jika itu adalah tipe pengembalian Int32 dan Anda memiliki kumpulan bilangan bulat yang lebih besar untuk dipilih, Anda dapat melakukan ini.
sumber
List.Contains
adalah O (n), 3) hanya membuat dua perbandingan (N > -3 && N < 3
) akan memberikan kode yang lebih pendek dan lebih mudah dibaca.Deret Fibonacci adalah rangkaian angka di mana sebuah angka ditemukan dengan menjumlahkan dua angka sebelumnya. Ada dua jenis titik awal: ( 0,1 , 1,2, ..) dan ( 1,1 , 2,3).
Posisi
N
dalam kasus ini dimulai dari1
, bukan0-based
sebagai indeks array.Dengan menggunakan fitur tubuh ekspresi C # 6 dan saran Dmitry tentang operator terner kita dapat menulis fungsi satu baris dengan perhitungan yang benar untuk tipe 1:
dan untuk tipe 2:
sumber
Agak terlambat ke pesta, tetapi Anda juga bisa melakukannya
(x==!!x)
!!x
mengonversi nilai menjadi1
jika tidak0
, dan membiarkannya0
jika memang demikian.Saya sering menggunakan hal semacam ini dalam kebingungan C.
Catatan: Ini C, tidak yakin apakah itu berfungsi di C #
sumber
uint n = 1; if (n == !!n) { }
memberikanOperator '!' cannot be applied to operand of type 'uint'
pada!n
C #. Hanya karena sesuatu bekerja di C tidak berarti bekerja di C #; bahkan#include <stdio.h>
tidak bekerja di C #, karena C # tidak memiliki arahan preprocessor "include". Bahasanya lebih berbeda daripada C dan C ++.Jadi saya membuat
List
bilangan bulat khusus ini dan memeriksa apakahN
berkaitan dengannya.Anda juga dapat menggunakan metode ekstensi untuk tujuan berbeda di mana
Contains
hanya dipanggil sekali (misalnya, saat aplikasi Anda mulai dan memuat data). Ini memberikan gaya yang lebih jelas dan menjelaskan hubungan utama dengan nilai Anda (N
):Terapkan itu:
Ini mungkin bukan cara tercepat untuk melakukannya, tetapi bagi saya, ini tampaknya gaya yang lebih baik.
sumber