水仙花数指的是一个n位数(n≥3), 它的每个位上的数字的n次幂之和等于它本身.(例如:1^3 + 5^3 + 3^3 = 153) , 理论上, 最大的水仙花数不超过34位
题目还是从 luciferisnotsatan 博客上看到的, 他的实现见:n位水仙花数
由于C++中整数的取值范围受限, 所以下面的代码最大只能计算到n = 19的情形
下面是我的实现代码:
#include <iostream>
using namespace std;
void show(int max_bits)
{
typedef unsigned long long long_type; // for gcc
// typedef unsigned __int64 long_type; // for vc and vs
if (max_bits > 34) {
max_bits = 34;
}
long_type base[10] =
{
0, 1, 8, 27, 64, 125, 216, 343, 512, 729
};
int number[34] = { 0 };
for (int i = 3; i <= max_bits; ++i) {
int count[10] = { 0 };
for (int j = 0; j < i; ++j) {
number[j] = 0;
++count[0];
}
while (true) {
int count_copy[10] = { 0 };
for (int j = 0; j < 10; ++j) {
count_copy[j] = count[j];
}
long_type value = 0;
for (int j = 0; j < i; ++j) {
value += base[number[j]];
}
bool equal = true;
long_type value_copy = value;
do {
if (--count_copy[value_copy % 10] < 0) {
equal = false;
break;
}
value_copy /= 10;
} while (value_copy > 0);
if (equal) {
for (int j = 0; j < 10; ++j) {
if (0 != count_copy[j]) {
equal = false;
break;
}
}
}
if (equal) {
cout << i << ": " << value << endl;
}
if (number[i - 1] < 9) {
--count[number[i - 1]];
++count[++number[i - 1]];
}
else {
int check_index = i - 2;
while (check_index >= 0 && 9 == number[check_index]) {
--check_index;
}
if (check_index < 0) {
break;
}
else {
--count[number[check_index]];
++count[++number[check_index]];
int new_number = number[check_index];
for (int j = check_index + 1; j < i; ++j) {
number[j] = new_number;
--count[9];
++count[new_number];
}
}
}
}
for (int j = 0; j < 10; ++j) {
base[j] *= j;
}
}
}
int main()
{
show(19);
return 0;
}
从代码量上, 我的比他的要少得多, 但效率不及他的, 我的在算16时就能明显感觉到等待了, 19时出来的第一个就等了2秒, 后面倒很快
我的出数据的顺序与百度百科上的相同, 估计算法相似, 另外百度百科中在19时少帖了一项
这次用到的手法又与 试题:网易笔试的一道题目 相似, 这个手法屡试不爽
想到value的计算是可优化的, 优化了下, 虽然现在还是要花8.5秒, 但相比前代码已减少2.5秒了:
#include <iostream>
using namespace std;
void show(int max_bits)
{
typedef unsigned long long long_type; // for gcc
// typedef unsigned __int64 long_type; // for vc and vs
if (max_bits > 34) {
max_bits = 34;
}
long_type base[10] =
{
0, 1, 8, 27, 64, 125, 216, 343, 512, 729
};
for (int i = 3; i <= max_bits; ++i) {
long_type value = 0;
int number[34] = { 0 };
int count[10] = { i };
while (true) {
int count_copy[10] = { 0 };
for (int j = 0; j < 10; ++j) {
count_copy[j] = count[j];
}
bool equal = true;
long_type value_copy = value;
do {
if (--count_copy[value_copy % 10] < 0) {
equal = false;
break;
}
value_copy /= 10;
} while (value_copy > 0);
if (equal) {
for (int j = 0; j < 10; ++j) {
if (0 != count_copy[j]) {
equal = false;
break;
}
}
}
if (equal) {
cout << i << ": " << value << endl;
}
if (number[i - 1] < 9) {
--count[number[i - 1]];
value -= base[number[i - 1]];
++number[i - 1];
++count[number[i - 1]];
value += base[number[i - 1]];
}
else {
int check_index = i - 2;
while (check_index >= 0 && 9 == number[check_index]) {
--check_index;
}
if (check_index < 0) {
break;
}
else {
--count[number[check_index]];
value -= base[number[check_index]];
++number[check_index];
++count[number[check_index]];
value += base[number[check_index]];
int new_number = number[check_index];
int change_count = i - check_index - 1;
count[9] -= change_count;
count[new_number] += change_count;
value -= change_count * (base[9] - base[new_number]);
for (int j = check_index + 1; j < i; ++j) {
number[j] = new_number;
}
}
}
}
for (int j = 2; j < 10; ++j) {
base[j] *= j;
}
}
}
int main()
{
show(19);
return 0;
}
再优化下:去除部分不可能的组合(过大和过小), 大约可节省1/10 - 1/4的组合尝试, 现用时7.5秒, 相对前代码, 节约1秒
#include <iostream>
using namespace std;
void show(int max_bits)
{
typedef unsigned long long long_type; // for gcc
// typedef unsigned __int64 long_type; // for vc and vs
if (max_bits > 34) {
max_bits = 34;
}
long_type base[10] =
{
0, 1, 8, 27, 64, 125, 216, 343, 512, 729
};
long_type lower_bound = 100;
long_type upper_bound = 1000;
for (int i = 3; i <= max_bits; ++i) {
long_type value = 0;
int number[34] = { 0 };
int count[10] = { 0 };
int index = 0;
long_type upper_tmp = upper_bound;
for (int j = 9; j >= 1; --j) {
int num = upper_tmp / base[j];
while (num > 0 && index < i) {
number[index] = j;
++count[j];
value += base[j];
--num;
++index;
}
if (index >= i) {
break;
}
upper_tmp %= base[j];
}
count[0] += (i - index);
int min = 0;
long_type avg = lower_bound / i;
for (int j = 8; j >= 1; --j) {
if (avg > base[j]) {
min = j + 1;
break;
}
}
while (true) {
int count_copy[10] = { 0 };
for (int j = 0; j < 10; ++j) {
count_copy[j] = count[j];
}
bool equal = true;
long_type value_copy = value;
do {
if (--count_copy[value_copy % 10] < 0) {
equal = false;
break;
}
value_copy /= 10;
} while (value_copy > 0);
if (equal) {
for (int j = 0; j < 10; ++j) {
if (0 != count_copy[j]) {
equal = false;
break;
}
}
}
if (equal) {
cout << i << ": " << value << endl;
}
if (number[i - 1] > 0) {
--count[number[i - 1]];
value -= base[number[i - 1]];
--number[i - 1];
++count[number[i - 1]];
value += base[number[i - 1]];
}
else {
int check_index = i - 2;
while (check_index >= 0 && 0 == number[check_index]) {
--check_index;
}
if (check_index < 0) {
break;
}
else {
--count[number[check_index]];
value -= base[number[check_index]];
--number[check_index];
++count[number[check_index]];
value += base[number[check_index]];
int new_number = number[check_index];
int change_count = i - check_index - 1;
count[0] -= change_count;
count[new_number] += change_count;
value += change_count * (base[new_number] - base[0]);
for (int j = check_index + 1; j < i; ++j) {
number[j] = new_number;
}
if (number[0] < min) {
break;
}
}
}
}
for (int j = 2; j < 10; ++j) {
base[j] *= j;
}
lower_bound *= 10;
upper_bound *= 10;
}
}
int main()
{
show(19);
return 0;
}