web-dev-qa-db-ja.com

与えられた数が最速で15で割り切れるかどうかを確認する方法は?

プロセッサでの除算には時間がかかるため、数値が他の数値と割り切れるかどうかを最速でチェックする方法を尋ねたいと思います。私の場合、数値が15で割り切れるかどうかをチェックする必要があります。

また、私はウェブを調べており、fun数がいくつかの数で割り切れないかどうかを確認する方法を見つけましたが、高速なオプションを探しています。

注:除算には時間がかかるため、/%なしで答えを探しています。

17
user2532605

乗算は除算よりも時間がかからないので、これを試すことができます:

inline bool divisible15(unsigned int x)
{
    //286331153 = (2^32 - 1) / 15
    //4008636143 = (2^32) - 286331153
    return x * 4008636143u <= 286331153u;
}

この方法は2^32-1(最大32ビット値)は15の割り切れる数ですが、たとえば7をとると、動作しているように見えますが、すべての場合で動作するとは限りません。

EDIT:this を参照してください。一部のコンパイラでは、このソリューションが(であることを証明します)はモジュールよりも高速です。

EDIT:ここ は説明と一般化です。

24
ST3

答えを探しに来るかもしれない他の学習者のための義務的な答え。

if (number % n == 0)

mostの場合、スマートモダンコンパイラを信頼して、いつでもこれを実行できます。

これは、楽しい方法を学ぶことに落胆するという意味ではありません。これらのリンクをチェックしてください。

高速分割可能性テスト(2、3、4、5、..、16による)?

ビットツイドルハック

32
Max

i % 15 == 0


  1. コンパイラーは15が決して変更されないことを容易に理解できるため、mod操作で必要な最適化を自由に行うことができます。あなたがそうするより良い方法を考えていなかったなら、この種の最適化をするのはコンパイラー作成者の仕事です。

  2. たとえば、最初のビットをチェックするだけなので、数値が2で割り切れるかどうかをチェックするのは非常に簡単です。コンパイラの作成者はこれを知っており、自分でコードを記述して最初のビットをチェックすることもできますが、特に成熟したコンパイラでは、これらのことを何年も考えて取り組んでいます。このタイプの最適化は、命令または2を変更するだけで済むため、非常に簡単に行うことができます。より優れたレジスタ割り当てなどの最適化は、達成がはるかに困難です。

  3. 考慮すべきもう1つのことは、コンパイラはそれが存在するシステム用に作成されたということです。一方、コードはどこでも同じです。1つのシステムで同じくらい高速かもしれない奇妙なコードを書く場合(おそらくまだ高速ではありません) )しかし、特別なハードウェア最適化を備えた別のシステムでは、コードが桁違いに失われる可能性があります。難解なコードを書いて、可分性をチェックするため、コンパイラーが単一のハードウェアopに最適化できることをコンパイラーが認識していない可能性が高いので、明白なことを書いておくと、コンパイラーにとってより便利で簡単になります。

  4. あなたは実際に速度がコードを書くことが重要であることをチェックしていないので、奇妙な方法は次の人にとってコードを非常に読みにくくし、エラーを起こしやすくします( 時期尚早な最適化はすべての悪の根源です

  5. 入力が16ビット、32ビット、64ビットのいずれであっても、ビット操作に依存しないため、引き続き機能します。

  6. コンパイラの作成者が実装していない場合でも、誰かが自分で実装することは明らかに可能です

24
aaronman

合理的に現代的なプロセスでは、15で割ることはそれほどひどいはずではありません。 AMD最適化ガイドは、商(分割される値)に基づいてそれを定義し、商の最上位ビットの8 +ビット位置を取ります。したがって、数値に63番目のビットが設定されている場合、71サイクルになります。これはもちろん、かなり長い命令です。しかし、上位ビットにいくつかのゼロがある32ビットの数値の場合、30〜40サイクルについて話しています。数値が16ビット値に収まる場合、最大は23サイクルです。

残りを取得するには、さらに1クロックサイクルかかります。

もちろん、これをずっとやっているのであれば、もちろん、この時間がかなり長いかもしれませんが、それを回避する簡単な方法があるかどうかはわかりません。

他の人が言ったように、コンパイラはそれをより良いものに置き換えることができるかもしれません。しかし、15にはありません。私の知る限り、明らかに高速な解決策があります(15ではなく16がある場合は、x & 15のトリックを使用できます)。

範囲が限られている場合は、[たとえば[vector<bool>]を作成できます。これにより、エントリごとに1ビットが格納されます]が、キャッシュされていないメモリアクセスにかかる時間が同じ問題にすぐに遭遇します。除算操作として...

数字を合計して3、5などで除算するかどうかを判断する興味深い方法がいくつかありますが、残念なことに、これらは長い除算のシーケンスを含む10進数のみに基づいて機能します。

6
Mats Petersson

他のアプローチより遅いと思われますが、加算、ビット単位、およびシフトのみを使用する別のアプローチを次に示します。

int divisible15(unsigned int x) {
        if (x==0) return 1;
        x = (x & 0x0f0f0f0f) + ((x&0xf0f0f0f0)>>4);
        x = (x & 0x00ff00ff) + ((x&0xff00ff00)>>8);
        x = (x & 0x0000ffff) + ((x&0xffff0000)>>16);
        x = (x & 0x0f) + ((x&0xf0)>>4);
        return x==15;
}

考え方は、基数16で15で割り切れる数は、基数10で9で割り切れる数に似ています。桁の合計は15で割り切れる必要があります。
そのため、コードはすべての16進数字を合計し(ビットのカウント方法と同様)、合計は15に等しくなければなりません(0を除く)。

5
ugoren

さて、16進数表現があれば、頭の中でそれを行うのは非常に簡単です。数字が1つになるまで、すべての数字を合計します。答えが「0xf」の場合、15で割り切れます。

0x3a98:3 + 0xa + 9 + 8 = 0x1e = 1 + 0xe = 0xfなので、15で割り切れます。

これは、X-1のすべての要素で機能します。Xは、数値を表すために使用される基数です。 (小さい因子の場合、最後の数字は因子で割り切れなければなりません)。

ただし、コードが高速であるとは期待しないでください。

0
Roddy