与えられた数の桁数を見つけるための解決策は複数あります。
例えば:
方法1:
_int findn(int num)
{
char snum[100];
sprintf(snum, "%d", num);
return strlen(snum);
}
_
方法2:
_int findn(int num)
{
if (num == 0) return 1;
int n = 0;
while(num) {
num /= 10;
n++;
}
return n;
}
_
方法3:
_int findn(int num)
{
/* math.h included */
return (int) log10(num) + 1;
}
_
問題は-最も効率的な方法は何ですか? method-2がO(n)
であることは知っていますが、method-1とmethod-3はどうですか?ライブラリ関数の実行時の複雑さを見つけるにはどうすればよいですか?
以下はさらに効率的です。
int findn(int num)
{
if ( num < 10 )
return 1;
if ( num < 100 )
return 2;
//continue until max int
}
バイナリ検索を行うことでこれをさらに最適化できますが、それはやり過ぎです。
現在のところ、受け入れられて最も承認されている回答は、負の数値に対して( still )が正しくありません。回答者が時間をかけてテストした場合そして、負の数では壊れていることを知ると、単にsnprintf
を使用するだけで、マシンがこれまでよりも多くの時間を無駄にした可能性があります。
int count_digits(int arg) {
return snprintf(NULL, 0, "%d", arg) - (arg < 0);
}
1980年代にはもういません。私たちのようにコーディングを停止します。私はC標準の熱狂者であり、ここでの私のお気に入りの答えは Tao Fengの答え ...でしたが、それでも why にはなりませんでしたこれまでのところ最も効率的な答え。この回答では、私は彼の回答が以下を検討することでさらに改善できることを示すつもりです:
snprintf
をキャッシュに戻す可能性があります。以下は、生産性を妨げるマイクロ最適化について説明しています。回答で提供した情報が不足しているため、現在のところ質問に答える人は、仮定を立てずに証拠を提供できません。 :
ボトルネックを測定することで、つまりこれらの各ソリューションのプロファイリング( "benchmarking" )をプロファイリングすることで、マイクロ最適化を回避できますon your system正しく。解決策が問題を解決しない場合、それは解決策ではないので、考慮されるべきではありません...正しく行われると、これはミクロ最適化を排除するはずです。一部のコンパイラは、インテリジェントなprofile-guided optimisationを提供しています。これは、一般に、ブランチとオブジェクトをキャッシュの局所性のために再編成することで20〜30%を削減しますそして自動的に行います。
数字のカウントについてはすでに説明しましたが、間違いなくあなたの質問に答えると思いますが、考えるドンするときに数字をカウントする必要がある場合があります't 、および数字のカウントのオーバーヘッドを削除する機能は、人手時間およびのマシン時間の両方で、非常に望ましい最適化をもたらす可能性があります。
たとえば、これらの数字を含む文字列を格納するために割り当てるバイト数を計算する場合は、プリプロセッサマクロを使用して最大桁数(または文字、記号)、および保存しようとする一時的なストレージの貴重なバイトは、ロジックに追加されたマシンコードのバイトよりもはるかに多く、これは私にはかなりのコストのようです。プログラマーがプリプロセッサーマクロを使用する利点もあります。同じマクロを任意の整数型に使用できます。 この問題の解決策については 私の答え から この質問 を参照;結局のところ、自分を繰り返す意味はありません...
GCC/Clang __builtin_clz()
またはMicrosoft Visual C _BitScanReverse()
組み込み関数は、多くのマシンで単一のマシン命令にコンパイルされます。これをO(1)ソリューションの基礎として使用できます。32ビット実装は次のとおりです。
#include <limits.h>
#include <stdint.h>
/* Return the number of digits in the decimal representation of n. */
unsigned digits(uint32_t n) {
static uint32_t powers[10] = {
0, 10, 100, 1000, 10000, 100000, 1000000,
10000000, 100000000, 1000000000,
};
static unsigned maxdigits[33] = {
1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5,
5, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10,
};
unsigned bits = sizeof(n) * CHAR_BIT - __builtin_clz(n);
unsigned digits = maxdigits[bits];
if (n < powers[digits - 1]) {
-- digits;
}
return digits;
}
最初のメソッドを次のように書くことができると思います
int findn(int num)
{
char snum[100];
return sprintf(snum, "%d", num);
}
sprintfは書き込まれた文字数を返し、strlenの呼び出しを保存できるためです。
効率については、sprintfの実装に依存すると思います。sprintfのソースを見つけて、それが効率的かどうかを確認する必要があるかもしれません。
1つのライナー:for(digits = 0; num > 0; digits++) num /= 10;
バイナリ検索を試してください。明確にするために、符号付き32ビット整数を想定します。まず、_x < 10000
_かどうかを確認します。次に、答えに応じて、_x < 100
_または_x < 1000000
_のように続きます。
それがO(log n)
です。ここで、n
は桁数です。
これらの関数は、正でない数に対して大幅に異なる結果をもたらします(最悪は方法3)。そのため、それらの時間の複雑さを比較することは、疑わしい値です。私はすべての場合に必要な答えを与えるものを使用します。コンテキストがないと、それが何であるかを知ることができません(おそらく方法3ではありません)。
方法1の場合、findn(0) == 1
、およびfindn(-n) == digits in n + 1
(負符号のため)。
方法2の場合、findn(0) == 0
、およびfindn(-n) == digits in n
。
メソッド3では、findn(0) == INT_MIN
、およびfindn(-n) == INT_MIN
も同様です。
sprintf()
はメソッド2を使用して数値を出力します(出力する文字列の長さを決定し、次に文字列の各文字を出力するため)、本質的に遅くなります。
番号3はおそらくln()
の多項式近似を含み、1を超える除算が含まれるため、速度も遅くなります( here's afastln()実装、フロート除算がまだ含まれているため、速度が遅くなります)。
だから私の暫定的な推測は、方法2が進むべき道だということです。
これは、この問題に取り組むためのかなり自由な方法であることに注意してください。古き良き時代のミリオン反復を各関数でテストすると、結果がわかると思います。しかし、それも蜂bruteforceでしょうか?
方法2のみが実際の結果を提供することに注意してください。他の方法には、正しく調整する必要のある欠陥があります(Aaronの回答を参照)。したがって、方法2を選択するだけです。
printf関数は、正常に印刷された桁数を返します。
int digits,n=898392;
digits=printf("%d",n);
printf("Number of digits:%d",digits);
出力:
898392
桁数:6
これが私の解決策です... 100桁までカウントされます。
max_digits = 100
int numdigits(int x) {
for (int i = 1; i < max_digits; i++) {
if (x < pow(10, i)) {
return i;
}
}
return 0;
}
ログの使用は良いオプションかもしれません...
int
をdouble
にキャストして、精度を失うことなく戻すことができると確信している場合。実装例...
int num_digits(int arg) {
if (arg == 0) {
return 1;
}
arg = abs(arg);
return (int)log10(arg)+1;
}