2016-08-20 7 views
-3

ユーザー定義の範囲内のすべてのArmstrong番号を検索するCプログラムを作成しました。コンパイルエラーと論理エラーはありませんでしたが、Turbo Cコンパイラで生成されたプログラムを実行すると、何とか000000 ....とハングします。以下は、書かれたコードです。C:のプログラムでArmstrongの数値を計算しようとしています.....プログラムが000 .....を出力してハングします

#include <conio.h> 
#include <stdio.h> 

void main() { 
    int n1, n2, sum, n_s; 
    printf("Enter the lower limit: "); 
    scanf("%d", &n1); 
    fflush(stdin); 
    printf("\nEnter the upper limit: "); 
    scanf("%d", &n2); 
    while (n1 <= n2) { 
     n_s = n1; 
     sum = 0; 
     while (n1 != 0) { 
      n1 = n1 % 10; 
      sum += n1 * n1 * n1; 
      n1 = n1/10; 
     } 
     if (sum == n_s) { 
      printf("%d", n_s); 
     } 
     n1++; 
    } 
    getch(); 
} 

どこが間違っている可能性があるか分かりません。

+0

「コンパイルエラーはありませんでした」 - はいあります。このコードはコンパイルされず、はるかに少なく実行されます。 'n1 ++'の後には文の終了はありません。それがコンパイルされたとしても、未定義の動作*が蔓延しています。 'n1'と' n2'のどちらも決まった値に設定されていません。 – WhozCraig

+0

Cで使用する前に常に変数を初期化します。これはVBではありません。 – alk

+0

申し訳ありませんが、実際にscanfでそれらをスキャンし、n1とn2のユーザー入力を受けましたが、それでも問題は解決しません。コードスニペットで書くことができないのは残念です – shanky

答えて

2

一時変数を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つまたは他のエラーが発生している可能性があります。私がそれらを修理できるように、以下のコメントで私に知らせてください。

+0

驚くべき努力のためのUV ...これを延期し、これをやめさせるために何が重要であったに違いないのだろうか。 – chqrlie

+0

バーが開くまで@chqrlieはまだ30分;-) – deamentiaemundi

+0

これをさらに最適化するための努力をしてもらえませんでしたが、1つのスレッドで1.2秒に短縮しました。この方法ですべてのアームストロング数を計算することはまだ手の届きません。 – chqrlie

1

私の以前の解決策の完全な書き換え。私のシステムでは、並列処理を行わずに0.02秒で(-O3最適化で)912985153に解決できます。それはちょうど9秒以上で64ビットの制限(4929273885928088826)に達することができます。

キー最適化は、最小数の集合(すなわち、順列置換)のみをテストすることです。そして、複雑な数学を避けてください。アルゴリズムはthe code in the OEIS definition of this sequence.

#include <stdio.h> 
#include <stdbool.h> 
#include <stdlib.h> 

#define MORE_THAN_ENOUGH (128) 

unsigned long long sort_digits(unsigned long long number) { 
    unsigned long long sortedNumber = 0; 

    for (size_t i = 0; i < 10; i++) { 
     unsigned long long temporary = number; 

     while (temporary > 0) { 
      int digit = temporary % 10; 

      if (digit == i) { 
       sortedNumber *= 10; 
       sortedNumber += digit; 
      } 
      temporary /= 10; 
     } 
    } 

    return sortedNumber; 
} 

int compare(const void *a, const void *b) { 
    unsigned long long x = *(unsigned long long *) a; 
    unsigned long long y = *(unsigned long long *) b; 

    return (x < y) ? -1 : (y < x) ? : 0; 
} 

unsigned long long power(unsigned long long number, unsigned exponent) { 
    unsigned long long total = 1; 

    for (unsigned i = 0; i < exponent; i++) { 
     total *= number; 
    } 

    return total; 
} 

size_t generate_narcissistic(size_t places, unsigned long long results[]) { 
    char digits[places]; 
    unsigned long long minimum = power(10, places - 1) - 1; 
    size_t results_count = 0; 

    for (size_t i = 0; i < places; i++) { 
     digits[i] = 0; 
     } 
    digits[places - 1] = 1; 

    bool finished = false; 

    while (!finished) { 
     unsigned long long sum = 0, number = 0; 

     for (size_t i = 0; i < places; i++) { 
      number *= 10; 
      number += digits[i]; 
      sum += power(digits[i], places); 
     } 

     if (sum > minimum) { 
      unsigned long long sorted = sort_digits(sum); 
      if (sorted == number) { 
       results[results_count++] = sum; 
      } 
     } 

     for (int i = places - 1; i >= 0; i--) { 
      digits[i] += 1; 

      if (digits[i] <= 9) { 
       break; 
      } 

      if (i == 0) { 
       finished = true; 
       break; 
      } 

      for (int j = i - 1; j >= 0; j--) { 
       if (digits[j] != 9) { 
        digits[i] = digits[j] + 1; 
        break; 
       } 
      } 
     } 
    } 

    if (results_count != 0) { 
     qsort(results, results_count, sizeof(unsigned long long), &compare);; 
    } 

    return results_count; 
} 

int main(int argc, char *argv[]) { 
    unsigned long long n0, n1, n2, narcissistic[MORE_THAN_ENOUGH]; 

    if (argc > 1) { 
     n1 = strtoull(argv[1], NULL, 0); 
    } else { 
     printf("Enter the lower limit: "); 
     scanf("%llu", &n1); 
    } 

    if (argc > 2) { 
     n2 = strtoull(argv[2], NULL, 0); 
    } else { 
     printf("Enter the upper limit: "); 
     scanf("%llu", &n2); 
    } 

    char scratch[MORE_THAN_ENOUGH]; 

    size_t lower_limit = sprintf(scratch, "%llu", n1); 
    size_t upper_limit = sprintf(scratch, "%llu", n2); 

    for (size_t places = lower_limit; places <= upper_limit; places++) { 
     size_t count = generate_narcissistic(places, narcissistic); 

     for (size_t i = 0; i < count; i++) { 
      n0 = narcissistic[i]; 

      if (n0 >= n1 && n0 <= n2) { 
       printf("%llu\n", n0); 
      } 
     } 
    } 

    return 0; 
} 

をモデルにしている私はあなたがunsigned long long 128のビット、および十分な忍耐を持っている場合、あなたは最大の自己陶酔数に達することができなければならないこと、そのようなコードで注意することを試みてきました。私は@のchqrlieのアドバイスを取り、その上での修正版を作っ

128 BITのUPDATEは、GCC /打ち鳴らす128ビット整数を使用しています。これにより、3日半実行した後、プログラムは10番目の88番目のナルシシズム数に達することができました。(エミュレートされた128ビット整数はハードウェアの64ビット整数よりも遅いです)特定の変更:

以下を定義し、uint128_tですべての私のunsigned long long宣言を置き換え:

typedef unsigned __int128 uint128_t; 

このSO question about how to print uint128_t numbersからuint128_to_str()uint128_to_str_iter()をつかみました。 printfは直接それらを処理しないので、これらのルーチンはuint128_tの数値を、代わりに印刷できる文字列に変換します。

ここ
uint128_t n0, narcissistic[MORE_THAN_ENOUGH]; 

for (size_t places = 1; places < 40; places++) { 
    size_t count = generate_narcissistic(places, narcissistic); 

    for (size_t i = 0; i < count; i++) { 
     n0 = narcissistic[i]; 

     printf("%s\n", uint128_to_str(n0)); 
    } 
} 
+0

私はこれをさらに最適化するための努力をしていませんでしたが、1つのスレッドで1.2秒に短縮しました。この方法ですべてのアームストロング数を計算することはまだ手の届きません。 – chqrlie

+0

@chqrlie、誰か​​が128ビット整数で私の修正されたソリューションを実行できる場合、最大のArmstrongが手に入るかもしれません。 – cdlane

+0

'gcc'と' clang'はともに、Intel 64ビットプラットフォーム上で128ビットの整数をサポートします。私は 'unsigned __int128'型を使用しており、64ビットの制限を超えて最大の数を検証するために128ビットの文字列変換関数を書いていますが、それはまだ私の現在の実装よりも妥当な時間内です。 – chqrlie

1

は、追加 の最適化を使用していますcdlaneのコードの改良版である:単にカウントアップ - 私は、他の数の変換に対処する必要はありませんでしたので、最後に、私はmain()ルーチンを簡素化。私のラップトップでは 処理なしで1.2秒clang -O3最適化)で最大912985153を解決できます。

余分な最適化は、以下のとおりです。

  • は、文字列表現を更新し、増分の代わりに、部分和が大きすぎるか、または現在の1のために小さくなりすぎると候補数をぶつけ繰り返し

  • のsprintfを呼び出します。ここで

はコードです:追加の1分50秒を実行

#include <stdio.h> 
#include <stdlib.h> 

unsigned long long next_narcissistic(unsigned long long number, unsigned long long max) { 
    static size_t power = 0; 
    static unsigned long long powers[10]; 
    static unsigned long long maxleft[42]; /* enough for 128 bit unsigned long long */ 
    static unsigned long long scale10[42] = { 1 }; 

    char string[64]; 
    size_t length; 

    while (number < max) { 

     length = sprintf(string, "%llu", number); 

     if (length > power) { 
      for (size_t i = power; i < length; i++) { 
       scale10[i + 1] = scale10[i] * 10; 
      } 
      for (size_t digit = 0; digit < 10; digit++) { 
       unsigned long long total = 1; 

       for (size_t j = 0; j < length; j++) { 
        total *= digit; 
       } 
       powers[digit] = total; 
      } 
      for (size_t i = 0; i <= length; i++) { 
       maxleft[i] = (length - i) * powers[9]; 
      } 
      power = length; 
     } 

     unsigned long long sum = 0; 
     unsigned long long max0 = max < scale10[length] ? max : scale10[length] - 1; 

     for (size_t i = 0;;) { 
      sum += powers[string[i++] - '0']; 

      if (i == length) { 
       if (sum == number) 
        return number; 

       /* bump to next number and update string */ 
       number += 1; 
       if (number > max0) 
        break; 

       for (;;) { /* i is always > 0 because number <= max0 */ 
        i--; 
        sum -= powers[string[i] - '0']; 
        if (++string[i] <= '9') 
         break; 
        string[i] = '0'; 
       } 
       continue; 
      } 

      if (sum <= number) { 
       if (sum + maxleft[i] >= number) 
        continue; 
      } else { 
       sum -= powers[string[--i] - '0']; 
      } 

      /* bump to next possible number */ 
      number += scale10[length - i] - number % scale10[length - i]; 
      if (number > max0) 
       break; 

      for (;;) { /* i is always > 0 because number <= max0 */ 
       i--; 
       sum -= powers[string[i] - '0']; 
       if (++string[i] <= '9') { 
        break; 
       } 
      } 
      for (size_t j = i + 1; j < length; j++) { 
       string[j] = '0'; 
      } 
     } 
    } 
    return 0; 
} 

int main(int argc, char *argv[]) { 

    unsigned long long n1, n2; 

    if (argc > 1) { 
     n1 = strtoull(argv[1], NULL, 0); 
    } else { 
     printf("Enter the lower limit: "); 
     scanf("%llu", &n1); 
    } 

    if (argc > 2) { 
     n2 = strtoull(argv[2], NULL, 0); 
    } else { 
     printf("Enter the upper limit: "); 
     scanf("%llu", &n2); 
    } 

    for (unsigned long long n = n1; n <= n2; n++) { 
     n = next_narcissistic(n, n2 + 1); 
     if (n == 0) 
      break; 
     printf("%llu\n", n); 
    } 

    return 0; 
} 

は10 件までこれらの余分なアームストロング番号を生成します。

4679307774 
32164049650 
32164049651 
40028394225 
42678290603 
44708635679 
49388550606 
82693916578 
94204591914 

また、それは理論的にことは可能であろうベース10の最大のアームストロング数に達するには、128ビット整数の115132219018763992565095597973971522401、それでもake a 非常に長い時間です。

さらなる最適化は、別の100倍の速度係数を提供し、10 件までこれらの余分なアームストロング番号を生成EDIT:

28116440335967 
4338281769391370 
4338281769391371 
21897142587612075 
35641594208964132 
35875699062250035 

最終溶液を128ビットに収まるが、上記のアルゴリズムは実際には失敗するでしょう。なぜなら、それは128ビットの符号なし整数の容量をほとんど上回らない10 を計算するからです。

+0

私はベンチマーク912985153に0.02秒、64ビット限界にわずか9秒を超える新しいソリューションを〜60xで作成しました。それはあなたが出発点として使用しなかったコードを投票したが、あなたが再利用した自分のコードに投票しなかったことに気づいていなかった。私は私の名誉を守るしかなかった。 ;-) – cdlane

+0

@cdlane:おっと、私の見解はどうですか?すぐに訂正される。その間に、私はいくつかの改善点を見出し、14msで最初の10億を達成しました。あなたの方法では、ベース10のすべてのAmstrong数を計算できますか?私はまだ遅すぎてあまりにも遅いです。 – chqrlie

+0

署名されていない__int128についてのあなたの助言(私はそれがどのようにうまくいったかについて私の答えに更新を追加しました)と、両方のソリューションをより速く進めるために、それは本当に違いを生む、速いハードウェアではなく、良いアルゴリズムであることを思い出してください。 – cdlane

関連する問題