我编写了一个C程序,用于查找用户定义范围内的所有Armstrong数字。没有编译错误,也没有逻辑错误,但是在运行Turbo C编译器生成的程序时,它以某种方式打印000000 ....并挂起。下面是编写的代码。
#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 :(得分:2)
您需要添加两个临时变量。
#include <stdio.h>
#include <stdlib.h>
int main()
{
int n1, n2, sum, tmp, digit;
printf("Enter the lower limit: ");
scanf("%d", &n1);
printf("\nEnter the upper limit: ");
scanf("%d", &n2);
while (n1 <= n2) {
sum = 0;
tmp = n1;
while (tmp != 0) {
digit = tmp % 10;
sum += digit * digit * digit;
tmp = tmp / 10;
}
if (sum == n1) {
printf("%d\n", n1);
}
n1++;
}
exit(EXIT_SUCCESS);
}
问题:不计算所有阿姆斯壮数(obligatory OEIS link)只有三位数。要计算所有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)。有很多优化空间。
编辑在两次实验中找到了一些时间(obligatory XKCD)。
以上代码需要大约10.5分钟才能找到前34个阿姆斯特朗号码(下限= 0,上限= 912985154)。如果我们从数学库中删除函数并自行滚动,我们可以节省超过一半的运行时间。
#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秒内运行 - 但半分钟并非一无所获。
使用编译器(GCC 4.8.1)优化-O3
:3分43秒
有点慢(大约相同,我刚刚使用time
并且它不是唯一在这里运行的进程)
外部循环邀请尝试并行方法(这里使用OpenMP来保持简单)
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);
}
运行时间为2分15秒(用户:8m11.933s,因为有4个CPU并行工作,而#34;用户&#34;全部添加),但输出当然没有排序。
PS:很多C&amp; P都参与了这篇文章,可能会在这里和那里造成一个或另一个错误。请在下面的评论中告诉我,我可以修理它们。答案 1 :(得分:1)
完全重写我以前的解决方案。它可以在0.02秒内解决912985153(带-O3优化)在我的系统上无需并行处理。它可以在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;
}
我在代码中一直要小心,如果你有128位的unsigned long long
,并且有足够的耐心,你应该能够达到最大的自恋数字。
128 BIT UPDATE
我接受了@ chqrlie的建议并制作了上面使用gcc / clang 128位整数的修改版本。这使得程序在运行三天半之后可以达到基数为10的第88个和最后一个自恋数。(模拟的128位整数比硬件64位整数慢。)具体更改:
定义了以下内容,并将所有unsigned long long
声明替换为uint128_t
:
typedef unsigned __int128 uint128_t;
从此SO question about how to print uint128_t numbers抓取uint128_to_str()
和uint128_to_str_iter()
。由于printf
不会直接处理它们,因此这些例程会将uint128_t
数字转换为您可以打印的字符串。最后,我简化了main()
例程,因此我无需处理任何其他数字转换 - 只是简单地计算:
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));
}
}
答案 2 :(得分:1)
以下是使用附加功能的cdlane代码的改进版本
优化。它可以在 1.2秒中解决最多912985153
(在我的笔记本电脑上进行clang -O3
优化)没有并行
处理
额外的优化是:
以递增方式更新字符串表示,而不是重复调用sprintf
当部分金额对于当前金额太大或太小时碰撞候选人编号。
以下是代码:
#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;
}
再运行1分50秒,这些额外的阿姆斯特朗数量达到10 11 :
4679307774
32164049650
32164049651
40028394225
42678290603
44708635679
49388550606
82693916578
94204591914
理论上, 1 可能达到基数10的最大Armstrong数,115132219018763992565095597973971522401
具有128位整数,它仍然需要非常很长一段时间。
编辑其他优化提供了另外100倍的速度因子,并产生了超过10 18 的额外阿姆斯特朗数字:
28116440335967
4338281769391370
4338281769391371
21897142587612075
35641594208964132
35875699062250035
1 虽然最后一个解决方案适合128位,但上述算法实际上会失败,因为它会计算10 40 ,这几乎不会超过128位无符号整数的容量