整数除算の結果を切り上げる方法は?


335

特に、C#やJavaなどの言語を使用している場合に、ページネーションコントロールを表示する方法について考えています。

ページごとにyのチャンクで表示するxアイテムがある場合、いくつのページが必要になりますか?


1
何か不足していますか?y / x + 1は適切に機能します(/演算子が常に切り捨てられることがわかっている場合)。
rikkit

51
@rikkit-yとxが等しい場合、y / x + 1は高すぎます。
Ian Nelson

1
これを今見つけたばかりの人にとっては、重複した質問に対するこの回答は、明確な説明を提供することに加えて、二重への不要な変換を回避し、オーバーフローの懸念回避します。
ZX9

2
@IanNelsonより一般的には、xで割り切れる場合yy/x + 1高すぎます。
Ohad Schneider

1
@ ZX9いいえ、オーバーフローの問題は回避できません。これは、Ian Nelsonがここに投稿したものとまったく同じソリューションです。
user247702

回答:


478

エレガントなソリューションを見つけました:

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

出典:Number Conversion、Roland Backhouse、2001



30
Obvious氏のコメント:recordsPerPageがゼロでないことを確認してください
Adam Gent

7
C#に整数の上限がないとは思えません。
gosukiwi 2012

2
うん、ここで私はいくつかのはるかに複雑なアプローチを試した後、2017年半ばにこの素晴らしい答えに出くわしました。
Mifo 2017

1
Pythonなどの適切なユークリッド除算演算子を使用する言語の場合、さらに簡単な方法が考えられますpageCount = -((-records) // recordsPerPage)
スーパーキャット2018

194

浮動小数点への変換とその逆変換は、CPUレベルでの時間の無駄のようです。

Ian Nelsonのソリューション:

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

次のように簡略化できます。

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

AFAICS、これにはBrandon DuRetteが指摘したオーバーフローバグはありません。これは1回しか使用しないため、構成ファイルから値をフェッチするための高価な関数から取得したり、特別にrecordsPerPageを保存したりする必要はありません。何か。

つまり、config.fetch_valueがデータベースルックアップなどを使用している場合、これは非効率的です。

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

これにより、実際には必要のない変数が作成されます。これはおそらく(マイナー)なメモリの影響があり、入力が多すぎます。

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

これはすべて1行で、データを1回だけフェッチします。

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

5
+1、ゼロのレコードが1 pageCountを返す問題は実際には便利です。「基準に一致するレコードはありません」というプレースホルダー/偽の行を表示したいので、1ページが必要なので、「0ページのカウント」の問題を回避できます。使用するページネーションコントロール。
ティモシーウォルターズ

27
2つのソリューションは、ゼロのレコードに対して同じpageCountを返さないことに注意してください。この単純化されたバージョンは、ゼロレコードに対して1 pageCountを返しますが、Roland Backhouseバージョンは0 pageCountを返します。それでいいのであれば問題ありませんが、C#/ Javaスタイルの整数除算で実行すると、2つの方程式は等しくありません。
Ian Nelson

10
Nelsonソリューションから簡略化に変更するときにそれをスキャンしてbodmaを欠落している人々のために明確にするための小さな編集(最初に行ったように!)、角かっこでの簡略化は... int pageCount =((records-1)/ recordsPerPage) + 1;
デイブヘイウッド2014年

1
操作の特定の順序に依存しないように、簡略化されたバージョンに括弧を追加する必要があります。つまり、((records-1)/ recordsPerPage)+ 1.
Martin

1
@Ian、この回答は常に1を返すわけではありません。recordsPerPageが "1"で、レコードが0の場合、0を返すことがあります-1 / 1 + 1 = 0。これは非常に一般的なことではありませんが、ユーザーがページサイズを調整できるようにする場合は注意が必要です。したがって、ユーザーがページサイズ1を許可しないようにするか、ページサイズをチェックするか、またはその両方を行います(おそらく予期しない動作を回避するために推奨されます)。
Michael

81

C#の場合の解決策は、値をdoubleにキャストすることです(Math.Ceilingはdoubleを取るため)。

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

Javaでは、Math.ceil()で同じことを行う必要があります。


4
なぜ、opがC#を明示的に要求したときに、この答えがこれまでにないのですか?
felickz

2
入力タイプに応じてa またはを返すintため、出力をキャストする必要もあります。Math.Ceilingdoubledecimal
DanM7 2012年

14
非常に効率が悪いため
Zar Shardan 2013

6
非効率的かもしれませんが、非常に理解しやすいです。通常、ページ数の計算はリクエストごとに1回行われるため、パフォーマンスの低下は測定できません。
Jared Kells 2015

1
この「(被除数+(除数-1))/除数;」よりもやや読みやすい また、速度が遅く、数学ライブラリが必要です。
2017

68

これはあなたが望むものを与えるはずです。ページごとにxアイテムをyアイテムで割ったものが確実に必要になります。問題は不均一な数が発生した場合です。そのため、部分的なページがある場合は、1ページも追加する必要があります。

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のような言語の分岐を回避します。オッズは良いですが、コンパイラはとにかくそうしています。
Rhys Ulerich、2010年

2
上記の答えのようにオーバーフローしないための+1 ...ただし、Math.ceilingのためだけにintをdoubleに変換してから再び変換することは、パフォーマンスに敏感なコードでは悪い考えです。
Cogwheel 2015

3
@RhysUlerichはc#では機能しません(intをboolに直接変換できません)。rjmunroのソリューションは、分岐を回避する唯一の方法だと思います。
smead

18

Ianが提供した整数演算ソリューションは優れていますが、整数オーバーフローのバグに悩まされています。変数がすべてintであると仮定すると、long計算を使用してバグを回避するようにソリューションを書き直すことができます。

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

もしがrecordsありlong、バグが残っています。弾性率ソリューションにはバグがありません。


4
提示されたシナリオで実際にこのバグに遭遇することはないと思います。2 ^ 31レコードは、ページをめくるにはかなりの数になります。
rjmunro 2009

8
@rjmunro、ここでの実例
finnw

@finnw:AFAICS、そのページには実際の例はありません。理論上のシナリオでバグを見つけた誰かのレポートだけです。
rjmunro

5
はい、私はバグを指摘するのに熱心でした。多くのバグは、問題を引き起こすことなく永続的に存在できます。同じ形式のバグが、誰かが報告する前に、約9年間、JDKのbinarySearchの実装に存在していました(googleresearch.blogspot.com/2006/06/…)。問題は、このバグに遭遇する可能性がどれほど低いかに関係なく、なぜそれを前もって修正しないのでしょうか?
Brandon DuRette、2011年

4
また、重要なのは、ページングされる要素の数だけではなく、ページサイズでもあるということです。したがって、ライブラリを構築していて、誰かがページサイズとして2 ^ 31-1(Integer.MAX_VALUE)を渡してページングしないことを選択した場合、バグがトリガーされます。
Brandon DuRette、2011年

7

分岐を回避するNick Berardiの回答の変形:

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

注:(-r >> (Integer.SIZE - 1))の符号ビットで構成され、r32回繰り返されます(>>演算子の符号拡張のおかげです。)これrは、ゼロまたは負の場合は0、r正の場合は-1と評価されます。したがって、それを引くとqif 1が加算されrecords % recordsPerPage > 0ます。


4

records == 0の場合、rjmunroの解は1を返します。正しい解は0です。つまり、records> 0であることがわかっている場合(そして、recordsPerPage> 0をすべて想定していると確信している場合)、rjmunroの解は正しい結果を返し、オーバーフローの問題はありません。

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

すべての整数演算ソリューションは、どの浮動小数点ソリューションより効率的です。


この方法がパフォーマンスのボトルネックになることはほとんどありません。そして、もしそうなら、あなたはブランチのコストも考慮すべきです。
finnw

4

拡張メソッドが必要な場合:

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

ここにはチェックはありません(オーバーフロー、 DivideByZeroなど)。必要に応じて追加してください。ちなみに、メソッド呼び出しのオーバーヘッドが気になる方は、とにかくこのような単純な関数をコンパイラにインライン化するかもしれませんので、気にする必要はないと思います。乾杯。

PSあなたもこれに気づくと便利だと思うかもしれません(残りを取得します):

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

1
これは誤りです。たとえば、DivideUp(4, -2)0を返します(-2にする必要があります)。これは、正解または関数のインターフェースから明確でない非負の整数に対してのみ正しいです
ハッシュ

6
ほら、答えを下に投票するのではなく、数が負の場合は少し余分なチェックを追加し、ブランケットステートメントを誤って作成するなど、何か便利なことをしてみませんか。実際、これは単なるエッジです。場合。最初に他のチェックを行う必要があることはすでに明確にしました。「ここではチェックなし(オーバーフロー、DivideByZeroなど)。必要に応じて追加してください。」
Nicholas Petersen

1
質問では「特にページネーションコントロールを表示する方法について考えている」と述べたので、負の数はとにかく範囲外でした。繰り返しになりますが、何か便利なことを行い、必要に応じて追加のチェックを提案するだけです。
Nicholas Petersen

私は失礼なことをするつもりはありませんでした。質問は「整数除算の結果を切り上げる方法」でした。著者はページネーションに言及しましたが、他の人々は異なるニーズを持っているかもしれません。インターフェイスからは明確でないため、関数が負の整数では機能しないことが何らかの形で反映されているとよいでしょう(たとえば、名前や引数の型が異なるなど)。負の整数を処理するには、たとえば、被除数と除数の絶対値を取り、その結果にその符号を掛けます。
ハッシュ

2

もう1つの方法は、mod()関数(または '%')を使用することです。ゼロ以外の剰余がある場合、除算の整数結果を増分します。


1

私は以下を行い、オーバーフローを処理します:

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

結果が0の場合にこの拡張機能を使用します。

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

また、現在のページ番号の場合(尋ねられませんでしたが、役立つ可能性があります):

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

0

ゼロのテストで分岐を削除する別の方法:

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

これがC#で機能するかどうかはわかりませんが、C / C ++で機能するはずです。


-1

反復できる一般的なメソッドには、興味深いものがあります。

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

グアバにも同様の方法(Lists.partition(List, int))があり、皮肉なことsize()に、結果List(の時点でr09)の方法は、ブランドンデュレットの回答で言及されたオーバーフローバグの影響を受けます
finnw

-2

私は、分を時間と分に変換する必要があるのと同じようなニーズがありました。私が使ったのは:

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

以下は、上記のソリューションよりも丸めを行う必要がありますが、パフォーマンスは犠牲になります(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;
}

-4

浮動小数点除算を行ってから、ceiling関数を使用して、値を次の整数に切り上げます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.