Czy coś brakuje? y / x + 1 działa świetnie (pod warunkiem, że operator / zawsze zaokrągla w dół).
rikkit,
51
@rikkit - jeśli y i x są równe, y / x + 1 jest o jeden za wysokie.
Ian Nelson,
1
Dla każdego, kto właśnie to znajdzie, ta odpowiedź na podwójne pytanie pozwala uniknąć niepotrzebnej konwersji na podwójne i pozwala uniknąć obaw związanych z przepełnieniem, a także zapewnia jasne wyjaśnienie.
ZX9,
2
@IanNelson bardziej ogólnie, jeśli xmożna go podzielić y, y/x + 1byłby o jeden za wysoki.
Ohad Schneider
1
@ ZX9 Nie, nie pozwala uniknąć obaw związanych z przepełnieniem. To dokładnie to samo rozwiązanie, co Ian Nelson opublikował tutaj.
user247702,
Odpowiedzi:
478
Znaleziono eleganckie rozwiązanie:
int pageCount =(records + recordsPerPage -1)/ recordsPerPage;
-1 z powodu błędu przepełnienia wskazanego przez Brandona DuRette
finnw
30
Pan Oczywisty mówi: Pamiętaj, aby upewnić się, że pPerPage nie ma wartości zero
Adam Gent
7
Dobra robota, nie mogę uwierzyć, że C # nie ma sufitu liczb całkowitych.
gosukiwi
2
Tak, tutaj w połowie 2017 roku natknąłem się na tę świetną odpowiedź po wypróbowaniu kilku znacznie bardziej złożonych podejść.
Mifo
1
W przypadku języków z odpowiednim operatorem podziału euklidesowego, takim jak Python, podejście byłoby jeszcze prostsze pageCount = -((-records) // recordsPerPage).
supercat
194
Konwersja na zmiennoprzecinkowe i wstecz wydaje się ogromną stratą czasu na poziomie procesora.
Rozwiązanie Iana Nelsona:
int pageCount =(records + recordsPerPage -1)/ recordsPerPage;
Można uprościć:
int pageCount =(records -1)/ recordsPerPage +1;
AFAICS, nie ma w nim błędu przepełnienia, na który zwrócił uwagę Brandon DuRette, a ponieważ używa go tylko raz, nie trzeba przechowywać recordPerPage specjalnie, jeśli pochodzi z drogiej funkcji pobierania wartości z pliku konfiguracyjnego lub coś.
Może to być nieefektywne, jeśli config.fetch_value użył wyszukiwania bazy danych lub czegoś takiego:
int pageCount =(records + config.fetch_value('records per page')-1)/ config.fetch_value('records per page');
To tworzy zmienną, której tak naprawdę nie potrzebujesz, która prawdopodobnie ma (niewielkie) implikacje pamięci i po prostu zbyt dużo pisania:
int recordsPerPage = config.fetch_value('records per page')int pageCount =(records + recordsPerPage -1)/ recordsPerPage;
To wszystko jest jedna linia i dane są pobierane tylko raz:
int pageCount =(records -1)/ config.fetch_value('records per page')+1;
+1, problem zerowych rekordów wciąż zwracających 1 stronę Liczba jest faktycznie przydatna, ponieważ nadal chciałbym 1 stronę, pokazując symbol zastępczy / fałszywy wiersz „brak rekordów spełniających twoje kryteria”, pomaga uniknąć problemów z „liczbą stron 0” kontrola paginacji, której używasz.
Timothy Walters
27
Należy pamiętać, że dwa rozwiązania nie zwracają tego samego pageCount dla zerowych rekordów. Ta uproszczona wersja zwróci 1 pageCount dla zerowych rekordów, natomiast wersja Roland Backhouse zwraca 0 pageCount. Dobrze, jeśli tego chcesz, ale dwa równania nie są równoważne, gdy są wykonywane przez dzielenie całkowite w stylu C # / Java.
Ian Nelson
10
drobna edycja dla jasności dla osób skanujących ją i brakujących ciał przy przejściu na uproszczenie z rozwiązania Nelsona (tak jak zrobiłem za pierwszym razem!), uproszczenie za pomocą nawiasów to ... int pageCount = ((records - 1) / recordsPerPage) + 1;
dave heywood
1
Powinieneś dodać nawias do uproszczonej wersji, aby nie opierała się na określonej kolejności operacji. tj. ((rekordy - 1) / recordsPerPage) + 1.
Martin
1
@Ian, ta odpowiedź nie zawsze zwraca 1. Może zwraca 0 jeśli recordsPerPage jest „1” i istnieją 0 rekordy: -1 / 1 + 1 = 0. Chociaż nie jest to zbyt częste zdarzenie, należy pamiętać, że pozwalasz użytkownikom dostosowywać rozmiar strony. Więc albo nie zezwalaj użytkownikom na rozmiar strony 1, sprawdź rozmiar strony, albo jedno i drugie (prawdopodobnie lepiej, aby uniknąć nieoczekiwanego zachowania).
Michael
81
W przypadku C # rozwiązaniem jest rzutowanie wartości na wartość podwójną (ponieważ Math.Ceiling przyjmuje wartość podwójną):
int nPages =(int)Math.Ceiling((double)nItems /(double)nItemsPerPage);
dlaczego ta odpowiedź jest tak daleko w dół, gdy operacja wyraźnie prosi o C #!
felickz
2
Musisz także rzutować dane wyjściowe na, intponieważ Math.Ceilingzwraca a doublelub decimal, w zależności od typów danych wejściowych.
DanM7
15
ponieważ jest niezwykle nieefektywny
Zar Shardan
6
Może to być nieefektywne, ale bardzo łatwe do zrozumienia. Biorąc pod uwagę obliczanie liczby stron zwykle wykonuje się raz na żądanie, żadna utrata wydajności nie byłaby mierzalna.
Jared Kells,
1
Jest ledwo bardziej czytelny niż ten „(dywidenda + (dzielnik - 1)) / dzielnik;” także jest wolny i wymaga biblioteki matematycznej.
rzuca
68
To powinno dać ci to, czego chcesz. Na pewno chcesz x elementów podzielonych przez y elementów na stronę, problem pojawia się, gdy pojawiają się nierówne liczby, więc jeśli jest częściowa strona, chcemy również dodać jedną stronę.
int x = number_of_items;int y = items_per_page;// with out libraryint pages = x/y +(x % y >0?1:0)// with libraryint pages =(int)Math.Ceiling((double)x /(double)y);
x / y + !! (x% y) unika gałęzi dla języków podobnych do C. Kursy są dobre, jednak kompilator i tak to robi.
Rhys Ulerich
2
+1 za to, że nie przepełnia się jak powyższe odpowiedzi ... chociaż konwersja ints do dublerów tylko dla Math.ceiling, a potem z powrotem jest złym pomysłem w kodzie wrażliwym na wydajność.
Koło zębate
3
@RhysUlerich, który nie działa w języku C # (nie można bezpośrednio przekonwertować int na bool). Myślę, że rozwiązanie rjmunro jest jedynym sposobem na uniknięcie rozgałęzień.
smead
18
Rozwiązanie matematyczne liczb całkowitych, które dostarczył Ian, jest dobre, ale ma błąd przepełnienia liczb całkowitych. Zakładając, że zmienne są wszystkie int, rozwiązanie można przepisać, aby użyć longmatematyki i uniknąć błędu:
int pageCount = (-1L + records + recordsPerPage) / recordsPerPage;
Jeśli recordsjest a long, błąd pozostaje. Rozwiązanie modułu nie ma błędu.
@finnw: AFAICS, na tej stronie nie ma rzeczywistego przykładu, tylko raport o tym, że ktoś znalazł błąd w scenariuszu teoretycznym.
rjmunro
5
Tak, pedantycznie wskazywałam na błąd. Wiele błędów może istnieć wiecznie, nie powodując żadnych problemów. Błąd o tej samej formie istniał w implementacji binarySearch przez JDK przez około dziewięć lat, zanim ktoś to zgłosił ( googleresearch.blogspot.com/2006/06/… ). Myślę, że pytanie brzmi: niezależnie od tego, jak mało prawdopodobne jest napotkanie tego błędu, dlaczego nie naprawić go z góry?
Brandon DuRette
4
Należy również zauważyć, że liczy się nie tylko liczba stron, które są stronicowane, ale także rozmiar strony. Tak więc, jeśli budujesz bibliotekę i ktoś zdecyduje się nie stronicować, przekazując 2 ^ 31-1 (Integer.MAX_VALUE) jako rozmiar strony, wówczas błąd jest wywoływany.
int q = records / recordsPerPage, r = records % recordsPerPage;int pageCount = q -(-r >>(Integer.SIZE -1));
Uwaga: (-r >> (Integer.SIZE - 1))składa się z bitu znaku r, powtórzonego 32 razy (dzięki rozszerzeniu znaku >>operatora). Wartość ta rwynosi 0, jeśli jest zero lub ujemna, a -1, jeśli rjest dodatnia. Odjęcie go qpowoduje dodanie 1 if records % recordsPerPage > 0.
Dla rekordów == 0 rozwiązanie rjmunro daje 1. Prawidłowe rozwiązanie wynosi 0. To powiedziawszy, jeśli wiesz, że rekordy> 0 (i jestem pewien, że wszyscy założyliśmy recordPerPage> 0), wtedy rozwiązanie rjmunro daje prawidłowe wyniki i nie ma żadnych problemów z przepełnieniem.
int pageCount =0;if(records >0){
pageCount =(((records -1)/ recordsPerPage)+1);}// no else required
Wszystkie rozwiązania matematyczne na liczbach całkowitych będą bardziej wydajne niż jakiekolwiek rozwiązania zmiennoprzecinkowe.
Brak kontroli tutaj (przepełnienie, DivideByZero itp.), Możesz dodać, jeśli chcesz. Nawiasem mówiąc, dla tych, którzy martwią się narzutami wywołanymi metodami, kompilator może i tak wprowadzić takie proste funkcje, więc nie sądzę, że o to chodzi. Twoje zdrowie.
PS może ci się przydać świadomość tego (dostaje resztę):
int remainder;int result =Math.DivRem(dividend, divisor,out remainder);
To jest niepoprawne. Na przykład: DivideUp(4, -2)zwraca 0 (powinno być -2). Jest to poprawne tylko dla liczb całkowitych nieujemnych, które nie są jednoznaczne z odpowiedzi lub interfejsu funkcji.
Thash,
6
Pomyśl, dlaczego nie zrobisz czegoś pożytecznego, jak dodanie małego dodatkowego sprawdzania, a następnie, jeśli liczba jest ujemna, zamiast głosować moją odpowiedź w dół i niepoprawnie wypowiadając ogólne stwierdzenie: „To jest niepoprawne”, podczas gdy w rzeczywistości jest to tylko przewaga walizka. Wyjaśniłem już, że powinieneś najpierw wykonać inne kontrole: „Żadnych kontroli tutaj (przepełnienie, DivideByZero itp.), Możesz dodać, jeśli chcesz ”.
Nicholas Petersen
1
Pytanie brzmiało: „Myślę w szczególności o tym, jak wyświetlić elementy sterujące paginacją ”, więc liczby ujemne i tak byłyby poza zakresem. Ponownie, po prostu zrób coś użytecznego i zasugeruj dodatkowe sprawdzenie, jeśli chcesz, to człowiek wysiłku zespołowego.
Nicholas Petersen
Nie chciałem być niegrzeczny i przepraszam, jeśli tak to rozumiesz. Pytanie brzmiało: „Jak zaokrąglić wynik dzielenia liczb całkowitych”. Autor wspomniał o paginacji, ale inni ludzie mogą mieć inne potrzeby. Myślę, że byłoby lepiej, gdyby twoja funkcja w jakiś sposób odzwierciedlała, że nie działa dla ujemnych liczb całkowitych, ponieważ nie jest to jasne z interfejsu (na przykład inna nazwa lub typy argumentów). Aby pracować z ujemnymi liczbami całkowitymi, możesz na przykład wziąć wartość bezwzględną dywidendy i dzielnika i pomnożyć wynik przez jego znak.
Thash,
2
Inną alternatywą jest użycie funkcji mod () (lub „%”). Jeżeli pozostała niezerowa wartość, zwiększ wynik dzielenia przez liczbę całkowitą.
Poniższe czynności powinny zaokrąglać lepiej niż powyższe rozwiązania, ale kosztem wydajności (ze względu na obliczenia zmiennoprzecinkowe 0,5 * rctDenominator):
uint64_t integerDivide(constuint64_t& rctNumerator,constuint64_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;}
x
można go podzielićy
,y/x + 1
byłby o jeden za wysoki.Odpowiedzi:
Znaleziono eleganckie rozwiązanie:
Źródło: Konwersja liczb, Roland Backhouse, 2001
źródło
pageCount = -((-records) // recordsPerPage)
.Konwersja na zmiennoprzecinkowe i wstecz wydaje się ogromną stratą czasu na poziomie procesora.
Rozwiązanie Iana Nelsona:
Można uprościć:
AFAICS, nie ma w nim błędu przepełnienia, na który zwrócił uwagę Brandon DuRette, a ponieważ używa go tylko raz, nie trzeba przechowywać recordPerPage specjalnie, jeśli pochodzi z drogiej funkcji pobierania wartości z pliku konfiguracyjnego lub coś.
Może to być nieefektywne, jeśli config.fetch_value użył wyszukiwania bazy danych lub czegoś takiego:
To tworzy zmienną, której tak naprawdę nie potrzebujesz, która prawdopodobnie ma (niewielkie) implikacje pamięci i po prostu zbyt dużo pisania:
To wszystko jest jedna linia i dane są pobierane tylko raz:
źródło
-1 / 1 + 1 = 0
. Chociaż nie jest to zbyt częste zdarzenie, należy pamiętać, że pozwalasz użytkownikom dostosowywać rozmiar strony. Więc albo nie zezwalaj użytkownikom na rozmiar strony 1, sprawdź rozmiar strony, albo jedno i drugie (prawdopodobnie lepiej, aby uniknąć nieoczekiwanego zachowania).W przypadku C # rozwiązaniem jest rzutowanie wartości na wartość podwójną (ponieważ Math.Ceiling przyjmuje wartość podwójną):
W java powinieneś zrobić to samo z Math.ceil ().
źródło
int
ponieważMath.Ceiling
zwraca adouble
lubdecimal
, w zależności od typów danych wejściowych.To powinno dać ci to, czego chcesz. Na pewno chcesz x elementów podzielonych przez y elementów na stronę, problem pojawia się, gdy pojawiają się nierówne liczby, więc jeśli jest częściowa strona, chcemy również dodać jedną stronę.
źródło
Rozwiązanie matematyczne liczb całkowitych, które dostarczył Ian, jest dobre, ale ma błąd przepełnienia liczb całkowitych. Zakładając, że zmienne są wszystkie
int
, rozwiązanie można przepisać, aby użyćlong
matematyki i uniknąć błędu:int pageCount = (-1L + records + recordsPerPage) / recordsPerPage;
Jeśli
records
jest along
, błąd pozostaje. Rozwiązanie modułu nie ma błędu.źródło
Wariant odpowiedzi Nicka Berardi, który omija gałąź:
Uwaga:
(-r >> (Integer.SIZE - 1))
składa się z bitu znakur
, powtórzonego 32 razy (dzięki rozszerzeniu znaku>>
operatora). Wartość tar
wynosi 0, jeśli jest zero lub ujemna, a -1, jeślir
jest dodatnia. Odjęcie goq
powoduje dodanie 1 ifrecords % recordsPerPage > 0
.źródło
Dla rekordów == 0 rozwiązanie rjmunro daje 1. Prawidłowe rozwiązanie wynosi 0. To powiedziawszy, jeśli wiesz, że rekordy> 0 (i jestem pewien, że wszyscy założyliśmy recordPerPage> 0), wtedy rozwiązanie rjmunro daje prawidłowe wyniki i nie ma żadnych problemów z przepełnieniem.
Wszystkie rozwiązania matematyczne na liczbach całkowitych będą bardziej wydajne niż jakiekolwiek rozwiązania zmiennoprzecinkowe.
źródło
Potrzebuje metody rozszerzenia:
Brak kontroli tutaj (przepełnienie,
DivideByZero
itp.), Możesz dodać, jeśli chcesz. Nawiasem mówiąc, dla tych, którzy martwią się narzutami wywołanymi metodami, kompilator może i tak wprowadzić takie proste funkcje, więc nie sądzę, że o to chodzi. Twoje zdrowie.PS może ci się przydać świadomość tego (dostaje resztę):
źródło
DivideUp(4, -2)
zwraca 0 (powinno być -2). Jest to poprawne tylko dla liczb całkowitych nieujemnych, które nie są jednoznaczne z odpowiedzi lub interfejsu funkcji.Inną alternatywą jest użycie funkcji mod () (lub „%”). Jeżeli pozostała niezerowa wartość, zwiększ wynik dzielenia przez liczbę całkowitą.
źródło
Wykonuję następujące czynności, obsługuje wszelkie przepełnienia:
I użyj tego rozszerzenia, jeśli jest 0 wyników:
Ponadto dla bieżącego numeru strony (nie został zapytany, ale może być przydatny):
źródło
Alternatywą do usunięcia rozgałęzień w testach na zero:
Nie jestem pewien, czy to zadziała w C #, powinien zrobić w C / C ++.
źródło
Interesująca może być metoda ogólna, której wynik można powtórzyć:
źródło
Lists.partition(List, int)
) i jak na ironięsize()
metoda wynikowaList
(jak na razier09
) cierpi na błąd przepełnienia wymieniony w odpowiedzi Brandona DuRette .Miałem podobną potrzebę, gdy musiałem konwertować minuty na godziny i minuty. Użyłem:
źródło
Poniższe czynności powinny zaokrąglać lepiej niż powyższe rozwiązania, ale kosztem wydajności (ze względu na obliczenia zmiennoprzecinkowe 0,5 * rctDenominator):
źródło
Będziesz chciał wykonać dzielenie zmiennoprzecinkowe, a następnie użyć funkcji pułapu, aby zaokrąglić wartość do następnej liczby całkowitej.
źródło