Saya memiliki 3 bilangan bulat bertanda tangan yang sangat besar.
long x = long.MaxValue;
long y = long.MaxValue - 1;
long z = long.MaxValue - 2;
Saya ingin menghitung rata-rata terpotongnya. Nilai rata-rata yang diharapkan adalah long.MaxValue - 1
, yaitu 9223372036854775806
.
Tidak mungkin menghitungnya sebagai:
long avg = (x + y + z) / 3; // 3074457345618258600
Catatan: Saya membaca semua pertanyaan tentang rata-rata 2 angka, tetapi saya tidak melihat bagaimana teknik itu dapat diterapkan pada rata-rata 3 angka.
Ini akan sangat mudah dengan penggunaan BigInteger
, tapi mari kita asumsikan saya tidak bisa menggunakannya.
BigInteger bx = new BigInteger(x);
BigInteger by = new BigInteger(y);
BigInteger bz = new BigInteger(z);
BigInteger bavg = (bx + by + bz) / 3; // 9223372036854775806
Jika saya mengonversi ke double
, maka, tentu saja, saya kehilangan presisi:
double dx = x;
double dy = y;
double dz = z;
double davg = (dx + dy + dz) / 3; // 9223372036854780000
Jika saya mengonversi ke decimal
, itu berfungsi, tetapi juga anggaplah saya tidak dapat menggunakannya.
decimal mx = x;
decimal my = y;
decimal mz = z;
decimal mavg = (mx + my + mz) / 3; // 9223372036854775806
Pertanyaan: Apakah ada cara untuk menghitung rata-rata terpotong dari 3 bilangan bulat yang sangat besar hanya dengan penggunaan long
tipe? Jangan anggap pertanyaan itu spesifik C #, hanya lebih mudah bagi saya untuk memberikan contoh dalam C #.
sumber
long.MinValue
dan dilong.MaxValue
antara nilai.BigInteger
ataudecimal
dikecualikan, atau hanya untuk membuat ini sulit?Jawaban:
Kode ini akan berfungsi, tetapi tidak terlalu bagus.
Ini pertama-tama membagi ketiga nilai (itu mendasarkan nilai, jadi Anda 'kehilangan' sisanya), dan kemudian membagi sisanya:
Perhatikan bahwa contoh di atas tidak selalu berfungsi dengan baik bila memiliki satu atau lebih nilai negatif.
Seperti yang didiskusikan dengan Ulugbek, karena banyaknya komentar di bawah ini, berikut adalah solusi TERBAIK saat ini untuk nilai positif dan negatif.
Terima kasih atas jawaban dan komentar Ulugbek Umirov , James S , KevinZ , Marc van Leeuwen , gnasher729 inilah solusi terkini:
sumber
(x + y + z) / 3 = x / 3 + y / 3 + z / 3
,.f(1,1,2) == 1
sementaraf(-2,-2,8) == 2
(x+y)/3
yang terlalu banyak.NB - Patrick telah memberikan jawaban yang bagus . Memperluas ini, Anda dapat melakukan versi umum untuk sejumlah bilangan bulat seperti:
sumber
long
, tetapi untuk tipe yang lebih kecil, perhatikan bahwa jumlah kedua dapat melimpah.Patrick Hofman telah memposting solusi hebat . Namun bila diperlukan masih bisa diimplementasikan dengan beberapa cara lain. Menggunakan algoritme di sini saya punya solusi lain. Jika diterapkan dengan hati-hati, ini mungkin lebih cepat daripada beberapa divisi dalam sistem dengan pembagi perangkat keras yang lambat. Ini dapat lebih dioptimalkan dengan menggunakan teknik divide by constants dari kesenangan peretas
Di C / C ++ pada platform 64-bit, ini jauh lebih mudah
__int128
sumber
x=y/3
melalui unsignedx=y>>2; x+=x>>2; x+=x>>4; x+=x>>8; x+=x>>16; x+=x>>32;
. Hasilnya akan sangat mendekati x, dan dapat dibuat presisi dengan menghitungdelta=y-x-x-x;
dan menggunakan penyesuaianx
sesuai kebutuhan.Anda dapat menghitung rata-rata angka berdasarkan perbedaan antara angka-angka daripada menggunakan jumlah.
Misalkan x adalah maks, y adalah median, z adalah min (seperti yang Anda miliki). Kami akan menyebutnya max, median dan min.
Pemeriksa bersyarat ditambahkan sesuai komentar @ UlugbekUmirov:
sumber
(double)(2 / 3)
sama dengan 0,0?Karena C menggunakan pembagian berlantai daripada pembagian Euclidian, mungkin lebih mudah untuk menghitung rata-rata yang dibulatkan dengan benar dari tiga nilai tak bertanda tangan daripada tiga nilai bertanda. Cukup tambahkan 0x8000000000000000UL ke setiap angka sebelum mengambil rata-rata unsigned, kurangi setelah mengambil hasilnya, dan gunakan cast back yang tidak dicentang
Int64
untuk mendapatkan rata-rata yang ditandatangani.Untuk menghitung rata-rata unsigned, hitung jumlah 32 bit teratas dari tiga nilai. Kemudian hitung jumlah 32 bit terbawah dari tiga nilai, ditambah jumlah dari atas, ditambah satu [nilai tambah satu untuk menghasilkan hasil yang bulat]. Rata-rata akan menjadi 0x55555555 kali jumlah pertama, ditambah sepertiga dari jumlah kedua.
Kinerja pada prosesor 32-bit dapat ditingkatkan dengan menghasilkan tiga nilai "jumlah" yang masing-masing sepanjang 32 bit, sehingga hasil akhirnya adalah
((0x55555555UL * sumX)<<32) + 0x55555555UL * sumH + sumL/3
; itu mungkin lebih ditingkatkan dengan menggantisumL/3
dengan((sumL * 0x55555556UL) >> 32)
, meskipun yang terakhir akan tergantung pada pengoptimal JIT [mungkin tahu bagaimana mengganti pembagian dengan 3 dengan perkalian, dan kodenya mungkin sebenarnya lebih efisien daripada operasi perkalian eksplisit].sumber
Menambal solusi Patrick Hofman dengan koreksi supercat , saya berikan Anda yang berikut:
Dan kasus elemen N:
Ini selalu memberikan floor () dari mean, dan menghilangkan setiap kemungkinan kasus edge.
sumber
Anda dapat menggunakan fakta bahwa Anda dapat menulis setiap angka sebagai
y = ax + b
, di manax
adalah konstanta. Masinga
- masing akan menjadiy / x
(bagian integer dari divisi itu). Setiap b akan menjadiy % x
(sisa / modulo dari divisi itu). Jika Anda memilih konstanta ini dengan cara yang cerdas, misalnya dengan memilih akar kuadrat dari bilangan maksimum sebagai konstanta, Anda bisa mendapatkan rata-ratax
angka tanpa mengalami masalah overflow.Rata-rata dari daftar angka yang berubah-ubah dapat ditemukan dengan menemukan:
dimana
%
menunjukkan modulo dan/
menunjukkan bagian 'keseluruhan' dari divisi.Programnya akan terlihat seperti:
sumber
Jika Anda tahu Anda memiliki nilai N, dapatkah Anda membagi setiap nilai dengan N dan menjumlahkannya?
sumber
Saya juga mencobanya dan menemukan solusi yang lebih cepat (meskipun hanya dengan faktor sekitar 3/4). Ini menggunakan satu divisi
dimana
smallDiv3
pembagian dengan 3 menggunakan perkalian dan bekerja hanya untuk argumen kecilIni seluruh kode termasuk tes dan patokan, hasilnya tidak terlalu mengesankan.
sumber
Fungsi ini menghitung hasil dalam dua divisi. Ini harus menggeneralisasi dengan baik ke pembagi dan ukuran kata lain.
Ia bekerja dengan menghitung hasil penjumlahan dua kata, kemudian mengerjakan pembagian.
sumber
Matematika
Kode
sumber
{1,2,3}
jawabannya adalah2
, tetapi kode Anda akan kembali1
.double
, karena kita akan kehilangan ketepatan dalam kasus seperti itu.Coba ini:
sumber