Tamsayı bölümünün sonucu nasıl toplanır?


335

Özellikle C # veya Java gibi bir dil kullanırken, sayfalandırma denetimlerini görüntülemek nasıl düşünüyorum.

Ben varsa x ben parçaları görüntülemek istediğiniz öğeleri y Sayfa başına, kaç sayfa ihtiyaç olacak?


1
Bir şey mi kaçırıyorum? y / x + 1 harika çalışıyor (/ operatörünün her zaman aşağı yuvarlandığını biliyorsanız).
rikkit

51
@rikkit - y ve x eşitse, y / x + 1 çok yüksektir.
Ian Nelson

1
Şimdi bunu bulan herkes için, bir dupe sorusuna verilen bu cevap , çifte gereksiz dönüşümü önler ve açık bir açıklama sağlamanın yanı sıra taşma endişelerini önler.
ZX9

2
@IanNelson daha genel xolarak bölünebilirse y, y/x + 1çok yüksek olur.
Ohad Schneider

1
@ ZX9 Hayır, taşma sorunlarından kaçınmaz. Ian Nelson'ın burada paylaştığı çözümle tamamen aynı.
user247702

Yanıtlar:


478

Zarif bir çözüm buldu:

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

Kaynak: Sayı Dönüşümü, Roland Backhouse, 2001



30
Bay Obvious diyor: KayıtlarınPerPage'in sıfır olmadığından emin olmayı unutmayın
Adam Gent

7
İyi iş, C # 'ın tamsayı tavana sahip olmadığına inanamıyorum.
gosukiwi

2
Evet, burada 2017'nin ortasında, çok daha karmaşık yaklaşımları denedikten sonra bu harika cevaba rastladım.
Mifo

1
Python gibi uygun bir Öklid bölme operatörü olan diller için daha basit bir yaklaşım olacaktır pageCount = -((-records) // recordsPerPage).
supercat

194

Kayan nokta ve geri dönüş, CPU seviyesinde büyük bir zaman kaybı gibi görünüyor.

Ian Nelson'ın çözümü:

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

Basitleştirilebilir:

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

AFAICS, Brandon DuRette'in işaret ettiği taşma hatasını içermiyor ve sadece bir kez kullandığından, bir yapılandırma dosyasından değeri almak için pahalı bir işlevden geliyorsa recordPerPage'i özel olarak depolamanıza gerek yok. şey.

Yani config.fetch_value bir veritabanı araması falan kullandıysa, bu verimsiz olabilir:

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

Bu, gerçekten ihtiyacınız olmayan, muhtemelen (küçük) bellek sonuçları olan ve çok fazla yazılan bir değişken oluşturur:

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

Bu yalnızca bir satırdır ve verileri yalnızca bir kez alır:

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

5
+1, sıfır kayıt sorunu hala 1 pageCount döndürüyor. kullandığınız sayfalama kontrolü.
Timothy Walters

27
İki çözümün sıfır kayıt için aynı pageCount değerini döndürmediğini unutmayın. Bu basitleştirilmiş sürüm sıfır kayıtları için 1 pageCount döndürürken Roland Backhouse sürümü 0 pageCount döndürür. İstediğiniz buysa iyi, ancak C # / Java stylee tamsayı bölümü tarafından gerçekleştirildiğinde iki denklem eşdeğer değildir.
Ian Nelson

10
Nelson çözümünden (ilk kez yaptığım gibi!) + 1;
dave heywood

1
Belirli bir işlem sırasına dayanmaması için basitleştirilmiş sürüme parantez eklemeniz gerekir. ör., ((kayıtlar - 1) / kayıtlarPerPage) + 1.
Martin

1
@Ian, bu yanıtın DAİMA recordsPerPage "1" dir ve 0 kayıt varsa bu 0 dönebilirsiniz 1. dönmez: -1 / 1 + 1 = 0. Bu çok yaygın bir durum olmasa da, kullanıcıların sayfa boyutunu ayarlamasına izin veriyorsanız aklınızda bulundurmanız önemlidir. Bu nedenle, kullanıcıların sayfa boyutunda 1 olmasına, sayfa boyutunu kontrol etmesine veya her ikisine birden izin vermeyin (muhtemelen beklenmedik davranışlardan kaçınmak için tercih edilir).
Michael

81

C # için çözüm, değerleri bir çifte atmaktır (Math.Ceiling bir çift alır):

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

Java'da Math.ceil () ile de aynısını yapmalısınız.


4
op açıkça C # istediğinde bu cevap neden şimdiye kadar aşağı!
felickz

2
Ayrıca çıktı döküm gerekir intçünkü Math.Ceilingdönene bir doubleveya decimalgiriş türlerine bağlı olarak,.
DanM7

15
çünkü son derece verimsiz
Zar Shardan

6
Verimsiz olabilir, ancak anlaşılması son derece kolaydır. Bir sayfa sayısının hesaplanması genellikle istek başına bir kez yapıldığında herhangi bir performans kaybı ölçülemez.
Jared Kells

1
Bundan daha kolay okunabilir "(temettü + (bölen - 1)) / bölen;" Ayrıca yavaş ve matematik kütüphanesi gerektirir.
rulo

68

Bu size istediğinizi vermelidir. Kesinlikle x öğenin sayfa başına y öğeye bölünmesini isteyeceksiniz, sorun düzensiz sayıların ortaya çıkmasıdır, bu nedenle kısmi bir sayfa varsa, bir sayfa eklemek istiyoruz.

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);

5
x / y + !! (x% y), C benzeri diller için dalı önler. Oran iyi, ancak derleyiciniz zaten bunu yapıyor.
Rhys Ulerich

2
Yukarıdaki cevaplar gibi taşmadığı için +1 ... ints sadece Math.ceiling için çiftler ve sonra tekrar dönüştürme performansa duyarlı kod kötü bir fikirdir.
Dişli

3
@ RhysUlerich c # ile çalışmaz (int'i doğrudan bir boole dönüştüremez). Bence rjmunro'nun çözümü dallanmayı önlemenin tek yoludur.
SMEAD

18

Ian'ın sağladığı tamsayı matematik çözümü güzel, ancak tamsayı taşması hatası yaşıyor. Değişkenlerin hepsi olduğu varsayıldığında, matematik intkullanmak longve hatadan kaçınmak için çözüm yeniden yazılabilir :

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

A ise records, longhata kalır. Modül çözümü hata içermiyor.


4
Sunulan senaryoda gerçekçi bir şekilde bu hatayı vuracağınızı sanmıyorum. 2 ^ 31 kayıtları sayfalar arasında gezinmek için oldukça fazla.
rjmunro


@finnw: AFAICS, bu sayfada gerçek dünyadan bir örnek yok, sadece teorik bir senaryoda hatayı bulan başka birinin raporu.
rjmunro

5
Evet, hatayı belirtmek için bilgiçlik yapıyordum. Birçok hata hiç bir soruna neden olmadan daimi olarak var olabilir. JDK'nın binarySearch uygulamasında, biri bildirmeden önce yaklaşık dokuz yıl boyunca aynı formdaki bir hata vardı ( googleresearch.blogspot.com/2006/06/… ). Sanırım soru, bu hatayla karşılaşma olasılığınız ne olursa olsun, neden onu düzeltmiyorsunuz?
Brandon DuRette

4
Ayrıca, sadece önemli olan sayfalanmış öğelerin sayısı değil, aynı zamanda sayfa boyutu olduğu da belirtilmelidir. Bu nedenle, bir kitaplık oluşturuyorsanız ve birisi sayfa boyutu olarak 2 ^ 31-1 (Integer.MAX_VALUE) ileterek sayfa yapmamayı seçerse, hata tetiklenir.
Brandon DuRette

7

Nick Berardi'nin bir dalı önleyen cevabının bir çeşidi :

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

Not: 32 kez tekrarlanan (-r >> (Integer.SIZE - 1))işaret bitinden oluşur ( operatörün rişaret uzantısı sayesinde >>). rSıfır veya negatifse 0 r, pozitifse -1 olarak değerlendirilir. Bu yüzden onu çıkarmak q1 ise ekleme etkisi vardır records % recordsPerPage > 0.


4

Kayıtlar == 0 için, rjmunro'nun çözümü 1'i verir. Doğru çözüm 0'dır. Bu, eğer kayıtların> 0 olduğunu biliyorsanız (ve eminim hepimizin recordPerPage> 0 olduğunu varsaydığımızdan emin olursanız), rjmunro çözümü doğru sonuçlar verir ve taşma sorunlarından hiçbiri yok.

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

Herşey tamsayı matematik çözümleri daha etkili olacak herhangi kayan nokta çözümlerinin.


Bu yöntemin performans darboğazı olması pek olası değildir. Ve eğer öyleyse, dalın maliyetini de düşünmelisiniz.
finnw

4

Bir uzatma yöntemine ihtiyaç var:

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

Burada kontrol yok (taşma, DivideByZerovb), isterseniz eklemek için çekinmeyin. Bu arada, yöntem çağırma yükü hakkında endişe duyanlar için, bunun gibi basit işlevler derleyici tarafından yine de işaretlenebilir, bu yüzden nerede endişe duyulacağını düşünmüyorum. Şerefe.

PS bunun da farkında olmak yararlı bulabilirsiniz (geri kalanı alır):

    int remainder; 
    int result = Math.DivRem(dividend, divisor, out remainder);

1
Bu yanlış. Örneğin: DivideUp(4, -2)0 değerini döndürür (-2 olmalıdır). Sadece cevaptan veya fonksiyonun arayüzünden net olmayan negatif olmayan tamsayılar için doğrudur .
Thash

6
Thash, neden biraz ekstra kontrol eklemek gibi yararlı bir şey yapmıyorsunuz, o zaman sayı negatifse, cevabımı oylamak yerine ve battaniye ifadesini yanlış yapmak yerine: "Bu yanlış," aslında sadece bir avantaj olduğunda durum. Öncelikle diğer kontrolleri yapmanız gerektiğini açıkça belirttim: "Burada kontrol yok (taşma, DivideByZero, vb.), İsterseniz eklemek için çekinmeyin ."
Nicholas Petersen

1
"Özellikle sayfalama kontrollerinin nasıl görüntüleneceğini düşünüyorum" sorusu, bu nedenle negatif sayıların sınırların dışında olması gerekirdi. Yine, sadece yararlı bir şey yapın ve isterseniz ek bir kontrol önerin, bu bir ekip çalışması adam.
Nicholas Petersen

Kaba olmak istememiştim ve eğer böyle yaparsan özür dilerim. Soru "Tamsayı bölümünün sonucunun nasıl yuvarlanacağı" idi. Yazar, sayfalandırmadan bahsetti, ancak diğer insanların farklı ihtiyaçları olabilir. Arayüzünüzden net olmadığı için (örneğin farklı bir ad veya bağımsız değişken türleri) işlevinizin bir şekilde negatif tamsayılar için çalışmadığını yansıtırsa daha iyi olacağını düşünüyorum. Negatif tamsayılarla çalışmak için, örneğin, temettü ve bölen için mutlak bir değer alabilir ve sonucu işaretiyle çarpabilirsiniz.
Thash

2

Başka bir alternatif mod () işlevini (veya '%') kullanmaktır. Sıfırdan farklı bir kalan varsa, bölümün tamsayı sonucunu artırın.


1

Aşağıdakileri yaparım, taşmaları yönetir:

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

0 sonuç varsa bu uzantıyı kullanın:

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

Ayrıca, geçerli sayfa numarası için (istenmedi, ancak yararlı olabilir):

var currentPage = (int) Math.Ceiling(recordsperpage/(double) recordsperpage) + 1;

0

Sıfır testinde dallanmayı gidermek için alternatif:

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

Bu C # çalışacak emin değilim, C / C ++ yapmak gerekir.


-1

Sonuçlarını tekrarlayabileceğiniz genel bir yöntem ilgi çekici olabilir:

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;
}

Guava benzer bir yönteme ( Lists.partition(List, int)) sahiptir ve ironik olarak size()ortaya çıkan List(itibariyle r09) yöntemi Brandon DuRette'in cevabında belirtilen taşma hatasından muzdariptir .
finnw

-2

Dakikaları saatlere ve dakikalara dönüştürmem gerektiğinde benzer bir ihtiyacım vardı. Ne kullandım:

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);

-2

Aşağıdakiler, yukarıdaki çözümlerden daha iyi yuvarlama yapmalıdır, ancak performans pahasına (0.5 * rctDenominator kayan nokta hesaplaması nedeniyle):

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;
}

-4

Kayan nokta bölümü yapmak ve ardından değeri bir sonraki tamsayıya yuvarlamak için tavan işlevini kullanmak isteyeceksiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.