一時変数を2つ追加する必要があります。
問題:すべてのアームストロング番号(obligatory OEIS link)は3桁の数字だけを計算するわけではありません。すべてのn-narcissistic数を計算するには、小数点の桁数を知り、それに応じてパワーを計算する必要があります。短いスケッチとして:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
// ALL CHECKS OMMITTED!
int decimal_digits(int number)
{
// actually wrong, but we don't use negative numbers here
// and can safely set log(0) = 1
if (number <= 0) {
return 1;
}
return (int) (floor(log10(number)) + 1);
}
int main()
{
int n1, n2, sum, tmp, digit, dec_digits;
printf("Enter the lower limit: ");
scanf("%d", &n1);
printf("\nEnter the upper limit: ");
scanf("%d", &n2);
while (n1 <= n2) {
sum = 0;
dec_digits = decimal_digits(n1);
tmp = n1;
while (tmp != 0) {
digit = tmp % 10;
sum += (int) (floor(pow((double) digit, (double) dec_digits)));
tmp = tmp/10;
}
if (sum == n1) {
printf("%d\n", n1);
}
n1++;
}
exit(EXIT_SUCCESS);
}
しかし、それは本当にスケッチです!醜いキャスティングがたくさんあり、環境などについてさらに多くの前提があります!
アームストロング数は非常に速く1741725ですが、残りの数分は必要です(5分後には146511208)。最適化の余地はたくさんあります。
EDITは、2回の実験のために若干の時間(obligatory XKCD)を見出した。
最初の34個のアームストロング数(下限= 0、上限= 912985154)を見つけるには、上記のコードが約10.5分必要です。数学ライブラリの関数を取り除き、独自の関数を呼び出すと、その実行時間の半分以上を節約できます。
#include <stdio.h>
#include <stdlib.h>
// ALL CHECKS OMMITTED!
int decimal_digits(int number);
/*
* If you know for sure that e.g.: sizeof(unsigned long)*CHAR_BITS = 32
* and sizeof(unsigned long long)*CHAR_BITS = 64 you can replace
* the inclusion of stdint.h with the following type definitions
*
* For 32 bit x86:
* typedef unsigned int uint32_t;
* typedef unsigned long long int uint64_t;
*
* For 64 bit x86:
* typedef unsigned int uint32_t;
* typedef unsigned long int uint64_t;
*/
#include <stdint.h>
uint64_t own_pow(uint32_t base, uint32_t exponent);
uint32_t own_ilogb(uint32_t base, uint32_t n);
int decimal_digits(int number)
{
// actually wrong, but we don't use negative numbers here
// and can safely set log(0) = 1
if (number < 10) {
return 1;
}
return (int) (own_ilogb(10,(uint32_t) number) + 1);
}
uint64_t uipow(uint32_t base, uint32_t exponent)
{
uint64_t power = 1;
uint64_t big_base = (uint64_t) base;
while (exponent) {
if (exponent % 2 == 1) {
power *= big_base;
}
exponent >>= 1;
big_base *= big_base;
}
return power;
}
uint32_t own_ilogb(uint32_t base, uint32_t n)
{
uint32_t low = 0, big_low = 1, high = 1, mid, big_mid;
uint64_t big_high = base;
// interval reduction (more useful for big-integers)
while (big_high < n) {
low = high;
big_low = big_high;
high <<= 1;
big_high *= big_high;
}
// the actual bisection
while ((high - low) > 1) {
mid = (low + high) >> 1;
big_mid = big_low * uipow(base, mid - low);
if (n < big_mid) {
high = mid;
big_high = big_mid;
}
if (n > big_mid) {
low = mid;
big_low = big_mid;
}
if (n == big_mid)
return mid;
}
if (big_high == n) {
return high;
}
return low;
}
int main()
{
int n1, n2, sum, tmp, digit, dec_digits;
printf("Enter the lower limit: ");
scanf("%d", &n1);
printf("\nEnter the upper limit: ");
scanf("%d", &n2);
while (n1 <= n2) {
sum = 0;
dec_digits = decimal_digits(n1);
tmp = n1;
while (tmp != 0) {
digit = tmp % 10;
sum += (int) uipow((uint32_t) digit, (uint32_t) dec_digits);
tmp = tmp/10;
}
if (sum == n1) {
printf("%d\n", n1);
}
n1++;
}
exit(EXIT_SUCCESS);
}
(4分と上記の範囲のための7秒で実行)
own_ilogb()
のバイナリ検索アルゴリズムが遅くなります、私たちは、これが保存されません
// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogDeBruijn
// vid.: http://stackoverflow.com/questions/7365562/de-bruijn-like-sequence-for-2n-1-how-is-it-constructed for an explanation
static const int tab32[32] = {
0, 9, 1, 10, 13, 21, 2, 29,
11, 14, 16, 18, 22, 25, 3, 30,
8, 12, 20, 28, 15, 17, 24, 7,
19, 27, 23, 6, 26, 5, 4, 31
};
static int ilog2(uint32_t value)
{
value |= value >> 1;
value |= value >> 2;
value |= value >> 4;
value |= value >> 8;
value |= value >> 16;
return tab32[(uint32_t) (value * 0x07C4ACDD) >> 27];
}
int decimal_digits(int number)
{
double logten2two = 3.3219280948873623478703194294893901759;
// actually wrong, but we don't use negative numbers here
// and can safely set log(0) = 1
if (number < 10) {
return 1;
}
return (int) (ilog2((uint32_t) number)/logten2two + 1.0);
}
とそれを交換することができ多くの時間 - それは3分38秒で実行されます - しかし、0.5分は何もありません。
コンパイラ(GCC 4.8.1)の最適化を使用して-O3
:3分43秒 少し遅く(ほぼ同じ、私はちょうどtime
を使用し、それがここで実行中のプロセスだけではなかった)
外側のループ招待(物事をシンプルに保つためにOpenMPとここ)
2分15秒で走る
int main()
{
int n1, n2, sum, tmp, digit, dec_digits;
int iter;
printf("Enter the lower limit: ");
scanf("%d", &n1);
printf("\nEnter the upper limit: ");
scanf("%d", &n2);
#pragma omp parallel for
for(iter = n1;iter <= n2;iter++) {
sum = 0;
dec_digits = decimal_digits(iter);
tmp = iter;
while (tmp != 0) {
digit = tmp % 10;
sum += (int) uipow((uint32_t) digit, (uint32_t) dec_digits);
tmp = tmp/10;
}
if (sum == iter) {
printf("%d\n", iter);
}
}
exit(EXIT_SUCCESS);
}
(ユーザーを並列的なアプローチを試して:8m11.933sは、4つのCPUが、それで働いていたので、平行して「ユーザ」は、すべてを加算します)、もちろん出力はソートされていません。
PS:この投稿にはC & Pが多く含まれていますが、ここに1つまたは他のエラーが発生している可能性があります。私がそれらを修理できるように、以下のコメントで私に知らせてください。
「コンパイルエラーはありませんでした」 - はいあります。このコードはコンパイルされず、はるかに少なく実行されます。 'n1 ++'の後には文の終了はありません。それがコンパイルされたとしても、未定義の動作*が蔓延しています。 'n1'と' n2'のどちらも決まった値に設定されていません。 – WhozCraig
Cで使用する前に常に変数を初期化します。これはVBではありません。 – alk
申し訳ありませんが、実際にscanfでそれらをスキャンし、n1とn2のユーザー入力を受けましたが、それでも問題は解決しません。コードスニペットで書くことができないのは残念です – shanky