leetcode483. Smallest Good Base 的一些思考

回顾题意:

这里写图片描述

也就是说,找到最小的一种进位制 a ,使得数n a 进位制下能表示为(111..111)a的形式,用数学语言来说就是,对任意的自然数 n3 ,求最小的 a 使得,存在自然数k,让

n=1+a+a2+...+ak

那么题意立马想到若干个问题:

  • 这样的 a 是否一定存在?是否唯一
  • 给定k a 是否存在?(反过来呢?单调性有木有?)

    带着这些问题下面是我思考的全过程

思路1

首先必须说明的是,这样的good base是存在最小的,这是因为我总可以让n表示为 n=1+(n1)1 ,即 a=n1 ,存在性得到说明。其次,由于整数分拆(拉马努金?)种类是有上限的,那么这样的分拆更加是有上界的,两者结合说明了最小good base的存在性,从数学上保证了这点后,我认为可以开始着手思考算法了。

现在来观察 n 的形式,可以发现a必能整除 n1 ,那么我是不是可以思考在 n1 的约数中进行遍历呢?我们用 S(n) 表示 n 的约数个数,不妨设n的标准分解为 n=ni=1pkii ,由伯努利不等式 (1+x)n1+nx

i=1npkiii=1n[(pi1)ki+1]>>i=1n(1+ki)=S(n)

大致估计了下,约数的个数应该是相对于 n 来说是灰常小的,比如20亿以内的数,最多的约数只有1536个。或者我们可以直接估算其数学期望:一个不大于n的正整数的约数个数期望意义下是多少?,对于一个自然数n的约数个数的数学期望和调和级数阶一致,大约是 O(logn) 。想到这里灰常开心,那么是不是就可以开始遍历呢?

等会,似乎和约数的个数并没有太大的关系,首先我们必须把约数求出来,在求出来的过程中完全可以边进行判断,这说明刚才的思考似乎是没有起到作用的。那么要找 n 的约数a,首先想到的就是对小于 n 的数进行遍历,找到约数 a 的同时(当然也同时找到了约数na),对 k 进行二分查找,显然klogan,那么总算法复杂度是 O(nlnln(n)) ,嗯,看起来是可以的,然后一波提交TLE,似乎被大数卡 O(n)

思路1(改进1:二分查找)

实际上,似乎不应该思维卡在约数问题上,对于大数,即使是题目的数上界 1018 也好,一个对数下来也才几十位,换句话说,我们不妨先去遍历 k ,然后对a [2,n1] 上二分,考虑:

||ak+11a1n||<eps

总算法复杂度是 O((logn)2) ,复杂度看起来灰常好,但是很遗憾一波提交TLE。嗯?原来是对于幂次 ak+1 溢出了,不过讲道理,我看到有人开大数后是可以AC的,这就很尴尬了。

思路1(改进2:对数形式)

对于指数形式溢出的问题,我改进了一下将问题从指数形式变成对数形式,即考虑:

||loga[n(a1)+1]1k||<eps

这次应该可以了吧,总算法复杂度也是 O((logn)2) ,兴高采烈地,一波提交WA。嗯?再看详细情况,结果发现106个样例已经过了98个了,额,要是OI的话我就不继续做了,反正拿了90%的分数。慢慢debug发现,原来是eps的问题,原来取对数之后误差也随之变小了,比如5和4本来相差1,但是ln5和ln4相差0.22314,而改进1中并没有这个问题是因为指数会放大误差,随意一个eps基本上都没有问题。

还有一些问题,实际上两种形式都是单调的,对 a 二分,理论上都是有唯一解的,但是对数形式减少了误差后,对于一些诡异的数就会使得二分收到邻近的解上。

思路1(改进3:结合)

这就很尴尬了,那么改进1改进2结合起来呢?就是对于大数,用对数形式,对于小数用指数形式,可以的,提交一波TLE,嗯?原来在区分大数小数的界随意定还是不行,可怕的样例(是谁搞的那么缜密的样例)

思路2(牛顿迭代法)

下午和狗蔡吃饭的时候,想到了能不能直接对对数形式:

f(a)=loga[n(a1)+1]1k=0

来求方程呢?而且对数形式关于 a 的导数是很容易写出来的,做一个迭代an+1=anf(an)/f(an),可以的,码好提交一波,WA。嗯?妈蛋,最终还是卡在了常数eps上,一样的问题,甚至有时候速度还比不上二分。

思路3(非数值数学方法)

看了下讨论,发现有大佬的神做法,先给出一个结论,如果 n 能表示为n=1+a+a2+...+ak的形式的话,那么 a=[nk] 。嗯? a 可以直接算出?
这里写图片描述
实际上这是容易证明的,首先注意到n>aknk>a,另一方面,注意到:

(1+a)k=i=0kCikai>1+a+...+ak=n

a>nk1 ,由两个不等式,立马有 a=[nk] ,接下来就厉害了,对 k 进行遍历,直接求出a,然后用指数形式判断,另外注意到的是,由于要求最小的 a ,所以k应该从大开始遍历,全程复杂度应该是 O(logn)

class Solution(object):
    def smallestGoodBase(self, n):
        n = int(n); 
        for k in xrange(int(math.log(n, 2)), 1 , -1):
            a = int(n ** k ** -1)
            if (1 - a ** (k + 1)) // (1 - a) == n: 
                return str(a)    
        return str(n - 1)

纪念一个血色的一天:
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值