Bagaimana cara mengumpulkan hasil pembagian integer?

335

Saya sedang memikirkan bagaimana cara menampilkan kontrol pagination, ketika menggunakan bahasa seperti C # atau Java.

Jika saya memiliki x item yang ingin saya tampilkan dalam potongan y per halaman, berapa banyak halaman yang akan dibutuhkan?

Ian Nelson
sumber
1
Apakah saya melewatkan sesuatu? y / x + 1 berfungsi dengan baik (asalkan Anda tahu operator selalu membulatkan ke bawah).
rikkit
51
@rikkit - jika y dan x sama, y ​​/ x + 1 terlalu tinggi.
Ian Nelson
1
Bagi siapa saja yang baru saja menemukan ini, jawaban untuk pertanyaan dupe ini menghindari konversi yang tidak perlu menjadi dua kali lipat dan menghindari masalah yang meluap selain memberikan penjelasan yang jelas.
ZX9
2
@IanNelson lebih umum jika xhabis dibagi y, y/x + 1akan menjadi terlalu tinggi.
Ohad Schneider
1
@ ZX9 Tidak, itu tidak menghindari kekhawatiran meluap. Ini persis solusi yang sama seperti yang diposkan Ian Nelson di sini.
user247702

Jawaban:

478

Menemukan solusi yang elegan:

int pageCount = (records + recordsPerPage - 1) / recordsPerPage;

Sumber: Konversi Angka, Roland Backhouse, 2001

Ian Nelson
sumber
12
-1 karena bug overflow yang ditunjukkan oleh Brandon DuRette
finnw
30
Mr Obvious mengatakan: Ingatlah untuk memastikan bahwa recordsPerPage tidak nol
Adam Gent
7
Kerja bagus, saya tidak percaya C # tidak memiliki langit-langit integer.
gosukiwi
2
Yup, saya di sini pada pertengahan 2017 menemukan jawaban hebat ini setelah mencoba beberapa pendekatan yang jauh lebih kompleks.
Mifo
1
Untuk bahasa dengan operator divisi Euclidian yang tepat seperti Python, pendekatan yang lebih sederhana adalah pageCount = -((-records) // recordsPerPage).
supercat
194

Mengubah ke floating point dan kembali sepertinya sangat membuang waktu di level CPU.

Solusi Ian Nelson:

int pageCount = (records + recordsPerPage - 1) / recordsPerPage;

Dapat disederhanakan untuk:

int pageCount = (records - 1) / recordsPerPage + 1;

AFAICS, ini tidak memiliki bug overflow yang ditunjukkan Brandon DuRette, dan karena itu hanya menggunakannya sekali, Anda tidak perlu menyimpan recordsPerPage khusus jika itu berasal dari fungsi yang mahal untuk mengambil nilai dari file konfigurasi atau sesuatu.

Yaitu ini mungkin tidak efisien, jika config.fetch_value menggunakan pencarian basis data atau sesuatu:

int pageCount = (records + config.fetch_value('records per page') - 1) / config.fetch_value('records per page');

Ini menciptakan variabel yang tidak benar-benar Anda butuhkan, yang mungkin memiliki implikasi memori (kecil) dan terlalu banyak mengetik:

int recordsPerPage = config.fetch_value('records per page')
int pageCount = (records + recordsPerPage - 1) / recordsPerPage;

Ini semua satu baris, dan hanya mengambil data satu kali:

int pageCount = (records - 1) / config.fetch_value('records per page') + 1;
rjmunro
sumber
5
+1, masalah nol catatan masih menghasilkan 1 pageCount sebenarnya mudah, karena saya masih ingin 1 halaman, menunjukkan placeholder / baris palsu "tidak ada catatan yang cocok dengan kriteria Anda", membantu menghindari masalah "hitungan 0 halaman" dalam apa pun kontrol pagination yang Anda gunakan.
Timothy Walters
27
Ketahuilah bahwa kedua solusi tidak mengembalikan pageCount yang sama untuk nol catatan. Versi yang disederhanakan ini akan mengembalikan 1 pageCount untuk nol catatan, sedangkan versi Roland Backhouse mengembalikan 0 pageCount. Baik jika itu yang Anda inginkan, tetapi kedua persamaan tidak setara ketika dilakukan oleh divisi integer gaya C # / Java.
Ian Nelson
10
suntingan kecil untuk kejelasan bagi orang-orang yang memindai dan kehilangan bodma saat mengubah penyederhanaan dari solusi Nelson (seperti yang saya lakukan pertama kali!), penyederhanaan dengan tanda kurung adalah ... int pageCount = ((catatan - 1) / recordsPerPage) +1
dave heywood
1
Anda harus menambahkan tanda kurung ke versi yang disederhanakan sehingga tidak bergantung pada urutan operasi tertentu. yaitu, ((catatan - 1) / recordsPerPage) + 1.
Martin
1
@Ian, jawaban ini tidak selalu kembali 1. Hal ini dapat kembali 0 jika recordsPerPage Anda adalah "1" dan ada 0 catatan: -1 / 1 + 1 = 0. Meskipun itu bukan kejadian super umum, penting untuk diingat jika Anda mengizinkan pengguna menyesuaikan ukuran halaman. Jadi jangan izinkan pengguna memiliki ukuran halaman 1, periksa ukuran halaman, atau keduanya (mungkin lebih baik untuk menghindari perilaku yang tidak terduga).
Michael
81

Untuk C # solusinya adalah dengan melemparkan nilai-nilai ke ganda (seperti Math.Ceiling mengambil ganda):

int nPages = (int)Math.Ceiling((double)nItems / (double)nItemsPerPage);

Di java Anda harus melakukan hal yang sama dengan Math.ceil ().

Huppie
sumber
4
mengapa jawaban ini sangat jauh ke bawah ketika op meminta C # secara eksplisit!
felickz
2
Anda juga perlu memberikan output intkarena Math.Ceilingmengembalikan a doubleatau decimal, tergantung pada tipe input.
DanM7
15
karena ini sangat tidak efisien
Zar Shardan
6
Mungkin tidak efisien tetapi sangat mudah dimengerti. Mengingat menghitung penghitungan halaman biasanya dilakukan sekali per permintaan, kehilangan kinerja tidak akan dapat diukur.
Jared Kells
1
Ini hampir tidak terbaca dari ini "(dividend + (pembagi - 1)) / pembagi;" juga lambat dan membutuhkan perpustakaan matematika.
bergulung
68

Ini akan memberi Anda apa yang Anda inginkan. Anda pasti ingin x item dibagi dengan item y per halaman, masalahnya adalah ketika angka tidak merata muncul, jadi jika ada halaman parsial kami juga ingin menambahkan satu halaman.

int x = number_of_items;
int y = items_per_page;

// with out library
int pages = x/y + (x % y > 0 ? 1 : 0)

// with library
int pages = (int)Math.Ceiling((double)x / (double)y);
Nick Berardi
sumber
5
x / y + !! (x% y) menghindari cabang untuk bahasa C-like. Kemungkinannya bagus, namun, kompiler Anda tetap melakukan itu.
Rhys Ulerich
2
1 untuk tidak meluap seperti jawaban di atas ... meskipun mengubah int menjadi dua kali lipat hanya untuk Math.ceiling dan kemudian kembali lagi adalah ide yang buruk dalam kode sensitif kinerja.
Cogwheel
3
@RhysUlerich yang tidak berfungsi di c # (tidak bisa langsung mengkonversi int ke bool). solusi rjmunro adalah satu-satunya cara untuk menghindari percabangan saya pikir.
smead
18

Solusi matematika integer yang disediakan Ian bagus, tetapi menderita bug integer overflow. Dengan asumsi semua variabel int, solusinya dapat ditulis ulang untuk menggunakan longmatematika dan menghindari bug:

int pageCount = (-1L + records + recordsPerPage) / recordsPerPage;

Jika recordsa long, bug tetap ada. Solusi modulus tidak memiliki bug.

Brandon DuRette
sumber
4
Saya tidak berpikir Anda secara realistis akan mengenai bug ini dalam skenario yang disajikan. 2 ^ 31 catatan cukup banyak harus melalui halaman.
rjmunro
8
@rjmunro, contoh dunia nyata di sini
finnw
@finnw: AFAICS, tidak ada contoh dunia nyata di halaman itu, hanya laporan orang lain yang menemukan bug dalam skenario teoretis.
rjmunro
5
Ya, saya menjadi jago dalam menunjukkan bug. Banyak bug dapat ada selamanya tanpa pernah menyebabkan masalah. Bug dengan bentuk yang sama ada dalam implementasi binarySearch JDK selama sekitar sembilan tahun, sebelum seseorang melaporkannya ( googleresearch.blogspot.com/2006/06/... ). Saya kira pertanyaannya adalah, terlepas dari seberapa tidak mungkin Anda menemukan bug ini, mengapa tidak memperbaikinya di muka?
Brandon DuRette
4
Juga, perlu dicatat bahwa itu bukan hanya jumlah elemen yang dihadirkan dalam hal ini, tetapi juga ukuran halaman. Jadi, jika Anda sedang membangun perpustakaan dan seseorang memilih untuk tidak halaman dengan melewati 2 ^ 31-1 (Integer.MAX_VALUE) sebagai ukuran halaman, maka bug dipicu.
Brandon DuRette
7

Varian dari jawaban Nick Berardi yang menghindari cabang:

int q = records / recordsPerPage, r = records % recordsPerPage;
int pageCount = q - (-r >> (Integer.SIZE - 1));

Catatan: (-r >> (Integer.SIZE - 1))terdiri dari bit tanda r, diulang 32 kali (terima kasih untuk menandatangani ekstensi >>operator.) Ini mengevaluasi ke 0 jika rnol atau negatif, -1 jika rpositif. Jadi mengurangi itu dari qefek menambahkan 1 jika records % recordsPerPage > 0.

menemukan
sumber
4

Untuk catatan == 0, solusi rjmunro memberikan 1. Solusi yang benar adalah 0. Yang mengatakan, jika Anda tahu bahwa catatan> 0 (dan saya yakin kita semua mengasumsikan recordsPerPage> 0), maka solusi rjmunro memberikan hasil yang benar dan tidak memiliki masalah luapan.

int pageCount = 0;
if (records > 0)
{
    pageCount = (((records - 1) / recordsPerPage) + 1);
}
// no else required

Semua solusi bilangan bulat matematika akan menjadi lebih efisien daripada salah satu solusi floating point.

Mike
sumber
Metode ini tidak mungkin menjadi hambatan kinerja. Dan jika ya, Anda juga harus mempertimbangkan biaya cabang.
finnw
4

Membutuhkan metode ekstensi:

    public static int DivideUp(this int dividend, int divisor)
    {
        return (dividend + (divisor - 1)) / divisor;
    }

Tidak ada pemeriksaan di sini (meluap, DivideByZero dll), jangan ragu untuk menambahkan jika Anda suka. Ngomong-ngomong, bagi mereka yang khawatir tentang overhead doa metode, fungsi-fungsi sederhana seperti ini mungkin digarisbawahi oleh kompiler, jadi saya tidak berpikir di situlah yang harus diperhatikan. Bersulang.

NB, Anda mungkin perlu menyadari hal ini juga (ada sisanya):

    int remainder; 
    int result = Math.DivRem(dividend, divisor, out remainder);
Nicholas Petersen
sumber
1
Ini salah. Sebagai contoh: DivideUp(4, -2)mengembalikan 0 (harus -2). Itu hanya benar untuk bilangan bulat non-negatif yang tidak jelas dari jawaban atau dari antarmuka fungsi.
Thash
6
Thash, mengapa Anda tidak melakukan sesuatu yang berguna seperti menambahkan sedikit cek tambahan kemudian jika jumlahnya negatif, bukannya memilih jawaban saya, dan salah membuat pernyataan selimut: "Ini tidak benar," padahal sebenarnya itu hanya keunggulan kasus. Saya sudah menjelaskan Anda harus melakukan pemeriksaan lain terlebih dahulu: "Tidak ada cek di sini (melimpah, DivideByZero, dll), jangan ragu untuk menambahkan jika Anda mau ."
Nicholas Petersen
1
Pertanyaan itu menyebutkan "Saya sedang memikirkan bagaimana cara menampilkan kontrol pagination " sehingga angka negatif akan keluar batas. Sekali lagi, lakukan saja sesuatu yang bermanfaat dan sarankan cek tambahan jika Anda mau, itu adalah upaya tim.
Nicholas Petersen
Saya tidak bermaksud bersikap kasar dan saya minta maaf jika Anda mengambilnya seperti itu. Pertanyaannya adalah "Bagaimana mengumpulkan hasil dari pembagian integer". Penulis menyebutkan pagination tetapi orang lain mungkin memiliki kebutuhan yang berbeda. Saya pikir akan lebih baik jika fungsi Anda entah bagaimana mencerminkan bahwa itu tidak berfungsi untuk bilangan bulat negatif karena tidak jelas dari antarmuka (misalnya nama atau jenis argumen yang berbeda). Untuk bekerja dengan bilangan bulat negatif, misalnya Anda dapat mengambil nilai absolut dari dividen dan pembagi dan mengalikan hasilnya dengan tandanya.
Thash
2

Alternatif lain adalah dengan menggunakan fungsi mod () (atau '%'). Jika ada sisa yang tidak nol, maka tambahlah hasil bilangan bulat dari divisi tersebut.

Jarod Elliott
sumber
1

Saya melakukan hal berikut, menangani setiap luapan:

var totalPages = totalResults.IsDivisble(recordsperpage) ? totalResults/(recordsperpage) : totalResults/(recordsperpage) + 1;

Dan gunakan ekstensi ini untuk jika ada 0 hasil:

public static bool IsDivisble(this int x, int n)
{
           return (x%n) == 0;
}

Juga, untuk nomor halaman saat ini (tidak ditanyakan tetapi dapat bermanfaat):

var currentPage = (int) Math.Ceiling(recordsperpage/(double) recordsperpage) + 1;
Sam Jones
sumber
0

Alternatif untuk menghapus percabangan dalam pengujian untuk nol:

int pageCount = (records + recordsPerPage - 1) / recordsPerPage * (records != 0);

Tidak yakin apakah ini akan berfungsi dalam C #, harus dilakukan dalam C / C ++.

aliran
sumber
-1

Metode generik, yang hasilnya dapat Anda ulangi mungkin menarik:

public static Object[][] chunk(Object[] src, int chunkSize) {

    int overflow = src.length%chunkSize;
    int numChunks = (src.length/chunkSize) + (overflow>0?1:0);
    Object[][] dest = new Object[numChunks][];      
    for (int i=0; i<numChunks; i++) {
        dest[i] = new Object[ (i<numChunks-1 || overflow==0) ? chunkSize : overflow ];
        System.arraycopy(src, i*chunkSize, dest[i], 0, dest[i].length); 
    }
    return dest;
}
Jeremy Hadfied
sumber
Guava memiliki metode yang mirip ( Lists.partition(List, int)) dan ironisnya size()metode yang dihasilkan List(pada r09) menderita bug melimpah yang disebutkan dalam jawaban Brandon DuRette .
finnw
-2

Saya memiliki kebutuhan serupa di mana saya perlu mengkonversi Menit menjadi jam & menit. Apa yang saya gunakan adalah:

int hrs = 0; int mins = 0;

float tm = totalmins;

if ( tm > 60 ) ( hrs = (int) (tm / 60);

mins = (int) (tm - (hrs * 60));

System.out.println("Total time in Hours & Minutes = " + hrs + ":" + mins);
Richard Parsons
sumber
-2

Berikut ini harus melakukan pembulatan yang lebih baik daripada solusi di atas, tetapi dengan mengorbankan kinerja (karena perhitungan floating point 0,5 * rctDenominator):

uint64_t integerDivide( const uint64_t& rctNumerator, const uint64_t& rctDenominator )
{
  // Ensure .5 upwards is rounded up (otherwise integer division just truncates - ie gives no remainder)
  return (rctDenominator == 0) ? 0 : (rctNumerator + (int)(0.5*rctDenominator)) / rctDenominator;
}
Jim Watson
sumber
-4

Anda ingin melakukan pembagian floating point, dan kemudian menggunakan fungsi plafon, untuk mengumpulkan nilai ke integer berikutnya.

Kibbee
sumber