计算机中的数学【水仙花数】求解自然数中所有的水仙花数

在数论中,水仙花数(Narcissistic number),也被称为超完全数字不变数(pluperfect digital invariant, PPDI)、自恋数、自幂数、阿姆斯壮数或阿姆斯特朗数(Armstrong number),用来描述一个N位非负整数,其各位数字的N次方和等于该数本身。

水仙花数只是自幂数的一种,严格来说3位数的3次幂数才称为水仙花数。
附:其他位数的自幂数名字
一位自幂数:独身数
两位自幂数:没有
三位自幂数:水仙花数
四位自幂数:四叶玫瑰数
五位自幂数:五角星数
六位自幂数:六合数
七位自幂数:北斗七星数
八位自幂数:八仙数
九位自幂数:九九重阳数
十位自幂数:十全十美数

可以证明:

当 n > 60 的时候,有


也就是说,n 位水仙花数最小的数字是10^(n-1) , 例如,3位水仙花数最小是10^2 = 100, 这个是个,n 位最小的数都大于各个位上的数字的 n 幂次和最大值: n * 9^n 。

最大的水仙花数有39位。十进制自然数中的所有水仙花数共有88个。

image.png

使用 Kotlin 编程来计算自然数中所有的水仙花数。

完整代码如下:

package com.easykotlin.lectures.kfp.math

import java.math.BigInteger

class NarcissisticNumbers {
    val TEN = BigInteger.TEN

    /**
     *
     * 获取一个 n 位数 N 的各个位上的数字,放到一个 List 中:
     * N=153, digits=[1,5,3]
     */
    fun digits(N: BigInteger, n: Int): ArrayList<Int> {
        var M = N
        var digits = arrayListOf<Int>()

        (1..n).forEach {
            val remainder = M.mod(TEN)
            digits.add(remainder.intValueExact())
            M = M.divide(TEN)
        }
        digits.reverse()
        return digits
    }

    /**
     *  数字 0-9 的 n次幂
     *  0, 1, 2^n, 3^n,...,9^n
     */
    fun zero2NinePower(n: Int): List<BigInteger> {
        var result = arrayListOf<BigInteger>()
        result.add(BigInteger.ZERO)
        result.add(BigInteger.ONE)
        (2..9).forEach {
            result.add(BigInteger.valueOf(it.toLong()).pow(n))
        }
        return result
    }

    fun checkOut(n: Int) {
        var N_ = BigInteger.ZERO
        val zero2NinePowers = zero2NinePower(n)
        // d0 表示0出现次数,d1 表示1出现次数。例如: 数字153,有 d0=1,d1=1,d2=0,d3=1,d4=0,d5=1,...
        for (d0 in 0L..(n - 1)) {
            for (d1 in 0L..(n - d0)) {
                for (d2 in 0L..(n - d0 - d1)) {
                    for (d3 in 0L..(n - d0 - d1 - d2)) {
                        for (d4 in 0L..(n - d0 - d1 - d2 - d3)) {
                            for (d5 in 0L..(n - d0 - d1 - d2 - d3 - d4)) {
                                for (d6 in 0L..(n - d0 - d1 - d2 - d3 - d4 - d5)) {
                                    for (d7 in 0L..(n - d0 - d1 - d2 - d3 - d4 - d5 - d6)) {
                                        for (d8 in 0L..(n - d0 - d1 - d2 - d3 - d4 - d5 - d6 - d7)) {
                                            for (d9 in 0L..(n - d0 - d1 - d2 - d3 - d4 - d5 - d6 - d7 - d8)) {
                                                N_ = zero2NinePowers[0].multiply(BigInteger.valueOf(d0)) +
                                                        zero2NinePowers[1].multiply(BigInteger.valueOf(d1)) +
                                                        zero2NinePowers[2].multiply(BigInteger.valueOf(d2)) +
                                                        zero2NinePowers[3].multiply(BigInteger.valueOf(d3)) +
                                                        zero2NinePowers[4].multiply(BigInteger.valueOf(d4)) +
                                                        zero2NinePowers[5].multiply(BigInteger.valueOf(d5)) +
                                                        zero2NinePowers[6].multiply(BigInteger.valueOf(d6)) +
                                                        zero2NinePowers[7].multiply(BigInteger.valueOf(d7)) +
                                                        zero2NinePowers[8].multiply(BigInteger.valueOf(d8)) +
                                                        zero2NinePowers[9].multiply(BigInteger.valueOf(d9))
                                                doCheck(N_, d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, n)
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    private fun doCheck(N_: BigInteger, d0: Long, d1: Long, d2: Long, d3: Long, d4: Long, d5: Long, d6: Long, d7: Long, d8: Long, d9: Long, n: Int) {
        // 数字 N_ 每位上的数字,例如: N_ = 153,  digitCounts = [1,5,3]
        val digitCounts = digits(N_, n)
        // 统计 digitCounts 中 0-9 分别出现的次数
        val d_ = arrayListOf(0L, 0, 0, 0, 0, 0, 0, 0, 0, 0)
        digitCounts.forEach {
            when (it) {
                0 -> d_[0]++
                1 -> d_[1]++
                2 -> d_[2]++
                3 -> d_[3]++
                4 -> d_[4]++
                5 -> d_[5]++
                6 -> d_[6]++
                7 -> d_[7]++
                8 -> d_[8]++
                9 -> d_[9]++
            }
        }
        var sum = 0
        d_.forEach { sum += it.toInt() }
        if (d0 == d_[0] &&
                d1 == d_[1] &&
                d2 == d_[2] &&
                d3 == d_[3] &&
                d4 == d_[4] &&
                d5 == d_[5] &&
                d6 == d_[6] &&
                d7 == d_[7] &&
                d8 == d_[8] &&
                d9 == d_[9] &&
                sum == n && validateBitWidth(N_, n)) { // 完全数字不变数
            println("${N_}")
//            println("d_ = ${d_}")
//            println("digitCounts = ${digitCounts}")
        }
    }

    /** 过滤掉,N 首位是 0 的情况:
     * 类似 : 5 位数:
    这样的数称为完全数字不变数(perfect digital invariant)
    N_ = 4151
    d_ = [1, 2, 0, 0, 1, 1, 0, 0, 0, 0]
    digitCounts = [0, 4, 1, 5, 1]
    5 位数:
    N_ = 4150
    d_ = [2, 1, 0, 0, 1, 1, 0, 0, 0, 0]
    digitCounts = [0, 4, 1, 5, 0]
    5 位数:
    N_ = 1
    d_ = [4, 1, 0, 0, 0, 0, 0, 0, 0, 0]
    digitCounts = [0, 0, 0, 0, 1]
     */
    fun validateBitWidth(N_: BigInteger, n: Int): Boolean {
        var count = 0
        var N = N_
        while (N > BigInteger.ZERO) {
            N = N.divide(BigInteger.TEN)
            count++
        }
//        println("count=$count")
        return n == count
    }

}

执行入口函数

    @Test
    fun test_checkOut() {
        (1..39).forEach {
            println("-----------------------------")
            println("$it 位水仙花数:")
            val s = System.currentTimeMillis()
            NarcissisticNumbers.checkOut(it)
            val t = System.currentTimeMillis()
            println("时间:${t - s} ms")
        }
    }

运行结果:

-----------------------------
1 位水仙花数:
9
8
7
6
5
4
3
2
1
时间:42 ms
-----------------------------
2 位水仙花数:
时间:8 ms
-----------------------------
3 位水仙花数:
371
153
407
370
时间:21 ms
-----------------------------
4 位水仙花数:
9474
1634
8208
时间:45 ms
-----------------------------
5 位水仙花数:
54748
92727
93084
时间:35 ms
-----------------------------
6 位水仙花数:
548834
时间:190 ms
-----------------------------
7 位水仙花数:
9926315
1741725
4210818
9800817
时间:142 ms
-----------------------------
8 位水仙花数:
88593477
24678051
24678050
时间:415 ms
-----------------------------
9 位水仙花数:
534494836
472335975
912985153
146511208
时间:468 ms
-----------------------------
10 位水仙花数:
4679307774
时间:1428 ms
-----------------------------
11 位水仙花数:
82693916578
44708635679
94204591914
32164049651
49388550606
42678290603
40028394225
32164049650
时间:1374 ms
-----------------------------
12 位水仙花数:
时间:2187 ms
-----------------------------
13 位水仙花数:
时间:4884 ms
-----------------------------
14 位水仙花数:
28116440335967
时间:8053 ms
-----------------------------
15 位水仙花数:
时间:14886 ms
-----------------------------
16 位水仙花数:
4338281769391371
4338281769391370
时间:24496 ms
-----------------------------
17 位水仙花数:
35641594208964132
21897142587612075
35875699062250035
时间:40413 ms
-----------------------------
18 位水仙花数:
时间:70124 ms
-----------------------------
19 位水仙花数:
4498128791164624869
4929273885928088826
3289582984443187032
1517841543307505039
时间:93881 ms
-----------------------------
20 位水仙花数:
63105425988599693916
时间:159871 ms
-----------------------------
21 位水仙花数:
128468643043731391252
449177399146038697307
时间:244321 ms
-----------------------------
22 位水仙花数:
时间:395756 ms
-----------------------------
23 位水仙花数:
21887696841122916288858
28361281321319229463398
27879694893054074471405
35452590104031691935943
27907865009977052567814
时间:612641 ms
-----------------------------
24 位水仙花数:
188451485447897896036875
239313664430041569350093
174088005938065293023722
时间:888945 ms
-----------------------------
25 位水仙花数:
...

使用一台普通的 PC 机器(单机、单线程):


可以看出——
前15位水仙花数,在 10 s 时间量级;
21位水仙花数,时间 4 min 。
22位数字中没有水仙花数。花费 5min。
23位水仙花数,时间 10 min 。
24位水仙花数,时间 15 min 。
......
后面的位数越大,时间将会翻倍。不过,终归会在有限的天数内完成计算。

当然,现代超大规模、并行计算机算起来会快很多。
上面的算法也有进一步优化的空间。

算法代码中的函数说明如下:

zero2NinePower() 函数:

    @Test
    fun test_zero2NinePower() {
        println(NarcissisticNumbers.zero2NinePower(3))
        println(NarcissisticNumbers.zero2NinePower(4))
        println(NarcissisticNumbers.zero2NinePower(21))
    }

输出:

[0, 1, 8, 27, 64, 125, 216, 343, 512, 729]
[0, 1, 16, 81, 256, 625, 1296, 2401, 4096, 6561]
[0, 1, 2097152, 10460353203, 4398046511104, 476837158203125, 21936950640377856, 558545864083284007, 9223372036854775808, 109418989131512359209]

digits() 函数

    @Test
    fun test_digits() {
        println(NarcissisticNumbers.digits(BigInteger.valueOf(153), 3))
        println(NarcissisticNumbers.digits(BigInteger.valueOf(1634), 4))
        println(NarcissisticNumbers.digits(BigInteger.valueOf(4150), 4))
    }

输出:

[1, 5, 3]
[1, 6, 3, 4]
[4, 1, 5, 0]

提示: 完整的工程源代码 https://github.com/EasyKotlin/lectures

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,911评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,014评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,129评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,283评论 1 264
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,159评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,161评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,565评论 3 382
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,251评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,531评论 1 292
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,619评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,383评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,255评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,624评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,916评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,199评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,553评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,756评论 2 335

推荐阅读更多精彩内容

  • 这个不错分享给大家,从扣上看到的,就转过来了 《电脑专业英语》 file [fail] n. 文件;v. 保存文...
    麦子先生R阅读 6,539评论 5 24
  • 题目: 打印出所有的水仙花数 水仙花数 水仙花数(Narcissistic number)也被称为超完全数字不变...
    MacXin阅读 722评论 0 0
  • 梧州市倒水中心小学——黎鉴贤 春节渐渐地走出了我们的视线,迎面而来的是又一年的开学季。 中国地大学校多,当我们正式...
    不期而遇的你我阅读 537评论 0 0
  • 那年你我两小无猜,你许诺娶我做娘子。 那年你偷偷爬上我家院门,只为受罚的我送上一串糖葫芦。 那年我被推入湖...
    梧瞳凄凄阅读 148评论 0 0
  • 意外的,一个中午,洗手池旁,一个腼腆的女孩子带着羞涩的笑容怯怯地说~“老师,给您”。双手递过一个淡雅却充满...
    花开无声201阅读 240评论 0 1