求解自幂数的一种算法

目录

关于自幂数的概念:

各种自幂数的名称:

实现思路:

Python 实现:

C 实现:


关于自幂数的概念:

        如果在一个固定的进制中,一个n位自然数等于自身各个数位上数字的n次幂之和,则称此数为自幂数。

        例如:在十进制中,153是一个三位数,各个数位的3次幂之和为1^3 + 5^3 + 3^3=153,所以153是十进制中的自幂数;1634 = 1^4 + 6^4 + 3^4 + 4^4,则1634也是十进制中的一个自幂数,如此类推。

各种自幂数的名称:

        一位自幂数:独身数(1,2,3,4,5,6,7,8,9)

        两位自幂数:没有

        三位自幂数:水仙花数(153,370,371,407

        四位自幂数:四叶玫瑰数(1634,8208,9474

        五位自幂数:五角星数(54748,92727,93084

        六位自幂数:六合数(548834

        七位自幂数:北斗七星数(1741725,4210818,9800817,9926315

        八位自幂数:八仙数(24678050,24678051,88593477

        九位自幂数:九九重阳数(146511208,472335975,534494836,912985153

        十位自幂数:十全十美数(4679307774

实现思路:

        1、根据当前数的位数 n 来构造一个数组,存放的是 0 - 9 各数的 n 次方。如当前数是3位的,则数组为 powArr[10] = [0, 1, 8, 27, 64, 125, 216, 343, 512, 729]。

        每次位数改变时,及时更新 2 - 9 位上的数。

        2、首先需要求出当前数 各数位 上的数的 n 次方 之和(当前数最高位是几位,n 就是几)。可以用循环对 10 取余的方法来实现。

        设当前数为 num,各数位上的数的 n 次方 和为 sum,n 为当前数的位数。

        3、sum < num 时,即和小于当前数,用当前数 num 减去 num 的最低位数值 minSite,即使之最低位为 0, 得到 tempNum,再减去 (sum - minSite ^ n), 得到两者的差值 tempGaptempGap 再开 n 次方,得到结果 result

        result 表示: sum 如果再得到 result 的 n 次方,就能与 tempNum 相同。

        此时对 result 向上取整得到 minBalance,若 minBalance == minSite,则再将 minBalance 自加 1。

        根据得到的 minSite,若minBalance < 10 ,则使 num = tempNum + minBalancesum = sum - minSite ^ n + minBalance ^ n,则当前可以确定:

        num - 1 > sum - (num % 10) ^ n + (num % 10 - 1) ^ n;

        num <= sum;

        然后再以当前的 num 进入下一次循环再做判断。

        若 minBalance > 9 ,则 num 直接十位进一,个位置零,再进入下一次循环。

        以 401 、437进入循环开始举例:

        ①401:

                当前值 num = 401 ,大于 sum = 4 ^ 3 + 0 ^ 3 + 1 ^ 3 = 65;

                tempNum = 400 - 1 = 400,sum = 4 ^ 3 = 64;

                对 (tempNum - sum)开 3 次方,得 result = 6.9382....;

                可知,sum 再加 result ^ 3 就能与tempNum 相等。

                对 result 做向上取整。向上取整的目的是为了让 tempNum + minBalance 大于 num 且 temp + minBalance <= 与之对应的 最小的 sum。取整后,minBalance = 7。此时 minBalance < 10 且 minBalance > minSite,满足要求,则 sum = tempNum + minBalance = 407,再进入下一个循环。

                可以验证判断:

                tempNum + floor(result) = 406 > 4 ^ 3 + 6 ^ 3 = 280; 则 402 —— 405 都大于各自对应的 sum。

                tempNum + ceil(result) = 407 = 4 ^ 3 + 7 ^ 3 = 407;

                此时407进入下一个循环,将被命中。

         ②437:

                当前值 num = 437 ,大于 sum = 4 ^ 3 + 3 ^ 3 + 7 ^ 3 = 434;

                tempNum = 437 - 7 = 430,sum = 4 ^ 3 + 3 ^ 3 = 91;

                对 (tempNum - sum)开 3 次方,得 result = 6.9726;

                可知,sum 再加 result ^ 3 就能与tempNum 相等。

                对 result 再做向上取整,但向上取整后 minBalance = 7,与 minSite 相等,需要再自加一,所以最后得到 minBalance = 8,8 < 10,则num 变为 408,进行下一次循环。

        

        4、sum > num 时,可以断定,最低位不管是多少(即最低位在不引起进位的情况下不管再加多少),都不可能大于其对应的 sum。

       此时可直接将最低位置零,十位加1,再继续循环。

Python 实现:

import math

def method(startDigit, digit):            # 传入开始位数,最高的位数 (100,5)即 100 ——99999
    num = 10 ** (startDigit - 1)
    curLength = len(str(num))
    powArr = []                     # 暂存 (0-9)的n次幂 数组
    for i in range(0, 10):
        powArr.append(i ** curLength)
    while(curLength <= digit):      # 当位数不超过指定的最高位数
        tempNum = num                   # 用来取余
        sum = 0                         # 记录每个数的每位的n次幂之和
        while(tempNum > 0):             # 初始化 各位数之和 sum
            sum += powArr[tempNum % 10]
            tempNum = tempNum // 10
        minSite = num % 10              # 最低位
        if(sum == num):                 # 相等时
            print('!! num: ', num)
            if(curLength != 1 and minSite != 0):
                num = num + 10 - minSite
            else:
                num += 1
        elif(sum < num):                # 和小于该数
            temp = math.ceil(pow((num - minSite) - (sum - powArr[minSite]), 1 / curLength))
            if(temp == minSite):
                temp += 1
            num = num - minSite + (temp if temp <= 10 else 10)
        else:                           # 和大于该数
            num += 10 - minSite
        if(len(str(num)) > curLength):    # 是否需要重置数组
            curLength += 1
            for i in range(2, 10):
                powArr[i] = i ** curLength

C 实现:

        使用同样的算法,移植到 C 中。

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>
#include<time.h>

clock_t start, stop;
double duration;				//记录被测函数运行时间,以秒为单位

void action(int startSite, int digit){		// 传最低位和最高位,如(3,4) 即 100-9999
	start = clock();						// 开始计时
	long count = 0;							// 记录循环的此次数
	
	int num = pow(10, startSite - 1);		// 设置初始数值
	int powArr[10] = {0};					// 存储(0-9)分别的n次方
	for(int i = 1 ; i < 10 ; i ++){	
		powArr[i] = pow(i, startSite);
	}
	char p[20];								// 将 num 转成字符串,存在 p 中,方便计算位数
	itoa(num, p, 10);
	int currentLen = (int)strlen(p);
	while(currentLen <= digit){
		int tempNum = num;                   
		int sum = 0;
		while(tempNum > 0){
			sum += powArr[tempNum % 10];
			tempNum /= 10;
		}
		int minSite = num % 10;				// 最低位
		if(sum == num){
			printf("%d\n", num);
			if(currentLen != 1 && minSite != 0){
				num += 10 - minSite;
			}else{
				num ++;
			}
		}else if(sum < num){
			int temp = ceil(pow((num - minSite) - (sum - powArr[minSite]), 1.0 / currentLen));
			if(temp == minSite){
				temp ++;
			}
			num = num - minSite + (temp <= 10 ? temp : 10);
		}
		else{
			num += 10 - minSite;
		}
		itoa(num, p, 10);
		if(currentLen < (int)strlen(p)){
			currentLen ++;
			for(int i = 1 ; i < 10 ; i ++){			// 重置数组
				powArr[i] = pow(i, currentLen);
			}
		}
		count ++;
	}
	
	stop = clock();
	duration = (double)(stop - start) / CLOCKS_PER_SEC;
	printf("\nspend time: %lf s\n",duration);
	printf("count : %ld ",count);
}

int main(){
	action(1, 8);
}

        使用C运行,查找 1 - 99,999,999(一位到八位)范围内的自幂数,运行时长花费 1.3 s左右,循环的次数为:14,465,229 次,约为该范围内所有数字总数的 七分之一。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bodyHealthy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值